/*******************************************************************************
 * Copyright (c) 2005, 2011 Intel 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:
 * Intel Corporation - Initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.managedbuilder.internal.dataprovider;

import org.eclipse.cdt.core.cdtvariables.ICdtVariable;
import org.eclipse.cdt.core.cdtvariables.ICdtVariableManager;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.managedbuilder.core.IBuilder;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IManagedProject;
import org.eclipse.cdt.managedbuilder.internal.macros.BuildMacroProvider;
import org.eclipse.cdt.managedbuilder.internal.macros.DefaultMacroContextInfo;
import org.eclipse.cdt.managedbuilder.internal.macros.IMacroContextInfo;
import org.eclipse.cdt.managedbuilder.macros.IBuildMacro;
import org.eclipse.cdt.managedbuilder.macros.IBuildMacroProvider;
import org.eclipse.cdt.managedbuilder.macros.IConfigurationBuildMacroSupplier;
import org.eclipse.cdt.managedbuilder.macros.IProjectBuildMacroSupplier;
import org.eclipse.cdt.utils.cdtvariables.ICdtVariableSupplier;
import org.eclipse.cdt.utils.cdtvariables.IVariableContextInfo;
import org.eclipse.cdt.utils.cdtvariables.SupplierBasedCdtVariableManager;
import org.eclipse.cdt.utils.envvar.EnvVarOperationProcessor;

/**
 * This supplier is used to suply macros provided by the tool-integrator
 *
 * @since 3.0
 */
public class ExternalExtensionMacroSupplier implements ICdtVariableSupplier {
	private static final String fNonOverloadableMacros[] = new String[] {
			//tool-integrators not allowed currently to override the "CWD" and "PWD" macros
			"CWD", //$NON-NLS-1$
			"PWD" //$NON-NLS-1$
	};

	private ICdtVariableManager fMngr;
	private ICConfigurationDescription fCfgDes;

	//	private static ExternalExtensionMacroSupplier fInstance;

	private class ExtensionMacroProvider extends BuildMacroProvider {
		private IMacroContextInfo fStartInfo;
		private int fContextType;
		private Object fContextData;
		private boolean fStartInitialized;

		public ExtensionMacroProvider(int contextType, Object contextData) {
			fContextType = contextType;
			fContextData = contextData;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.cdt.managedbuilder.envvar.IEnvironmentVariableProvider#getVariable(java.lang.String, java.lang.Object, boolean)
		 */
		@Override
		public ICdtVariable getVariable(String macroName, int contextType, Object contextData, boolean includeParent) {
			if (getValidName(macroName) == null)
				return null;
			return fMngr.getVariable(macroName, fCfgDes);
			//			return super.getMacro(macroName,contextType,contextData,includeParent);
		}

		/* (non-Javadoc)
		 * @see org.eclipse.cdt.managedbuilder.macros.IBuildMacroProvider#getMacros(int, java.lang.Object, boolean)
		 */
		@Override
		public ICdtVariable[] getVariables(int contextType, Object contextData, boolean includeParent) {
			//			return filterVariables(super.getMacros(contextType, contextData,  includeParent));
			return filterVariables(fMngr.getVariables(fCfgDes));
		}

		/* (non-Javadoc)
		 * @see org.eclipse.cdt.managedbuilder.internal.envvar.EnvironmentVariableProvider#getContextInfo(java.lang.Object)
		 */
		@Override
		public IMacroContextInfo getMacroContextInfo(int contextType, Object contextData) {
			IMacroContextInfo startInfo = getStartInfo();
			if (contextType == fContextType && contextData == fContextData)
				return startInfo;

			IMacroContextInfo info = super.getMacroContextInfo(contextType, contextData);
			if (info == null)
				return null;

			if (SupplierBasedCdtVariableManager.checkParentContextRelation(startInfo, info))
				return info;
			return null;
		}

		protected IMacroContextInfo getStartInfo() {
			if (fStartInfo == null && !fStartInitialized) {
				IMacroContextInfo info = super.getMacroContextInfo(fContextType, fContextData);
				if (info != null) {
					ICdtVariableSupplier suppliers[] = info.getSuppliers();
					suppliers = filterValidSuppliers(suppliers);
					if (suppliers != null)
						fStartInfo = new DefaultMacroContextInfo(fContextType, fContextData, suppliers);
					else
						fStartInfo = (IMacroContextInfo) info.getNext();
					fStartInitialized = true;
				}
				fStartInitialized = true;
			}
			return fStartInfo;
		}
	}

