/**
 * Code copied from org.eclipse.jdt.ui.
 */
package com.intel.ide.eclipse.mpt.ast;

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

import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
import org.eclipse.jdt.internal.ui.text.correction.CorrectionMessages;
import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedCorrectionProposal;
import org.eclipse.jdt.internal.ui.text.correction.proposals.NewDefiningMethodProposal;
import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
import org.eclipse.jdt.ui.text.java.IInvocationContext;

/**
 * @author xsun12
 *
 */
public class QuickAssistProcessor {

    public static boolean getCreateInSuperClassProposals(IInvocationContext context, ASTNode node, Map<String, Map> proposals) throws CoreException {
        if (!(node instanceof SimpleName) || !(node.getParent() instanceof MethodDeclaration)) {
            return false;
        }
        MethodDeclaration decl= (MethodDeclaration) node.getParent();
        if (decl.getName() != node || decl.resolveBinding() == null || Modifier.isPrivate(decl.getModifiers())) {
            return false;
        }

        ICompilationUnit cu= context.getCompilationUnit();
        CompilationUnit astRoot= context.getASTRoot();

        IMethodBinding binding= decl.resolveBinding();
        ITypeBinding[] paramTypes= binding.getParameterTypes();

        ITypeBinding[] superTypes= getAllSuperTypes(binding.getDeclaringClass());

        List<SingleVariableDeclaration> params= decl.parameters();
        String[] paramNames= new String[paramTypes.length];
        for (int i = 0; i < params.size(); i++) {
            SingleVariableDeclaration param= params.get(i);
            paramNames[i]= param.getName().getIdentifier();
        }

        for (int i= 0; i < superTypes.length; i++) {
            ITypeBinding curr= superTypes[i];
            if (curr.isFromSource()) {
                IMethodBinding method= Bindings.findOverriddenMethodInType(curr, binding);
                if (method == null) {
                    ITypeBinding typeDecl= curr.getTypeDeclaration();
                    ICompilationUnit targetCU= ASTResolving.findCompilationUnitForBinding(cu, astRoot, typeDecl);
                    if (targetCU != null) {
                        String fqName = targetCU.findPrimaryType().getFullyQualifiedName();
                        Map<String, LinkedCorrectionProposal> proposalsMap = proposals.get(fqName);
                        
                        if (proposalsMap == null){
                             continue;
                        }
                        String label= Messages.format(CorrectionMessages.QuickAssistProcessor_createmethodinsuper_description, new String[] { BasicElementLabels.getJavaElementName(curr.getName()), BasicElementLabels.getJavaElementName(binding.getName()) });
                        proposalsMap.put(binding.getName(), new NewDefiningMethodProposal(label, targetCU, astRoot, typeDecl, binding, paramNames, IProposalRelevance.CREATE_METHOD_IN_SUPER));
                    }
                }
            }
        }
        return true;
    }
    

    /**
     * Returns all super types (classes and interfaces) for the given type in inheritance order.
     * @param type The type to get the supertypes of.
     * @return all super types (excluding <code>type</code>)
     */
    public static ITypeBinding[] getAllSuperTypes(ITypeBinding type) {
        ArrayList<ITypeBinding> result= new ArrayList<ITypeBinding>();

        collectSuperTypes(type, result);
        
        for(int i=0; i< result.size(); i++){
            type = result.get(i);
            collectSuperTypes(type, result);
        }

//        result.remove(type);
        return result.toArray(new ITypeBinding[result.size()]);
    }

    private static void collectSuperTypes(ITypeBinding curr, ArrayList<ITypeBinding> list) {
        ITypeBinding superClass= curr.getSuperclass();
        if (superClass != null) {
            list.add(superClass);
        }
        
        ITypeBinding[] interfaces = curr.getInterfaces();
        for (int i = 0; i < interfaces.length; i++) {
            list.add(interfaces[i]);
        }
    }

}

@@ -1352,25 +1352,33 @@
 g, Map%3E 
-proposals
+missingMethodsMap
 ) throws
@@ -1979,24 +1979,103 @@
 erTypes();%0A%0A
+%09%09if(!checkParameterTypes(paramTypes, missingMethodsMap))%0A%09%09    return false;%0A%0A
         ITyp
@@ -2445,32 +2445,120 @@
 r();%0A        %7D%0A%0A
+%09%09String sig= ASTResolving.getMethodSignature(binding.getName(), paramTypes, false);%0A%09%09%0A
         for (int
@@ -3205,25 +3205,33 @@
 lsMap = 
-proposals
+missingMethodsMap
 .get(fqN
@@ -3676,33 +3676,19 @@
 Map.put(
-binding.getName()
+sig
 , new Ne
@@ -3813,16 +3813,47 @@
 UPER));%0A
+                        break;%0A
         
@@ -3935,20 +3935,697 @@
 ;%0A    %7D%0A
-    
+%0A%09/**%0A     * @param parameterTypes Type bindings of the arguments%0A     * @param missingMethodsMap Missing methods map%0A     * @return If a method with the given argument types could be generated safely%0A     */%0A    private static boolean checkParameterTypes(ITypeBinding%5B%5D parameterTypes,%0A            Map%3CString, Map%3E missingMethodsMap) %7B%0A        if (parameterTypes == null)%0A            return false;%0A%0A        for (int i = 0; i %3C parameterTypes.length; i++) %7B%0A            ITypeBinding binding = parameterTypes%5Bi%5D;%0A            if (binding.isFromSource() && !missingMethodsMap.containsKey(binding.getQualifiedName()))%0A                return false;%0A        %7D%0A%0A        return true;%0A    %7D
 %0A%0A    /*

Fix bug 1849 - use method signature string as the key to the hash map