/*******************************************************************************
 * Copyright (c) 2004, 2009 Tasktop Technologies 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:
 *     Tasktop Technologies - initial API and implementation
 *******************************************************************************/
package org.eclipse.mylyn.internal.cdt.ui.contentassist;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.cdt.core.dom.ast.ASTTypeUtil;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTCompletionContext;
import org.eclipse.cdt.core.dom.ast.IASTCompletionNode;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFunctionStyleMacroParameter;
import org.eclipse.cdt.core.dom.ast.IASTIdExpression;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorFunctionStyleMacroDefinition;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition;
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.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.IScope;
import org.eclipse.cdt.core.dom.ast.IType;
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.ICPPASTQualifiedName;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDeclaration;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTUsingDirective;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBlockScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassTemplate;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPField;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionTemplate;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMember;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPUsingDeclaration;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.CoreModelUtil;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility;
import org.eclipse.cdt.core.parser.util.CharArrayUtils;
import org.eclipse.cdt.internal.core.dom.parser.c.ICInternalBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPImplicitFunction;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPImplicitMethod;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPImplicitTypedef;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding;
import org.eclipse.cdt.internal.ui.text.contentassist.CContentAssistInvocationContext;
import org.eclipse.cdt.internal.ui.text.contentassist.CProposalContextInformation;
import org.eclipse.cdt.internal.ui.text.contentassist.ParsingBasedProposalComputer;
import org.eclipse.cdt.internal.ui.text.contentassist.RelevanceConstants;
import org.eclipse.cdt.internal.ui.viewsupport.CElementImageProvider;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.text.ICPartitions;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.commons.ui.CommonImages;
import org.eclipse.mylyn.internal.cdt.ui.CDTUIBridgePlugin;
import org.eclipse.swt.graphics.Image;

/**
 * Searches the DOM (both the AST and the index) for completion proposals.
 * 
 * @author Bryan Wilkinson
 */
public class DOMCompletionProposalComputer extends ParsingBasedProposalComputer {

	/**
	 * Default constructor is required (executable extension).
	 */
	public DOMCompletionProposalComputer() {
	}

	@Override
	protected List<ICompletionProposal> computeCompletionProposals(CContentAssistInvocationContext context,
			IASTCompletionNode completionNode, String prefix) {

		List<ICompletionProposal> proposals = new ArrayList<ICompletionProposal>();

		if (inPreprocessorDirective(context)) {
			if (!inPreprocessorKeyword(context)) {
				// add only macros
				if (prefix.length() == 0) {
					try {
						prefix = context.computeIdentifierPrefix().toString();
					} catch (BadLocationException exc) {
						CUIPlugin.log(exc);
					}
				}
				addMacroProposals(context, prefix, proposals);
			}
		} else {
			boolean handleMacros = false;
			IASTName[] names = completionNode.getNames();

			for (int i = 0; i < names.length; ++i) {
				if (names[i].getTranslationUnit() == null) {
					// The node isn't properly hooked up, must have backtracked out of this node
					continue;
				}

				IASTCompletionContext astContext = names[i].getCompletionContext();
				if (astContext == null) {
					continue;
				} else if (astContext instanceof IASTIdExpression || astContext instanceof IASTNamedTypeSpecifier) {
					// handle macros only if there is a prefix
					handleMacros = prefix.length() > 0;
				}

				IBinding[] bindings = astContext.findBindings(names[i], !context.isContextInformationStyle());
				if (bindings != null) {
					for (int j = 0; j < bindings.length; ++j) {
						handleBinding(bindings[j], context, prefix, astContext, proposals);
					}
				}
			}

			if (handleMacros) {
				addMacroProposals(context, prefix, proposals);
			}
		}

		return proposals;
	}

	/**
	 * Test whether the invocation offset is inside or before the preprocessor directive keyword.
	 * 
	 * @param context
	 *            the invocation context
	 * @return <code>true</code> if the invocation offset is inside or before the directive keyword
	 */
	private boolean inPreprocessorKeyword(CContentAssistInvocationContext context) {
		IDocument doc = context.getDocument();
		int offset = context.getInvocationOffset();

		try {
			final ITypedRegion partition = TextUtilities.getPartition(doc, ICPartitions.C_PARTITIONING, offset, true);
			if (ICPartitions.C_PREPROCESSOR.equals(partition.getType())) {
				String ppPrefix = doc.get(partition.getOffset(), offset - partition.getOffset());
				if (ppPrefix.matches("\\s*#\\s*\\w*")) { //$NON-NLS-1$
					// we are inside the directive keyword
					return true;
				}
			}

		} catch (BadLocationException exc) {
		}
		return false;
	}

