/*******************************************************************************
 * Copyright (c) 2004, 2007 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM - Initial API and implementation
 *     Tianchao Li (tianchao.li@gmail.com) - arbitrary build directory (bug #136136)
 *     Gerhard Schaber (Wind River Systems) - bug 187910
 *     Markus Schorn (Wind River Systems)
 *******************************************************************************/
package org.eclipse.cdt.make.internal.core.scannerconfig.gnu;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.cdt.core.IMarkerGenerator;
import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector;
import org.eclipse.cdt.make.core.scannerconfig.ScannerInfoTypes;
import org.eclipse.cdt.make.internal.core.scannerconfig.util.CCommandDSC;
import org.eclipse.cdt.make.internal.core.scannerconfig.util.TraceUtil;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.IPath;


/**
 * GCC per file build output parser
 * 
 * @author vhirsl
 */
public class GCCPerFileBOPConsoleParser extends AbstractGCCBOPConsoleParser {
    private final static String[] FILE_EXTENSIONS = {
        ".c", ".cc", ".cpp", ".cxx", ".C", ".CC", ".CPP", ".CXX" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
    };
    private final static List FILE_EXTENSIONS_LIST = Arrays.asList(FILE_EXTENSIONS);
    
    private GCCPerFileBOPConsoleParserUtility fUtil;
    
    /* (non-Javadoc)
     * @see org.eclipse.cdt.make.core.scannerconfig.IScannerInfoConsoleParser#startup(org.eclipse.core.resources.IProject, org.eclipse.core.runtime.IPath, org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector, org.eclipse.cdt.core.IMarkerGenerator)
     */
    public void startup(IProject project, IPath workingDirectory, IScannerInfoCollector collector, IMarkerGenerator markerGenerator) {
        fUtil = (project != null && workingDirectory != null && markerGenerator != null) ?
                new GCCPerFileBOPConsoleParserUtility(project, workingDirectory, markerGenerator) : null;
        super.startup(project, collector);
    }

    /* (non-Javadoc)
     * @see org.eclipse.cdt.make.internal.core.scannerconfig.gnu.AbstractGCCBOPConsoleParser#getUtility()
     */
    protected AbstractGCCBOPConsoleParserUtility getUtility() {
        return fUtil;
    }

    /* (non-Javadoc)
     * @see org.eclipse.cdt.make.internal.core.scannerconfig.gnu.AbstractGCCBOPConsoleParser#processSingleLine(java.lang.String)
     */
    protected boolean processCommand(String[] tokens) {
        // GCC C/C++ compiler invocation 
        int compilerInvocationIndex= findCompilerInvocation(tokens);
        if (compilerInvocationIndex < 0) {
            return false;
        }

        // find a file name
        int extensionsIndex = -1;
        boolean found = false;
        String filePath = null;
        for (int i = compilerInvocationIndex+1; i < tokens.length; i++) {
        	String token= tokens[i];
            int k = token.lastIndexOf('.');
            if (k != -1 && (token.length() - k < 5)) {
                String fileExtension = token.substring(k);
                extensionsIndex = FILE_EXTENSIONS_LIST.indexOf(fileExtension);
                if (extensionsIndex != -1) {
                    filePath = token;
                    found = true;
                    break;
                }
            }
        }
        if (!found) {
            TraceUtil.outputTrace("Error identifying file name :1", tokens, TraceUtil.EOL); //$NON-NLS-1$
            return false;
        }
        // sanity check
        if (filePath.indexOf(FILE_EXTENSIONS[extensionsIndex]) == -1) {
            TraceUtil.outputTrace("Error identifying file name :2", tokens, TraceUtil.EOL); //$NON-NLS-1$
            return false;
        }
        if (fUtil != null) {
            IPath pFilePath = fUtil.getAbsolutePath(filePath);
            String shortFileName = pFilePath.removeFileExtension().lastSegment();

            // generalize occurrences of the file name
            for (int i = compilerInvocationIndex+1; i < tokens.length; i++) {
				String token = tokens[i];
				if (token.equals("-include")) { //$NON-NLS-1$
					++i;
				}
				else if (token.equals("-imacros")) { //$NON-NLS-1$
					++i;
				}
				else if (token.equals(filePath)) {
					tokens[i]= "LONG_NAME"; //$NON-NLS-1$
				}
				else if (token.startsWith(shortFileName)) {
					tokens[i]= token.replaceFirst(shortFileName, "SHORT_NAME"); //$NON-NLS-1$
				}
			}
            
            CCommandDSC cmd = fUtil.getNewCCommandDSC(tokens, compilerInvocationIndex, extensionsIndex > 0);
            IPath baseDirectory = fUtil.getBaseDirectory();
            boolean isValidPath = baseDirectory.isPrefixOf(pFilePath);
            if (!isValidPath) {
            	final IProject prj= fUtil.getProject();
                IFile[] foundOccurrences = ((IWorkspaceRoot) prj.getParent()).findFilesForLocation(pFilePath);
                for (int j=0; !isValidPath && j<foundOccurrences.length; j++) {
                	isValidPath= prj.equals(foundOccurrences[j].getProject());
                }
            }
            if (isValidPath) {
	            List cmdList = new ArrayList();
	            cmdList.add(cmd);
	            Map sc = new HashMap(1);
	            sc.put(ScannerInfoTypes.COMPILER_COMMAND, cmdList);

				IPath relPath = pFilePath.removeFirstSegments(baseDirectory.segmentCount());
				//Note: We add the scanner-config even if the resource doesn't actually
				//exist below this project (which may happen when reading existing
				//build logs, because resources can be created as part of the build
				//and may not exist at the time of analyzing the config but re-built
				//later on.
				//if (getProject().exists(relPath)) {
	            IFile file = getProject().getFile(relPath);
	            getCollector().contributeToScannerConfig(file, sc);
            } else {
            	//TODO limiting to paths below this project means not being
            	//able to work with linked resources. Linked resources could
            	//be checked through IWorkspaceRoot.findFilesForLocation().
            	TraceUtil.outputError("Build command for file outside project: "+pFilePath.toString(), tokens); //$NON-NLS-1$
            }
            // fUtil.addGenericCommandForFile2(longFileName, genericLine);
        }
        return true;
    }
}
