/*******************************************************************************
 *  Copyright (c) 2004, 2012 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
 *    Markus Schorn (Wind River Systems)
 *******************************************************************************/
package org.eclipse.cdt.make.internal.core.scannerconfig.gnu;

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

import org.eclipse.cdt.core.IMarkerGenerator;
import org.eclipse.cdt.make.core.MakeCorePlugin;
import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoCollector;
import org.eclipse.cdt.make.core.scannerconfig.IScannerInfoConsoleParser;
import org.eclipse.cdt.make.core.scannerconfig.ScannerInfoTypes;
import org.eclipse.cdt.make.internal.core.scannerconfig.util.TraceUtil;
import org.eclipse.cdt.make.internal.core.scannerconfig2.PerProjectSICollector;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;

/**
 * Parses output of gcc -c -v specs.c or
 *                  g++ -c -v specs.cpp
 * command
 *
 * @author vhirsl
 */
public class GCCSpecsConsoleParser implements IScannerInfoConsoleParser {
	private final String INCLUDE = "#include"; //$NON-NLS-1$
	private final String DEFINE = "#define"; //$NON-NLS-1$

	private IProject fProject = null;
	protected IScannerInfoCollector fCollector = null;

	private boolean expectingIncludes = false;
	protected List<String> symbols = new ArrayList<>();
	protected List<String> includes = new ArrayList<>();

	@Override
	public void startup(IProject project, IPath workingDirectory, IScannerInfoCollector collector,
			IMarkerGenerator markerGenerator) {
		this.fProject = project;
		this.fCollector = collector;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.make.internal.core.scannerconfig.IScannerInfoConsoleParser#processLine(java.lang.String)
	 */
	@Override
	public boolean processLine(String line) {
		TraceUtil.outputTrace("GCCSpecsConsoleParser parsing line: [", line, "]"); //$NON-NLS-1$ //$NON-NLS-2$

		line = line.trim();
		if (line.length() == 0) {
			return false;
		}

		// contribution of -dD option
		if (line.startsWith(DEFINE)) {
			String[] defineParts = line.split("\\s+", 3); //$NON-NLS-1$
			if (defineParts[0].equals(DEFINE)) {
				if (defineParts[1].indexOf('(') >= 0) {
					// #define __X__(P1, P2) __Y__(P1, P2)
					// Enclose matching parentheses pairs
					// in the macro name if they are present
					int i = line.indexOf(')'); // macro definition itself can have only one pair of brackets

					// i now marks the space between the name and definition
					if (i > 0) {
						int start = line.indexOf(defineParts[1]); // start of definition
						defineParts[1] = line.substring(start, i + 1);
						if (defineParts.length > 2) {
							defineParts[2] = line.substring(i + 1).trim();
						}
					} else {
						MakeCorePlugin
								.log(new Exception("GCCSpecsConsoleParser ERROR: Unmatched brackets: [" + line + "]")); //$NON-NLS-1$ //$NON-NLS-2$
					}
				}

				// Now defineParts[1] is the symbol name, and [2] is the definition
				String symbol = null;
				if (defineParts.length > 1) {
					symbol = defineParts[1] + "="; //$NON-NLS-1$
					if (defineParts.length > 2) {
						symbol += defineParts[2];
					}
					if (!symbols.contains(symbol)) {
						symbols.add(symbol);
					}
				}
			}
		}
		// now get all the includes
		else if (line.startsWith(INCLUDE) && line.endsWith("search starts here:")) { //$NON-NLS-1$
			expectingIncludes = true;
		} else if (line.startsWith("End of search list.")) { //$NON-NLS-1$
			expectingIncludes = false;
		} else if (expectingIncludes) {
			if (!includes.contains(line))
				includes.add(line);
		}

		return false;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.make.internal.core.scannerconfig.IScannerInfoConsoleParser#shutdown()
	 */
	@Override
	public void shutdown() {
		Map<ScannerInfoTypes, List<String>> scannerInfo = new HashMap<>();
		scannerInfo.put(ScannerInfoTypes.INCLUDE_PATHS, includes);
		scannerInfo.put(ScannerInfoTypes.SYMBOL_DEFINITIONS, symbols);
		if (fCollector != null) {
			if (fCollector instanceof PerProjectSICollector) {
				((PerProjectSICollector) fCollector).contributeToScannerConfig(fProject, scannerInfo, true);
			} else {
				fCollector.contributeToScannerConfig(fProject, scannerInfo);
			}
		}
		TraceUtil.outputTrace("Scanner info from \'specs\' file", //$NON-NLS-1$
				"Include paths", includes, new ArrayList<String>(0), "Defined symbols", symbols); //$NON-NLS-1$ //$NON-NLS-2$);
	}

}