	/**
	 * Check if the invocation offset is inside a preprocessor directive.
	 * 
	 * @param context
	 *            the content asist invocation context
	 * @return <code>true</code> if invocation offset is inside a preprocessor directive
	 */
	private boolean inPreprocessorDirective(CContentAssistInvocationContext context) {
		IDocument doc = context.getDocument();
		int offset = context.getInvocationOffset();

		try {
			final ITypedRegion partition = TextUtilities.getPartition(doc, ICPartitions.C_PARTITIONING, offset, true);
			if (ICPartitions.C_PREPROCESSOR.equals(partition.getType())) {
				return true;
			}

		} catch (BadLocationException exc) {
		}
		return false;
	}

	private void addMacroProposals(CContentAssistInvocationContext context, String prefix,
			List<ICompletionProposal> proposals) {
		char[] prefixChars = prefix.toCharArray();
		final boolean matchPrefix = !context.isContextInformationStyle();
		IASTCompletionNode completionNode = context.getCompletionNode();
		IASTPreprocessorMacroDefinition[] macros = completionNode.getTranslationUnit().getMacroDefinitions();
		if (macros != null) {
			for (int i = 0; i < macros.length; ++i) {
				final char[] macroName = macros[i].getName().toCharArray();
				if (CharArrayUtils.equals(macroName, 0, matchPrefix ? prefixChars.length : macroName.length,
						prefixChars, true)) {
					handleMacro(macros[i], context, prefix, proposals);
				}
			}
		}
		macros = completionNode.getTranslationUnit().getBuiltinMacroDefinitions();
		if (macros != null) {
			for (int i = 0; i < macros.length; ++i) {
				final char[] macroName = macros[i].getName().toCharArray();
				if (CharArrayUtils.equals(macroName, 0, matchPrefix ? prefixChars.length : macroName.length,
						prefixChars, true)) {
					handleMacro(macros[i], context, prefix, proposals);
				}
			}
		}
	}

	private void handleMacro(IASTPreprocessorMacroDefinition macro, CContentAssistInvocationContext context,
			String prefix, List<ICompletionProposal> proposals) {

//CElementHandleFactory.create;
		final String macroName = macro.getName().toString();
		final int baseRelevance = computeBaseRelevance(prefix, macroName);

		Image image = getImage(CElementImageProvider.getMacroImageDescriptor());

		if (macro instanceof IASTPreprocessorFunctionStyleMacroDefinition) {
			IASTPreprocessorFunctionStyleMacroDefinition functionMacro = (IASTPreprocessorFunctionStyleMacroDefinition) macro;

			StringBuilder repStringBuff = new StringBuilder();
			repStringBuff.append(macroName);
			repStringBuff.append('(');

			StringBuilder args = new StringBuilder();

			IASTFunctionStyleMacroParameter[] params = functionMacro.getParameters();
			if (params != null) {
				for (int i = 0; i < params.length; ++i) {
					if (i > 0) {
						args.append(", "); //$NON-NLS-1$
					}
					args.append(params[i].getParameter());
				}
			}
			String argString = args.toString();

			StringBuilder descStringBuff = new StringBuilder(repStringBuff.toString());
			descStringBuff.append(argString);
			descStringBuff.append(')');

			repStringBuff.append(')');
			String repString = repStringBuff.toString();
			String descString = descStringBuff.toString();

			CCompletionProposal proposal = createProposal(repString, descString, prefix.length(), image, baseRelevance
					+ RelevanceConstants.MACRO_TYPE_RELEVANCE, context, getCElement(macro), macro.getName().toString());
			if (!context.isContextInformationStyle()) {
				proposal.setCursorPosition(repString.length() - 1);
			}

			if (argString.length() > 0) {
				CProposalContextInformation info = new CProposalContextInformation(image, descString, argString);
				info.setContextInformationPosition(context.getContextInformationOffset());
				proposal.setContextInformation(info);
			}

			proposals.add(proposal);
		} else {
			proposals.add(createProposal(macroName, macroName, prefix.length(), image, baseRelevance
					+ RelevanceConstants.MACRO_TYPE_RELEVANCE, context, getCElement(macro), macro.getName().toString()));
		}
	}

