package org.apache.flex.compiler.internal.codegen.externals.pass;

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

import org.apache.flex.compiler.internal.codegen.externals.reference.*;

import com.google.javascript.jscomp.AbstractCompiler;
import com.google.javascript.jscomp.NodeTraversal;
import com.google.javascript.rhino.Node;

/**
 * @author: Frederic Thomas Date: 05/07/2015 Time: 18:16
 */
public class CollectImportsPass extends AbstractCompilerPass
{
    public CollectImportsPass(final ReferenceModel model, AbstractCompiler compiler)
    {
        super(model, compiler);
    }

    @Override
    public boolean shouldTraverse(final NodeTraversal nodeTraversal, final Node n, final Node parent)
    {
        for (ClassReference reference : model.getClasses())
        {
            collectClassImports(reference);
        }

        for (FunctionReference reference : model.getFunctions())
        {
            collectFunctionImports(reference);
        }

        return false;
    }

    private void collectClassImports(ClassReference reference)
    {
        final MethodReference constructor = reference.getConstructor();
        final List<ClassReference> superClasses = reference.getSuperClasses();
        final List<ClassReference> interfaces = reference.getInterfaces();
        final List<ClassReference> extendedInterfaces = reference.getExtendedInterfaces();
        final Map<String, FieldReference> fields = reference.getFields();
        final Map<String, MethodReference> methods = reference.getMethods();

        for (ClassReference superClass : superClasses)
        {
            if (model.isExcludedClass(superClass) == null)
            {
                addClassImport(reference, superClass);
            }
        }

        for (ClassReference _interface : interfaces)
        {
            if (model.isExcludedClass(_interface) == null)
            {
                addClassImport(reference, _interface);
            }
        }

        for (ClassReference _interface : extendedInterfaces)
        {
            if (model.isExcludedClass(_interface) == null)
            {
                addClassImport(reference, _interface);
            }
        }

        for (FieldReference field : fields.values())
        {
            if (field.isExcluded() == null)
            {
                addClassImport(reference, getType(field));
            }
        }

        for (ParameterReference parameterReference : constructor.getParameters())
        {
            addClassImport(reference, getType(parameterReference));
        }

        for (MethodReference method : methods.values())
        {
            if (method.isExcluded() == null)
            {
                addClassImport(reference, getReturnType(method));

                for (ParameterReference parameterReference : method.getParameters())
                {
                    addClassImport(reference, getType(parameterReference));
                }
            }
        }
    }

    private void addClassImport(final ClassReference thisReference, final ClassReference referenceToImport)
    {
        if (canImport(referenceToImport))
        {
            final String thisPackageName = thisReference.getPackageName();
            final String importPackageName = referenceToImport.getPackageName();

            if (!importPackageName.equals(thisPackageName))
            {
                thisReference.addImport(referenceToImport);
            }
        }
    }

    private void collectFunctionImports(final FunctionReference function)
    {
        if (function.isExcluded() == null)
        {
            ClassReference returnType = getReturnType(function);

            if (canImport(returnType))
            {
                function.addImport(returnType);
            }

            for (ParameterReference parameterReference : function.getParameters())
            {
                ClassReference type = getType(parameterReference);

                if (canImport(type))
                {
                    function.addImport(type);
                }
            }
        }
    }

    private ClassReference getType(final FieldReference field)
    {
        return model.getClassReference(field.toTypeString());
    }

    private ClassReference getReturnType(final MethodReference method)
    {
        return model.getClassReference(method.transformReturnString());
    }

    private ClassReference getReturnType(final FunctionReference function)
    {
        return model.getClassReference(function.transformReturnString());
    }

    private ClassReference getType(final ParameterReference parameter)
    {
        return model.getClassReference(parameter.getQualifiedName());
    }

    private boolean canImport(ClassReference reference)
    {
        return reference != null && reference.isQualifiedName() && model.isExcludedClass(reference) == null;
    }

    @Override
    public void visit(final NodeTraversal t, final Node n, final Node parent)
    {
    }
}

@@ -2386,32 +2386,79 @@
    %7D%0A        %7D%0A%0A
+        if (constructor != null)%0A        %7B%0A    
         for (Par
@@ -2523,32 +2523,36 @@
 ters())%0A        
+    
 %7B%0A            ad
@@ -2541,32 +2541,36 @@
   %7B%0A            
+ 
+   
 addClassImport(r
@@ -2601,32 +2601,46 @@
 terReference));%0A
+            %7D%0A
         %7D%0A%0A     

Check if we have a constructor