package org.jboss.windup.rules.apps.java.reporting.freemarker;

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

import com.tinkerpop.blueprints.Direction;
import org.jboss.windup.config.GraphRewrite;
import org.jboss.windup.graph.GraphContext;
import org.jboss.windup.graph.model.resource.FileModel;
import org.jboss.windup.reporting.freemarker.WindupFreeMarkerMethod;
import org.jboss.windup.reporting.model.ClassificationModel;
import org.jboss.windup.reporting.model.InlineHintModel;
import org.jboss.windup.rules.apps.java.model.JavaClassFileModel;
import org.jboss.windup.rules.apps.java.model.JavaSourceFileModel;
import org.jboss.windup.rules.apps.java.query.FindFilesNotClassifiedOrHintedGremlinCriterion;
import org.jboss.windup.rules.apps.xml.model.XmlFileModel;
import org.jboss.windup.rules.files.model.FileLocationModel;
import org.jboss.windup.rules.files.model.FileReferenceModel;
import org.jboss.windup.util.ExecutionStatistics;

import com.tinkerpop.blueprints.Vertex;

import freemarker.ext.beans.StringModel;
import freemarker.template.TemplateModelException;

/**
 * Finds the files that have not had {@link ClassificationModel}s linked, and also does not have {@link FileLocationModel}s linked.
 * <p>
 * Called by:
 * <p>
 * findFilesNotClassifiedOrHinted(Iterable<FileModel>)
 * <p>
 * NOTE: This will only return JavaSourceFileModels and XmlFileModels in order to reduce clutter.
 *
 * @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a>
 */
public class FindFilesNotClassifiedOrHinted implements WindupFreeMarkerMethod
{
    private static final String NAME = "findFilesNotClassifiedOrHinted";
    private GraphContext context;

    @Override
    public void setContext(GraphRewrite event)
    {
        this.context = event.getGraphContext();
    }

    @Override
    public Object exec(@SuppressWarnings("rawtypes") List arguments) throws TemplateModelException
    {
        ExecutionStatistics.get().begin(NAME);
        if (arguments.size() != 1)
        {
            throw new TemplateModelException("Error, method expects one argument (Iterable<FileModel>)");
        }
        StringModel stringModelArg = (StringModel) arguments.get(0);
        @SuppressWarnings("unchecked")
        Iterable<FileModel> fileModels = (Iterable<FileModel>) stringModelArg.getWrappedObject();

        FindFilesNotClassifiedOrHintedGremlinCriterion criterion = new FindFilesNotClassifiedOrHintedGremlinCriterion();
        List<Vertex> initialFileModelsAsVertices = new ArrayList<>();
        for (FileModel fm : fileModels)
        {
            initialFileModelsAsVertices.add(fm.asVertex());
        }
        Iterable<Vertex> result = criterion.query(context, initialFileModelsAsVertices);

        List<FileModel> resultModels = new ArrayList<>();
        for (Vertex v : result)
        {
            FileModel f = context.getFramed().frame(v, FileModel.class);

            //we don't want to show our decompiled classes in the report
            boolean wasNotGenerated = !f.isWindupGenerated();
            boolean isOfInterestingType = f instanceof JavaSourceFileModel || f instanceof XmlFileModel || f instanceof JavaClassFileModel;
            //we don't want to list .class files that have their decompiled .java file with hints/classifications
            boolean withoutHiddenHints = true;

            if (f instanceof JavaClassFileModel)
            {
                Iterator<Vertex> decompiled = v.getVertices(Direction.OUT, JavaClassFileModel.DECOMPILED_FILE).iterator();
                if (decompiled.hasNext())
                {
                    withoutHiddenHints = !decompiled.next().getVertices(Direction.IN, FileReferenceModel.FILE_MODEL).iterator().hasNext();
                }
            }

            if (wasNotGenerated && withoutHiddenHints && isOfInterestingType)
            {
                //if it passed all the checks, add it
                resultModels.add(f);
            }
        }

        ExecutionStatistics.get().end(NAME);
        return resultModels;
    }

    @Override
    public String getMethodName()
    {
        return NAME;
    }

    @Override
    public String getDescription()
    {
        return "Takes an Iterable<" + FileModel.class.getSimpleName()
                    + "> as a parameter and returns the files that have neither " + ClassificationModel.class.getSimpleName()
                    + "s nor " + InlineHintModel.class.getSimpleName() + "s associated with them.";
    }
}