	@SuppressWarnings("restriction")
	protected void handleBinding(IBinding binding, CContentAssistInvocationContext cContext, String prefix,
			IASTCompletionContext astContext, List<ICompletionProposal> proposals) {

		if ((binding instanceof CPPImplicitFunction || binding instanceof CPPImplicitTypedef)
				&& !(binding instanceof CPPImplicitMethod)) {
			return;
		}

		if (!isAnonymousBinding(binding)) {
			final String name = binding.getName();
			final int baseRelevance = computeBaseRelevance(prefix, name);
			if (binding instanceof ICPPClassType) {
				handleClass((ICPPClassType) binding, astContext, cContext, baseRelevance, proposals);
			} else if (binding instanceof IFunction) {
				handleFunction((IFunction) binding, cContext, baseRelevance, proposals);
			} else if (!cContext.isContextInformationStyle()) {
				if (binding instanceof IVariable) {
					handleVariable((IVariable) binding, cContext, baseRelevance, proposals);
				} else if (binding instanceof ITypedef) {
					proposals.add(createProposal(name, name, getImage(binding), baseRelevance
							+ RelevanceConstants.TYPEDEF_TYPE_RELEVANCE, cContext, getCElement(binding),
							binding.getName()));
				} else if (binding instanceof ICPPNamespace) {
					handleNamespace((ICPPNamespace) binding, astContext, cContext, baseRelevance, proposals);
				} else if (binding instanceof IEnumeration) {
					proposals.add(createProposal(name, name, getImage(binding), baseRelevance
							+ RelevanceConstants.ENUMERATION_TYPE_RELEVANCE, cContext, getCElement(binding),
							binding.getName()));
				} else if (binding instanceof IEnumerator) {
					proposals.add(createProposal(name, name, getImage(binding), baseRelevance
							+ RelevanceConstants.ENUMERATOR_TYPE_RELEVANCE, cContext, getCElement(binding),
							binding.getName()));
				} else {
					proposals.add(createProposal(name, name, getImage(binding), baseRelevance
							+ RelevanceConstants.DEFAULT_TYPE_RELEVANCE, cContext, getCElement(binding),
							binding.getName()));
				}
			}
		}
	}

	private boolean isAnonymousBinding(IBinding binding) {
		char[] name = binding.getNameCharArray();
		return name.length == 0 || name[0] == '{';
	}

	private void handleClass(ICPPClassType classType, IASTCompletionContext astContext,
			CContentAssistInvocationContext context, int baseRelevance, List<ICompletionProposal> proposals) {
		if (context.isContextInformationStyle()) {
			try {
				ICPPConstructor[] constructors = classType.getConstructors();
				for (ICPPConstructor constructor : constructors) {
					handleFunction(constructor, context, baseRelevance, proposals);
				}
			} catch (Exception e) {
				handle(e);
			}
		} else {
			int relevance = 0;
			try {
				switch (classType.getKey()) {
				case ICPPClassType.k_class:
					relevance = RelevanceConstants.CLASS_TYPE_RELEVANCE;
					break;
				case ICompositeType.k_struct:
					relevance = RelevanceConstants.STRUCT_TYPE_RELEVANCE;
					break;
				case ICompositeType.k_union:
					relevance = RelevanceConstants.UNION_TYPE_RELEVANCE;
					break;
				}
			} catch (Exception e) {
				handle(e);
			}
			if (astContext instanceof IASTName && !(astContext instanceof ICPPASTQualifiedName)) {
				IASTName name = (IASTName) astContext;
				if (name.getParent() instanceof IASTDeclarator) {
					proposals.add(createProposal(
							classType.getName() + "::", classType.getName(), getImage(classType), baseRelevance + relevance, context, getCElement(classType), classType.getName())); //$NON-NLS-1$
				}
			}
			proposals.add(createProposal(classType.getName(), classType.getName(), getImage(classType), baseRelevance
					+ RelevanceConstants.CLASS_TYPE_RELEVANCE, context, getCElement(classType), classType.getName()));
		}
	}

	private void handle(Exception e) {
		if (e instanceof RuntimeException) {
			throw (RuntimeException) e;
		}
	}