	public ExternalExtensionMacroSupplier(ICdtVariableManager mngr, ICConfigurationDescription cfgDes) {
		fMngr = mngr;
		fCfgDes = cfgDes;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.macros.IBuildMacroSupplier#getMacro(java.lang.String, int, java.lang.Object)
	 */
	@Override
	public ICdtVariable getVariable(String macroName, IVariableContextInfo context) {
		if ((macroName = getValidName(macroName)) == null)
			return null;

		IMacroContextInfo info = (IMacroContextInfo) context;
		int contextType = info.getContextType();
		Object contextData = info.getContextData();
		switch (contextType) {
		case IBuildMacroProvider.CONTEXT_CONFIGURATION:
			IConfiguration cfg = null;
			IBuilder builder = null;
			if (contextData instanceof IBuilder) {
				builder = (IBuilder) contextData;
				cfg = builder.getParent().getParent();
			} else if (contextData instanceof IConfiguration) {
				cfg = (IConfiguration) contextData;
				builder = cfg.getBuilder();
			}
			if (cfg != null) {
				IConfigurationBuildMacroSupplier supplier = cfg.getBuildMacroSupplier();
				if (supplier == null)
					return null;
				return supplier.getMacro(macroName, cfg, new ExtensionMacroProvider(contextType, contextData));
			}
		case IBuildMacroProvider.CONTEXT_PROJECT:
			if (contextData instanceof IManagedProject) {
				IManagedProject project = (IManagedProject) contextData;
				IProjectBuildMacroSupplier supplier = project.getProjectType() != null
						? project.getProjectType().getBuildMacroSupplier()
						: null;
				if (supplier == null)
					return null;
				return supplier.getMacro(macroName, project, new ExtensionMacroProvider(contextType, contextData));
			}
		}
		return null;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.managedbuilder.macros.IBuildMacroSupplier#getMacros(int, java.lang.Object)
	 */
	@Override
	public ICdtVariable[] getVariables(IVariableContextInfo context) {
		IBuildMacro macros[] = null;
		IMacroContextInfo info = (IMacroContextInfo) context;
		int contextType = info.getContextType();
		Object contextData = info.getContextData();

		switch (contextType) {
		case IBuildMacroProvider.CONTEXT_CONFIGURATION:
			IConfiguration cfg = null;
			IBuilder builder = null;
			if (contextData instanceof IBuilder) {
				builder = (IBuilder) contextData;
				cfg = builder.getParent().getParent();
			} else if (contextData instanceof IConfiguration) {
				cfg = (IConfiguration) contextData;
				builder = cfg.getBuilder();
			}
			if (cfg != null) {
				IConfigurationBuildMacroSupplier supplier = cfg.getBuildMacroSupplier();
				if (supplier != null)
					macros = supplier.getMacros(cfg, new ExtensionMacroProvider(contextType, contextData));
			}
			break;
		case IBuildMacroProvider.CONTEXT_PROJECT:
			if (contextData instanceof IManagedProject) {
				IManagedProject project = (IManagedProject) contextData;
				IProjectBuildMacroSupplier supplier = project.getProjectType() != null
						? project.getProjectType().getBuildMacroSupplier()
						: null;
				if (supplier != null)
					macros = supplier.getMacros(project, new ExtensionMacroProvider(contextType, contextData));
			}
		}
		return filterVariables(macros);
	}

	protected String getValidName(String name) {
		if (name == null || (name = name.trim()).length() == 0)
			return null;
		if (fNonOverloadableMacros != null) {
			for (int i = 0; i < fNonOverloadableMacros.length; i++) {
				if (fNonOverloadableMacros[i].equals(EnvVarOperationProcessor.normalizeName(name)))
					return null;
			}
		}
		return name;
	}

	protected ICdtVariable[] filterVariables(ICdtVariable macros[]) {
		return filterVariables(macros, fNonOverloadableMacros);
	}

	private ICdtVariable[] filterVariables(ICdtVariable macros[], String remove[]) {
		if (macros == null || macros.length == 0)
			return macros;

		ICdtVariable filtered[] = new ICdtVariable[macros.length];
		int filteredNum = 0;
		for (int i = 0; i < macros.length; i++) {
			ICdtVariable var = macros[i];
			String name = null;
			if (var != null && (name = var.getName().trim()).length() != 0) {
				boolean skip = false;
				if (remove != null && remove.length > 0) {
					for (int j = 0; j < remove.length; j++) {
						if (remove[j] != null && remove[j].equals(name)) {
							skip = true;
							break;
						}
					}
				}
				if (!skip)
					filtered[filteredNum++] = var;
			}
		}

		if (filteredNum != filtered.length) {
			ICdtVariable m[] = new ICdtVariable[filteredNum];
			for (int i = 0; i < filteredNum; i++)
				m[i] = filtered[i];
			filtered = m;
		}
		return filtered;

	}

	protected ICdtVariableSupplier[] filterValidSuppliers(ICdtVariableSupplier suppliers[]) {
		if (suppliers == null)
			return null;

		int i = 0, j = 0;
		for (i = 0; i < suppliers.length; i++) {
			if (suppliers[i] == this)
				break;
		}

		if (i >= suppliers.length)
			return null;

		int startNum = i + 1;

		ICdtVariableSupplier validSuppliers[] = new ICdtVariableSupplier[suppliers.length - startNum];

		for (i = startNum, j = 0; i < suppliers.length; i++, j++)
			validSuppliers[j] = suppliers[i];

		return validSuppliers;
	}

}
