/*******************************************************************************
 * Copyright (c) 2006, 2008 QNX Software Systems 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:
 * 		QNX - Initial API and implementation
 * 		IBM Corporation
 *      Andrew Ferguson (Symbian)
 *      Anton Leherbauer (Wind River Systems)
 *      Markus Schorn (Wind River Systems)
 *******************************************************************************/
package org.eclipse.cdt.internal.core.browser.util;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.ast.ASTTypeUtil;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IBinding;
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.IFunction;
import org.eclipse.cdt.core.dom.ast.IMacroBinding;
import org.eclipse.cdt.core.dom.ast.IParameter;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.ITypedef;
import org.eclipse.cdt.core.dom.ast.IVariable;
import org.eclipse.cdt.core.dom.ast.c.ICFunctionScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBlockScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespaceAlias;
import org.eclipse.cdt.core.index.IIndexMacroContainer;
import org.eclipse.cdt.core.model.ICElement;

/**
 * Convenience class for bridging the model gap between binding types and CModel types
 * 
 * This is internal in case some IBinding's do not have ICElement constants in future
 */
public class IndexModelUtil {
	private static final String[] EMPTY_STRING_ARRAY= {};

	/**
	 * Returns whether the binding is of any of the specified CElement type constants
	 * @param binding
	 * @param kinds
	 * @return whether the binding is of any of the specified CElement type constants
	 */
	public static boolean bindingHasCElementType(IBinding binding, int[] kinds) {
		try {
			for (int kind : kinds) {
				switch(kind) {
				case ICElement.C_STRUCT:
					if (binding instanceof ICompositeType
							&& ((ICompositeType)binding).getKey() == ICompositeType.k_struct)
						return true;
					break;
				case ICElement.C_UNION:
					if (binding instanceof ICompositeType
							&& ((ICompositeType)binding).getKey() == ICompositeType.k_union)
						return true;
					break;
				case ICElement.C_CLASS:
					if (binding instanceof ICompositeType
							&& ((ICompositeType)binding).getKey() == ICPPClassType.k_class)
						return true;
					break;
				case ICElement.C_NAMESPACE:
					if (binding instanceof ICPPNamespace || binding instanceof ICPPNamespaceAlias)
						return true;
					break;
				case ICElement.C_ENUMERATION:
					if (binding instanceof IEnumeration)
						return true;
					break;
				case ICElement.C_TYPEDEF:
					if(binding instanceof ITypedef)
						return true;
					break;
				case ICElement.C_FUNCTION:
					if(binding instanceof IFunction)
						return true;
					break;
				case ICElement.C_VARIABLE:
					if(binding instanceof IVariable)
						return true;
					break;
				}
			}
		} catch(DOMException de) {
			CCorePlugin.log(de);
		}
		return false;
	}
	
	/**
	 * Returns the CElement type constant for the specified binding
	 * @param binding
	 * @return the CElement type constant for the specified binding
	 */
	public static int getElementType(IBinding binding) {
		int elementType = Integer.MIN_VALUE;

		if (binding instanceof ICompositeType) {
			ICompositeType classType = (ICompositeType) binding;
			try {
				switch(classType.getKey()) {
				case ICPPClassType.k_class:
					elementType = ICElement.C_CLASS;
					break;
				case ICompositeType.k_struct:
					elementType = ICElement.C_STRUCT;
					break;
				case ICompositeType.k_union:
					elementType = ICElement.C_UNION;
					break;
				}
			} catch(DOMException de) {
				CCorePlugin.log(de);
			}
		}

		if (binding instanceof ICPPNamespace || binding instanceof ICPPNamespaceAlias) {
			elementType = ICElement.C_NAMESPACE;
		}
		if (binding instanceof IEnumeration) {
			elementType = ICElement.C_ENUMERATION;
		}
		if (binding instanceof ITypedef) {
			elementType = ICElement.C_TYPEDEF;
		}
		if (binding instanceof IFunction) {
			elementType = ICElement.C_FUNCTION;
		}
		if (binding instanceof IVariable) {
			IScope scope= null;
			try {
				scope = binding.getScope();
			} catch (DOMException e) {
			}
			if (scope instanceof ICPPBlockScope || scope instanceof ICFunctionScope) {
				elementType= ICElement.C_VARIABLE_LOCAL;
			} else {
				elementType = ICElement.C_VARIABLE;
			}
		}
		if (binding instanceof IEnumerator) {
			elementType = ICElement.C_ENUMERATOR;
		}
		if (binding instanceof IMacroBinding || binding instanceof IIndexMacroContainer) {
			elementType= ICElement.C_MACRO;
		}
		if (binding instanceof IParameter) {
			elementType= ICElement.C_VARIABLE_LOCAL;
		}
		return elementType;
	}

	/**
	 * Extract the parameter types of the given function as array of strings.
	 * @param function
	 * @return the parameter types of the function
	 * @throws DOMException
	 */
	public static String[] extractParameterTypes(IFunction function) throws DOMException {
		IParameter[] params= function.getParameters();
		String[] parameterTypes= new String[params.length];
		for (int i = 0; i < params.length; i++) {
			IParameter param = params[i];
			parameterTypes[i]= ASTTypeUtil.getType(param.getType());
		}
		if (parameterTypes.length == 1 && parameterTypes[0].equals("void")) { //$NON-NLS-1$
			return EMPTY_STRING_ARRAY;
		}
		return parameterTypes;
	}

	/**
	 * Extract the return type of the given function as string.
	 * @param function
	 * @return the return type of the function
	 * @throws DOMException 
	 */
	public static String extractReturnType(IFunction function) throws DOMException {
		return ASTTypeUtil.getType(function.getType().getReturnType());
	}
}