	private void handleFunction(IFunction function, CContentAssistInvocationContext context, int baseRelevance,
			List<ICompletionProposal> proposals) {
		Image image = getImage(function);

		StringBuilder repStringBuff = new StringBuilder();
		repStringBuff.append(function.getName());
		repStringBuff.append('(');

		StringBuilder dispargs = new StringBuilder(); // for the displayString
		StringBuilder idargs = new StringBuilder(); // for the idString
		String returnTypeStr = null;
		try {
			IParameter[] params = function.getParameters();
			if (params != null) {
				for (int i = 0; i < params.length; ++i) {
					IType paramType = params[i].getType();
					if (i > 0) {
						dispargs.append(',');
						idargs.append(',');
					}

					dispargs.append(ASTTypeUtil.getType(paramType, false));
					idargs.append(ASTTypeUtil.getType(paramType, false));
					String paramName = params[i].getName();
					if (paramName != null && paramName.length() > 0) {
						dispargs.append(' ');
						dispargs.append(paramName);
					}
				}

				if (function.takesVarArgs()) {
					if (params.length > 0) {
						dispargs.append(',');
						idargs.append(',');
					}
					dispargs.append("..."); //$NON-NLS-1$
					idargs.append("..."); //$NON-NLS-1$
				} else if (params.length == 0) { // force the void in
					dispargs.append("void"); //$NON-NLS-1$
					idargs.append("void"); //$NON-NLS-1$
				}
			}
			IFunctionType functionType = function.getType();
			if (functionType != null) {
				IType returnType = functionType.getReturnType();
				if (returnType != null) {
					returnTypeStr = ASTTypeUtil.getType(returnType, false);
				}
			}
		} catch (Exception e) {
		}

		String dispargString = dispargs.toString();
		String idargString = idargs.toString();

		StringBuilder dispStringBuff = new StringBuilder(repStringBuff.toString());
		dispStringBuff.append(dispargString);
		dispStringBuff.append(')');
		if (returnTypeStr != null && returnTypeStr.length() > 0) {
			dispStringBuff.append(" : "); //$NON-NLS-1$
			dispStringBuff.append(returnTypeStr);
		}
		String dispString = dispStringBuff.toString();

		StringBuilder idStringBuff = new StringBuilder(repStringBuff.toString());
		idStringBuff.append(idargString);
		idStringBuff.append(')');
		String idString = idStringBuff.toString();

		repStringBuff.append(')');
		String repString = repStringBuff.toString();

		final int relevance = function instanceof ICPPMethod
				? RelevanceConstants.METHOD_TYPE_RELEVANCE
				: RelevanceConstants.FUNCTION_TYPE_RELEVANCE;
		CCompletionProposal proposal = createProposal(repString, dispString, idString, context.getCompletionNode()
				.getLength(), image, baseRelevance + relevance, context, getCElement(function), function.getName());
		if (!context.isContextInformationStyle()) {
			proposal.setCursorPosition(repString.length() - 1);
		}

		if (dispargString.length() > 0) {
			CProposalContextInformation info = new CProposalContextInformation(image, dispString, dispargString);
			info.setContextInformationPosition(context.getContextInformationOffset());
			proposal.setContextInformation(info);
		}

		proposals.add(proposal);
	}

	private void handleVariable(IVariable variable, CContentAssistInvocationContext context, int baseRelevance,
			List<ICompletionProposal> proposals) {
		StringBuilder repStringBuff = new StringBuilder();
		repStringBuff.append(variable.getName());

		String returnTypeStr = "<unknown>"; //$NON-NLS-1$
		try {
			IType varType = variable.getType();
			if (varType != null) {
				returnTypeStr = ASTTypeUtil.getType(varType, false);
			}
		} catch (Exception e) {
		}

		StringBuilder dispStringBuff = new StringBuilder(repStringBuff.toString());
		if (returnTypeStr != null) {
			dispStringBuff.append(" : "); //$NON-NLS-1$
			dispStringBuff.append(returnTypeStr);
		}
		String dispString = dispStringBuff.toString();

		StringBuilder idStringBuff = new StringBuilder(repStringBuff.toString());
		String idString = idStringBuff.toString();

		String repString = repStringBuff.toString();

		Image image = getImage(variable);
		final int relevance = isLocalVariable(variable)
				? RelevanceConstants.LOCAL_VARIABLE_TYPE_RELEVANCE
				: isField(variable)
						? RelevanceConstants.FIELD_TYPE_RELEVANCE
						: RelevanceConstants.VARIABLE_TYPE_RELEVANCE;
		CCompletionProposal proposal = createProposal(repString, dispString, idString, context.getCompletionNode()
				.getLength(), image, baseRelevance + relevance, context, getCElement(variable), variable.getName());
		proposals.add(proposal);
	}

