/*******************************************************************************
 * Copyright (c) 2009, 2013 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 * IBM - Initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.make.xlc.core.scannerconfig;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector2;
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.cdt.make.xlc.core.activator.Activator;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;

/**
 * @author crecoskie
 *
 */
@SuppressWarnings("restriction")
public class XLCPerFileBuildOutputParser extends AbstractXLCBuildOutputParser {

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.make.xlc.core.scannerconfig.AbstractXLCBuildOutputParser#processCommand(java.lang.String[])
	 */
	@Override
	protected boolean processCommand(String[] tokens) {
		try {

			// 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;

			// check for automake format first
			// e.g. line will start with
			// source='/some/path/source.cpp'
			int automakeSrcIndex = findAutoMakeSourceIndex(tokens);
			if (automakeSrcIndex != -1) {
				filePath = getAutoMakeSourcePath(tokens[automakeSrcIndex]);
				int k = filePath.lastIndexOf('.');
				if (k != -1 && (filePath.length() - k < 5)) {
					String fileExtension = filePath.substring(k);
					extensionsIndex = getFileExtensionsList().indexOf(fileExtension);
					if (extensionsIndex != -1) {
						found = true;
					}
				}
			}

			if (!found) {
				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 = getFileExtensionsList().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(getFileExtensions()[extensionsIndex]) == -1) {
				TraceUtil.outputTrace("Error identifying file name :2", tokens, TraceUtil.EOL); //$NON-NLS-1$
				return false;
			}
			if (getUtility() != null) {
				IPath pFilePath = fUtility.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$
					}
				}

				IFile file = null;
				IPath baseDirectory = fUtility.getBaseDirectory();
				if (baseDirectory.isPrefixOf(pFilePath)) {
					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)) {
					file = getProject().getFile(relPath);
				} else {
					file = getUtility().findFileInWorkspace(pFilePath);
				}
				if (true /*file != null*/) {
					CCommandDSC cmd = getUtility().getNewCCommandDSC(tokens, compilerInvocationIndex,
							extensionsIndex > 0);
					List<CCommandDSC> cmdList = new CopyOnWriteArrayList<>();
					cmdList.add(cmd);
					Map<ScannerInfoTypes, List<CCommandDSC>> sc = new HashMap<>(1);
					sc.put(ScannerInfoTypes.COMPILER_COMMAND, cmdList);
					getCollector().contributeToScannerConfig(file, sc);
					if (fCollector != null && fCollector instanceof IScannerInfoCollector2) {
						IScannerInfoCollector2 collector = (IScannerInfoCollector2) fCollector;
						try {
							collector.updateScannerConfiguration(null);
						} catch (CoreException e) {
							// TODO Auto-generated catch block
							Activator.log(e);
						}
					}
				} else {
					/*
					//TraceUtil.outputError("Build command for file outside project: " + pFilePath.toString(), tokens); //$NON-NLS-1$
					// put the info on the project
					//CCommandDSC cmd = getUtility().getNewCCommandDSC(tokens, compilerInvocationIndex, extensionsIndex > 0);
					//List<CCommandDSC> cmdList = new ArrayList<CCommandDSC>();
					//				cmdList.add(cmd);
					Map<ScannerInfoTypes, List<?>> sc = new HashMap<ScannerInfoTypes, List<?>>();
					//				sc.put(ScannerInfoTypes.COMPILER_COMMAND, cmdList);


					// put in empty info for the other types
					sc.put(ScannerInfoTypes.INCLUDE_PATHS, new LinkedList<String>());
					sc.put(ScannerInfoTypes.SYMBOL_DEFINITIONS, new LinkedList<String>());

					getCollector().contributeToScannerConfig(getProject(), sc);
					if (fCollector != null && fCollector instanceof IScannerInfoCollector2) {
						IScannerInfoCollector2 collector = (IScannerInfoCollector2) fCollector;
						try {
							collector.updateScannerConfiguration(null);
						} catch (CoreException e) {
							// TODO Auto-generated catch block
							Activator.log(e);
						}
					}

					*/
				}
			}
			return true;

		} catch (Throwable e) {
			e.printStackTrace();
			return false;
		}
	}

	protected String getAutoMakeSourcePath(String string) {
		// path may be enclosed in single quotes
		int firstQuoteIndex = string.indexOf('\'');
		int lastQuoteIndex = string.lastIndexOf('\'');
		if (firstQuoteIndex != -1 && lastQuoteIndex != -1)
			return string.substring(firstQuoteIndex, lastQuoteIndex);
		else {
			// just take everything after the equals sign
			int equalsIndex = string.indexOf('=');
			if (equalsIndex != -1 && equalsIndex < string.length())
				return string.substring(equalsIndex + 1);

		}
		return null;
	}

	protected int findAutoMakeSourceIndex(String[] tokens) {
		for (int i = 0; i < tokens.length; i++) {
			final String token = tokens[i].toLowerCase();
			if (token.indexOf("source=") != -1) //$NON-NLS-1$
				return i;
		}
		return -1;
	}

}
