/*******************************************************************************
 * Copyright (c) 2007, 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:
 *     Bryan Wilkinson (QNX) - Initial API and implementation
 *     Anton Leherbauer (Wind River Systems)
 *******************************************************************************/
package org.eclipse.cdt.internal.ui.text.contentassist;

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

import org.eclipse.core.runtime.CoreException;
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.swt.graphics.Image;

import org.eclipse.cdt.core.dom.ast.IASTCompletionNode;
import org.eclipse.cdt.core.dom.ast.IASTFieldReference;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.model.ICLanguageKeywords;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.text.ICPartitions;

import org.eclipse.cdt.internal.ui.viewsupport.CElementImageProvider;

public class KeywordCompletionProposalComputer extends ParsingBasedProposalComputer {

	private static final int MIN_KEYWORD_LENGTH = 5;
	
	@Override
	protected List<ICompletionProposal> computeCompletionProposals(
			CContentAssistInvocationContext context,
			IASTCompletionNode completionNode, String prefix)
			throws CoreException {

		if (prefix.length() == 0) {
			try {
				prefix= context.computeIdentifierPrefix().toString();
			} catch (BadLocationException exc) {
				CUIPlugin.log(exc);
			}
		}
		final int prefixLength = prefix.length();
		// No prefix, no completions
        if (prefixLength == 0 || context.isContextInformationStyle())
            return Collections.emptyList();

        // keywords are matched case-sensitive
		final int relevance = RelevanceConstants.CASE_MATCH_RELEVANCE + RelevanceConstants.KEYWORD_TYPE_RELEVANCE;

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

		ICLanguageKeywords languageKeywords = null;
		ITranslationUnit tu = context.getTranslationUnit();
        if(tu != null) {
        	ILanguage language = tu.getLanguage();
        	if(language instanceof ICLanguageKeywords)
        		languageKeywords = (ICLanguageKeywords)language;
        }
        	
        if(languageKeywords == null)
    		return Collections.emptyList();
        
		
		if (inPreprocessorDirective(context)) {
			// TODO split this into a separate proposal computer?
			boolean needDirectiveKeyword= inPreprocessorKeyword(context);

			// add matching preprocessor keyword proposals
			ImageDescriptor imagedesc = CElementImageProvider.getKeywordImageDescriptor();
			Image image = imagedesc != null ? CUIPlugin.getImageDescriptorRegistry().get(imagedesc) : null;
			
			for(String keyword : languageKeywords.getPreprocessorKeywords()) {
				if (keyword.startsWith(prefix) && keyword.length() > prefixLength) {
					String repString = keyword + ' ';
					int repLength = prefixLength;
					int repOffset = context.getInvocationOffset() - repLength;
					if (prefix.charAt(0) == '#') {
						// strip leading '#' from replacement
						repLength--;
						repOffset++;
						repString= repString.substring(1);
					} else if (needDirectiveKeyword) {
						continue;
					}
					proposals.add(new CCompletionProposal(repString, repOffset,
							repLength, image, keyword, relevance, context.getViewer()));
				}
			}
		} else {
	        if (!isValidContext(completionNode))
	            return Collections.emptyList();
	        
			// add matching keyword proposals
	        ImageDescriptor imagedesc = CElementImageProvider.getKeywordImageDescriptor();
	        Image image = imagedesc != null ? CUIPlugin.getImageDescriptorRegistry().get(imagedesc) : null;
	        
	        for(String keyword : languageKeywords.getKeywords()) {
	            if (keyword.startsWith(prefix) && keyword.length() > prefixLength && keyword.length() >= MIN_KEYWORD_LENGTH) {
	                int repLength = prefixLength;
	                int repOffset = context.getInvocationOffset() - repLength;
	                proposals.add(new CCompletionProposal(keyword, repOffset,
							repLength, image, keyword, relevance, context.getViewer()));
	            }
	        }
		}
        
        return proposals;
	}

	/**
	 * Checks whether the given invocation context looks valid for template completion.
	 * 
	 * @param context  the content assist invocation context
	 * @return <code>false</code> if the given invocation context looks like a field reference
	 */
	private boolean isValidContext(IASTCompletionNode completionNode) {
		IASTName[] names = completionNode.getNames();
		for (int i = 0; i < names.length; ++i) {
			IASTName name = names[i];
			
			// ignore if not connected
			if (name.getTranslationUnit() == null)
				continue;
		
			// ignore if this is a member access
			if (name.getParent() instanceof IASTFieldReference)
				continue;
			
			return true;
		}
		
		return false;
	}

	/**
	 * 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;
	}
}