	private static boolean isField(IVariable variable) {
		return variable instanceof IField;
	}

	private static boolean isLocalVariable(IVariable variable) {
		try {
			return isLocalScope(variable.getScope());
		} catch (DOMException exc) {
			return false;
		}
	}

	private static boolean isLocalScope(IScope scope) {
		while (scope != null) {
			if (scope instanceof ICPPFunctionScope || scope instanceof ICPPBlockScope
					|| scope instanceof ICFunctionScope) {
				return true;
			}
			try {
				scope = scope.getParent();
			} catch (DOMException e) {
				scope = null;
			}
		}
		return false;
	}

	private void handleNamespace(ICPPNamespace namespace, IASTCompletionContext astContext,
			CContentAssistInvocationContext cContext, int baseRelevance, List<ICompletionProposal> proposals) {

		if (astContext instanceof ICPPASTQualifiedName) {
			IASTCompletionContext parent = ((ICPPASTQualifiedName) astContext).getCompletionContext();
			handleNamespace(namespace, parent, cContext, baseRelevance, proposals);
			return;
		}

		StringBuilder repStringBuff = new StringBuilder();
		repStringBuff.append(namespace.getName());

		if (!(astContext instanceof ICPPASTUsingDeclaration) && !(astContext instanceof ICPPASTUsingDirective)) {
			repStringBuff.append("::"); //$NON-NLS-1$
		}

		String repString = repStringBuff.toString();
		proposals.add(createProposal(repString, namespace.getName(), getImage(namespace), baseRelevance
				+ RelevanceConstants.NAMESPACE_TYPE_RELEVANCE, cContext, getCElement(namespace), namespace.getName()));
	}

	private ICElement getCElement(ICPPNamespace namespace) {
		// ignore
		return null;
	}

	private ICElement getCElement(IVariable variable) {
		return null;
	}

	private ICElement getCElement(IFunction function) {

		return null;
	}

	private ICElement getCElement(ICPPClassType classType) {
		return null;
	}

	private ICElement getCElement(IASTPreprocessorMacroDefinition macro) {
		if (macro != null && macro.getFileLocation() != null) {
			try {
				return getCElement(macro.getFileLocation().getFileName(), macro.getFileLocation().getNodeOffset());
			} catch (Exception e) {
				StatusHandler.log(new Status(IStatus.ERROR, CDTUIBridgePlugin.ID_PLUGIN,
						"Unable to get CElement for Macro", e)); //$NON-NLS-1$
			}
		}
		return null;
	}

	@SuppressWarnings("restriction")
	private ICElement getCElement(IBinding binding) {
		IASTNode definition = null;
		if (binding instanceof ICInternalBinding) {
			IASTNode[] declarations = ((ICInternalBinding) binding).getDeclarations();
			if (declarations != null && declarations.length > 0) {
				definition = declarations[0];
			}
		} else if (binding instanceof ICPPInternalBinding) {
			IASTNode[] declarations = ((ICPPInternalBinding) binding).getDeclarations();
			if (declarations != null && declarations.length > 0) {
				definition = declarations[0];
			}
		}
		if (definition != null && definition.getFileLocation() != null) {
			try {
				return getCElement(definition.getFileLocation().getFileName(), definition.getFileLocation()
						.getNodeOffset());
			} catch (CModelException e) {
				StatusHandler.log(new Status(IStatus.ERROR, CDTUIBridgePlugin.ID_PLUGIN,
						"Unable to get CElement for Binding", e)); //$NON-NLS-1$
			}
		}
		return null;
	}

	private ICElement getCElement(String filename, int offset) throws CModelException {

		IFile res = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(new Path(filename));

		ITranslationUnit cu = null;
		if (res instanceof IFile) {
			IFile file = res;
			if (CoreModel.isValidTranslationUnitName(null, file.getName())) {
				cu = CoreModelUtil.findTranslationUnit(file);
			} else {
				return null;
			}
		}
		if (cu != null) {
			ICElement ce = cu.getElementAtOffset(offset);
			return ce;
		} else {
			return null;
		}
	}

	private CCompletionProposal createProposal(String repString, String dispString, Image image, int relevance,
			CContentAssistInvocationContext context, ICElement element, String bindingName) {
		return createProposal(repString, dispString, null, context.getCompletionNode().getLength(), image, relevance,
				context, element, bindingName);
	}

