/*******************************************************************************
 *  Copyright (c) 2000, 2008 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *     Markus Schorn (Wind River Systems)
 *******************************************************************************/
package org.eclipse.cdt.internal.core.parser.util;

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

import junit.framework.Assert;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.IName;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.c.CASTVisitor;
import org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPConstructor;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.parser.ParserLanguage;
import org.eclipse.cdt.core.parser.util.ArrayUtil;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.content.IContentType;

/**
 * Utility class to have commonly used algorithms in one place for searching with the DOM. 
 * 
 * @author dsteffle
 */
public class DOMSearchUtil {
	private static final IASTName[] BLANK_NAME_ARRAY = new IASTName[0];
//    private static final IASTName[] EMPTY_NAME_LIST = BLANK_NAME_ARRAY;

    public static final int DECLARATIONS = 1;
    public static final int DEFINITIONS = 2;
    public static final int DECLARATIONS_DEFINITIONS = 3;
    public static final int REFERENCES = 4;
    public static final int ALL_OCCURRENCES = 5;
    /**
    * This retrieves the ParserLanguage from an IFile.
    *
    * @param file
    * @return
    */
    public static ParserLanguage getLanguageFromFile(IFile file) {
        IProject project = file.getProject();
        IContentType contentType = CCorePlugin.getContentType(project, file.getFullPath().lastSegment());
        if (contentType != null) {
        	String lid = contentType.getId();
        	if (CCorePlugin.CONTENT_TYPE_CXXSOURCE.equals(lid) || 
        			CCorePlugin.CONTENT_TYPE_CXXHEADER.equals(lid)) {
        		return ParserLanguage.CPP;
        	}
        }        
        return ParserLanguage.C;
    }

    /**
     * The CPPNameCollector used to get IASTNames from an IASTNode.
     * 
     * @author dsteffle
     */
    static public class CPPNameCollector extends CPPASTVisitor {
        {
            shouldVisitNames = true;
        }
        public List nameList = new ArrayList();
        public int visit( IASTName name ){
            nameList.add( name );
            return PROCESS_CONTINUE;
        }
        public IASTName getName( int idx ){
            if( idx < 0 || idx >= nameList.size() )
                return null;
            return (IASTName) nameList.get( idx );
        }
        public int size() { return nameList.size(); } 
    }

    /**
     * The CNameCollector used to get IASTNames from an IASTNode.
     * 
     * @author dsteffle
     */
    static public class CNameCollector extends CASTVisitor {
        {
            shouldVisitNames = true;
        }
        public List nameList = new ArrayList();
        public int visit( IASTName name ){
            nameList.add( name );
            return PROCESS_CONTINUE;
        }
        public IASTName getName( int idx ){
            if( idx < 0 || idx >= nameList.size() )
                return null;
            return (IASTName) nameList.get( idx );
        }
        public int size() { return nameList.size(); } 
    }
    
	/**
	 * Returns the ParserLanguage corresponding to the IPath and IProject.  Returns ParserLanguage.CPP if the file type is a header.
	 * 
	 * @param path
	 * @param project
	 * @return
	 */
    public static ParserLanguage getLanguage( IPath path, IProject project )
    {  
    	//FIXME: ALAIN, for headers should we assume CPP ??
    	// The problem is that it really depends on how the header was included.
    	String id = null;
    	IContentType contentType = CCorePlugin.getContentType(project, path.lastSegment());
    	if (contentType != null) {
    		id = contentType.getId();
    	}
    	if (id != null) {
    		if (CCorePlugin.CONTENT_TYPE_CXXHEADER.equals(id)) {
    			return ParserLanguage.CPP;
    		} else if (CCorePlugin.CONTENT_TYPE_CXXSOURCE.equals(id)) {
    			return ParserLanguage.CPP;
    		} else if (CCorePlugin.CONTENT_TYPE_CHEADER.equals(id)) {
    			return ParserLanguage.C; 				
    		} else if (CCorePlugin.CONTENT_TYPE_CSOURCE.equals(id)) {
    			return ParserLanguage.C;
    		} else if (CCorePlugin.CONTENT_TYPE_ASMSOURCE.equals(id)) {
    			// ???
    			// What do we do here ?
    		}
    	}
		return ParserLanguage.CPP;
    }

    /**
     * This is used to get the names from the TU that the IASTName searchName belongs to.
     * 
     * @param searchName the IASTName whose references/delcarations are to be retrieved
     * @param limitTo used to specify whether to get declarations, references, or both, one of: 
     * ( CSearchPattern.DECLARATION | CSearchPattern.REFERENCES | CSearchPattern.ALL_OCCURRENCES ) 
     * @return IASTName[] declarations, references, or both depending on limitTo that correspond to the IASTName searchName searched for
     */
    public static IName[] getNamesFromDOM(IASTName searchName, int limitTo) {
		IName[] names = null;
		IASTTranslationUnit tu = searchName.getTranslationUnit();
		
		if (tu == null) {
			return BLANK_NAME_ARRAY;
		}
		
		IBinding binding = searchName.resolveBinding();
		if (binding instanceof IIndexBinding) {
			Assert.fail("Not implemented");
//			try { 
//				ArrayList pdomNames = new ArrayList();
//				IPDOMResolver pdom= ((PDOMBinding) binding).getPDOM();
//				// First decls
//				names= pdom.getDeclarations(binding);
//				pdomNames.addAll(Arrays.asList(names));
//				// Next defs
//				names= pdom.getDefinitions(binding);
//				pdomNames.addAll(Arrays.asList(names));
//				names = (IName[])pdomNames.toArray(new IName[pdomNames.size()]);
//			} catch (CoreException e) {
//				CCorePlugin.log(e);
//			}
		} else {
			names = getNames(tu, binding, limitTo);
			
			if (names == null || names.length == 0) { // try alternate strategies		
				try {
					// fix for 86829, 95224
					if ((binding instanceof ICPPConstructor || (binding instanceof ICPPMethod && ((ICPPMethod)binding).isDestructor())) 
							&& binding.getScope() instanceof ICPPClassScope) {
						binding =  ((ICPPClassScope)binding.getScope()).getClassType();
						names = getNames(tu, binding, limitTo);
					}
				} catch (DOMException e) {}
			}
		}

		return names;
    }
	
	private static IASTName[] getNames(IASTTranslationUnit tu, IBinding binding, int limitTo) {
        IASTName[] names = null;
		if (limitTo == DECLARATIONS ||
			limitTo == DECLARATIONS_DEFINITIONS) {
            names = tu.getDeclarationsInAST(binding);
        } else if (limitTo == REFERENCES) {
            names = tu.getReferences(binding);
        } else if (limitTo == DEFINITIONS) {
            names = tu.getDefinitionsInAST(binding);
        } else if (limitTo == ALL_OCCURRENCES){
            names = tu.getDeclarationsInAST(binding);
            names = (IASTName[])ArrayUtil.addAll(IASTName.class, names, tu.getReferences(binding));
        } else {  // assume ALL
            names = tu.getDeclarationsInAST(binding);
            names = (IASTName[])ArrayUtil.addAll(IASTName.class, names, tu.getReferences(binding));
        }
		
		return names;
	}

}
