/*******************************************************************************
 * Copyright (c) 2007, 2015 Symbian Software Systems 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:
 *     Andrew Ferguson (Symbian) - Initial implementation
 *     Sergey Prigogin (Google)
 *******************************************************************************/
package org.eclipse.cdt.internal.core.index.composite.c;

import org.eclipse.cdt.core.dom.ast.EScopeKind;
import org.eclipse.cdt.core.dom.ast.IBasicType;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IEnumeration;
import org.eclipse.cdt.core.dom.ast.IEnumerator;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IFunction;
import org.eclipse.cdt.core.dom.ast.IFunctionType;
import org.eclipse.cdt.core.dom.ast.IParameter;
import org.eclipse.cdt.core.dom.ast.ISemanticProblem;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.ITypedef;
import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.c.ICArrayType;
import org.eclipse.cdt.core.dom.ast.c.ICCompositeTypeScope;
import org.eclipse.cdt.core.dom.ast.c.ICPointerType;
import org.eclipse.cdt.core.dom.ast.c.ICQualifierType;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.index.IIndexMacroContainer;
import org.eclipse.cdt.internal.core.dom.parser.c.CArrayType;
import org.eclipse.cdt.internal.core.dom.parser.c.CFunctionType;
import org.eclipse.cdt.internal.core.dom.parser.c.CPointerType;
import org.eclipse.cdt.internal.core.dom.parser.c.CQualifierType;
import org.eclipse.cdt.internal.core.index.IIndexFragmentBinding;
import org.eclipse.cdt.internal.core.index.IIndexScope;
import org.eclipse.cdt.internal.core.index.composite.AbstractCompositeFactory;
import org.eclipse.cdt.internal.core.index.composite.CompositeMacroContainer;
import org.eclipse.cdt.internal.core.index.composite.CompositingNotImplementedError;

public class CCompositesFactory extends AbstractCompositeFactory {

	public CCompositesFactory(IIndex index) {
		super(index);
	}

	@Override
	public IIndexScope getCompositeScope(IIndexScope rscope) {
		if (rscope == null)
			return null;
		if (rscope.getKind() == EScopeKind.eGlobal)
			return rscope;

		if (rscope instanceof ICCompositeTypeScope) {
			ICCompositeTypeScope cscope = (ICCompositeTypeScope) rscope;
			IIndexFragmentBinding rbinding = (IIndexFragmentBinding) cscope.getCompositeType();
			return (IIndexScope) ((ICompositeType) getCompositeBinding(rbinding)).getCompositeScope();
		}
		throw new CompositingNotImplementedError();
	}

	@Override
	public IType getCompositeType(IType rtype) {
		if (rtype instanceof IIndexFragmentBinding) {
			return (IType) getCompositeBinding((IIndexFragmentBinding) rtype);
		}
		if (rtype instanceof IFunctionType) {
			IFunctionType ft = (IFunctionType) rtype;
			IType r = ft.getReturnType();
			IType r2 = getCompositeType(r);
			IType[] p = ft.getParameterTypes();
			IType[] p2 = getCompositeTypes(p);
			boolean takesVarargs = ft.takesVarArgs();
			if (r != r2 || p != p2) {
				return new CFunctionType(r2, p2, takesVarargs);
			}
			return ft;
		}
		if (rtype instanceof ICPointerType) {
			ICPointerType pt = (ICPointerType) rtype;
			IType r = pt.getType();
			IType r2 = getCompositeType(r);
			if (r != r2) {
				int q = 0;
				if (pt.isConst())
					q |= CPointerType.IS_CONST;
				if (pt.isVolatile())
					q |= CPointerType.IS_VOLATILE;
				if (pt.isRestrict())
					q |= CPointerType.IS_RESTRICT;
				return new CPointerType(r2, q);
			}
			return pt;
		}
		if (rtype instanceof ICQualifierType) {
			ICQualifierType qt = (ICQualifierType) rtype;
			IType r = qt.getType();
			IType r2 = getCompositeType(r);
			if (r != r2) {
				return new CQualifierType(r2, qt.isConst(), qt.isVolatile(), qt.isRestrict());
			}
			return qt;
		}
		if (rtype instanceof ICArrayType) {
			ICArrayType at = (ICArrayType) rtype;
			IType r = at.getType();
			IType r2 = getCompositeType(r);
			IValue v = at.getSize();
			IValue v2 = getCompositeValue(v);
			if (r != r2 || v != v2) {
				CArrayType at2 = new CArrayType(r2, at.isConst(), at.isVolatile(), at.isRestrict(), v2);
				at2.setIsStatic(at.isStatic());
				at2.setIsVariableLength(at.isVariableLength());
			}
			return at;
		}
		if (rtype instanceof IBasicType || rtype instanceof ISemanticProblem || rtype == null) {
			return rtype;
		}

		throw new CompositingNotImplementedError();
	}

	@Override
	public IValue getCompositeValue(IValue v) {
		return v;
	}

	/*
	 * @see org.eclipse.cdt.internal.core.index.composite.cpp.ICompositesFactory#getCompositeBinding(org.eclipse.cdt.core.index.IIndex, org.eclipse.cdt.core.dom.ast.IBinding)
	 */
	@Override
	public IIndexBinding getCompositeBinding(IIndexFragmentBinding rbinding) {
		IIndexBinding result;

		if (rbinding == null) {
			result = null;
		} else if (rbinding instanceof IParameter) {
			result = new CompositeCParameter(this, rbinding);
		} else if (rbinding instanceof IField) {
			result = new CompositeCField(this, rbinding);
		} else if (rbinding instanceof IVariable) {
			result = new CompositeCVariable(this, rbinding);
		} else if (rbinding instanceof ICompositeType) {
			result = new CompositeCStructure(this, findOneBinding(rbinding, false));
		} else if (rbinding instanceof IEnumeration) {
			result = new CompositeCEnumeration(this, findOneBinding(rbinding, false));
		} else if (rbinding instanceof IFunction) {
			result = new CompositeCFunction(this, rbinding);
		} else if (rbinding instanceof IEnumerator) {
			result = new CompositeCEnumerator(this, rbinding);
		} else if (rbinding instanceof ITypedef) {
			result = new CompositeCTypedef(this, rbinding);
		} else if (rbinding instanceof IIndexMacroContainer) {
			result = new CompositeMacroContainer(this, rbinding);
		} else {
			throw new CompositingNotImplementedError(
					"Composite binding unavailable for " + rbinding + " " + rbinding.getClass()); //$NON-NLS-1$ //$NON-NLS-2$
		}

		return result;
	}
}