	private CCompletionProposal createProposal(String repString, String dispString, int prefixLength, Image image,
			int relevance, CContentAssistInvocationContext context, ICElement element, String bindingName) {
		return createProposal(repString, dispString, null, prefixLength, image, relevance, context, element,
				bindingName);
	}

	private CCompletionProposal createProposal(String repString, String dispString, String idString, int prefixLength,
			Image image, int relevance, CContentAssistInvocationContext context, ICElement element, String bindingName) {
		int parseOffset = context.getParseOffset();
		int invocationOffset = context.getInvocationOffset();
		boolean doReplacement = !context.isContextInformationStyle();

		int repLength = doReplacement ? prefixLength : 0;
		int repOffset = doReplacement ? parseOffset - repLength : invocationOffset;
		repString = doReplacement ? repString : ""; //$NON-NLS-1$

		return new CCompletionProposal(repString, repOffset, repLength, image, dispString, idString, relevance,
				context.getViewer(), element, bindingName);
	}

	private Image getImage(ImageDescriptor desc) {
		return desc != null ? CommonImages.getImage(desc) : null;
	}

	private Image getImage(IBinding binding) {
		ImageDescriptor imageDescriptor = null;

		try {
			if (binding instanceof ITypedef) {
				imageDescriptor = CElementImageProvider.getTypedefImageDescriptor();
			} else if (binding instanceof ICompositeType) {
				if (((ICompositeType) binding).getKey() == ICPPClassType.k_class
						|| binding instanceof ICPPClassTemplate) {
					imageDescriptor = CElementImageProvider.getClassImageDescriptor();
				} else if (((ICompositeType) binding).getKey() == ICompositeType.k_struct) {
					imageDescriptor = CElementImageProvider.getStructImageDescriptor();
				} else if (((ICompositeType) binding).getKey() == ICompositeType.k_union) {
					imageDescriptor = CElementImageProvider.getUnionImageDescriptor();
				}
			} else if (binding instanceof ICPPMethod) {
				switch (((ICPPMethod) binding).getVisibility()) {
				case ICPPMember.v_private:
					imageDescriptor = CElementImageProvider.getMethodImageDescriptor(ASTAccessVisibility.PRIVATE);
					break;
				case ICPPMember.v_protected:
					imageDescriptor = CElementImageProvider.getMethodImageDescriptor(ASTAccessVisibility.PROTECTED);
					break;
				default:
					imageDescriptor = CElementImageProvider.getMethodImageDescriptor(ASTAccessVisibility.PUBLIC);
					break;
				}
			} else if (binding instanceof IFunction) {
				imageDescriptor = CElementImageProvider.getFunctionImageDescriptor();
			} else if (binding instanceof ICPPField) {
				switch (((ICPPField) binding).getVisibility()) {
				case ICPPMember.v_private:
					imageDescriptor = CElementImageProvider.getFieldImageDescriptor(ASTAccessVisibility.PRIVATE);
					break;
				case ICPPMember.v_protected:
					imageDescriptor = CElementImageProvider.getFieldImageDescriptor(ASTAccessVisibility.PROTECTED);
					break;
				default:
					imageDescriptor = CElementImageProvider.getFieldImageDescriptor(ASTAccessVisibility.PUBLIC);
					break;
				}
			} else if (binding instanceof IField) {
				imageDescriptor = CElementImageProvider.getFieldImageDescriptor(ASTAccessVisibility.PUBLIC);
			} else if (binding instanceof IVariable) {
				imageDescriptor = CElementImageProvider.getVariableImageDescriptor();
			} else if (binding instanceof IEnumeration) {
				imageDescriptor = CElementImageProvider.getEnumerationImageDescriptor();
			} else if (binding instanceof IEnumerator) {
				imageDescriptor = CElementImageProvider.getEnumeratorImageDescriptor();
			} else if (binding instanceof ICPPNamespace) {
				imageDescriptor = CElementImageProvider.getNamespaceImageDescriptor();
			} else if (binding instanceof ICPPFunctionTemplate) {
				imageDescriptor = CElementImageProvider.getFunctionImageDescriptor();
			} else if (binding instanceof ICPPUsingDeclaration) {
				IBinding[] delegates = ((ICPPUsingDeclaration) binding).getDelegates();
				if (delegates.length > 0) {
					return getImage(delegates[0]);
				}
			}
		} catch (Exception e) {
			handle(e);
		}

		return imageDescriptor != null ? CommonImages.getImage(imageDescriptor) : null;
	}
}
