package org.jboss.windup.rules.apps.java.query;

import com.tinkerpop.blueprints.Direction;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.gremlin.java.GremlinPipeline;
import com.tinkerpop.pipes.PipeFunction;
import org.jboss.forge.furnace.util.Lists;
import org.jboss.windup.graph.GraphContext;
import org.jboss.windup.graph.model.FileLocationModel;
import org.jboss.windup.graph.model.resource.FileModel;
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.util.ExecutionStatistics;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * This provides a helper class that can be used execute a Gremlin search returning all FileModels that do not have associated
 * {@link FileLocationModel}s or @{link ClassificationModel}s.
 * 
 * @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a>
 */
public class FindFilesNotClassifiedOrHintedGremlinCriterion
{
    public Iterable<Vertex> query(final GraphContext context, Iterable<Vertex> initialVertices)
    {
        ExecutionStatistics.get().begin("FindFilesNotClassifiedOrHintedGremlinCriterion.total");

        final List<Vertex> initialVerticesList = Lists.toList(initialVertices);

        GremlinPipeline<Vertex, Vertex> pipeline = new GremlinPipeline<>(initialVertices);

        final Set<Vertex> allClassifiedOrHintedVertices = new HashSet<>();

        ExecutionStatistics.get().begin("FindFilesNotClassifiedOrHintedGremlinCriterion.hintPipeline");
        // create a pipeline to get all hinted items
        GremlinPipeline<Vertex, Vertex> hintPipeline = new GremlinPipeline<>(
                    context.getQuery().type(InlineHintModel.class).vertices());
        hintPipeline.as("fileLocation1").out(FileLocationModel.FILE_MODEL).retain(initialVerticesList);
        hintPipeline.fill(allClassifiedOrHintedVertices);
        ExecutionStatistics.get().end("FindFilesNotClassifiedOrHintedGremlinCriterion.hintPipeline");

        ExecutionStatistics.get().begin("FindFilesNotClassifiedOrHintedGremlinCriterion.classificationPipeline");
        // create a pipeline to get all items with attached classifications
        GremlinPipeline<Vertex, Vertex> classificationPipeline = new GremlinPipeline<>(
                    context.getQuery().type(ClassificationModel.class).vertices());
        classificationPipeline.as("fileModel2").out(ClassificationModel.FILE_MODEL).retain(initialVerticesList);
        classificationPipeline.fill(allClassifiedOrHintedVertices);
        ExecutionStatistics.get().end("FindFilesNotClassifiedOrHintedGremlinCriterion.classificationPipeline");

        pipeline.filter(new PipeFunction<Vertex, Boolean>()
        {
            @Override
            public Boolean compute(Vertex v)
            {
                FileModel f = context.getFramed().frame(v, FileModel.class);

                //1. we don't want to show files with hints/classifications
                if (allClassifiedOrHintedVertices.contains(v))
                {
                    return false;
                }

                //2. we don't want to show our decompiled classes in the report
                if (f.isWindupGenerated())
                {
                    return false;
                }

                //3. we don't want to show class in case it's .java decompiled file has hints/classifications
                if (f instanceof JavaClassFileModel)
                {
                    Iterator<Vertex> decompiled = v.getVertices(Direction.OUT, JavaClassFileModel.DECOMPILED_FILE).iterator();
                    if (decompiled.hasNext())
                    {
                        JavaSourceFileModel source = context.getFramed().frame(decompiled.next(), JavaSourceFileModel.class);
                        if (allClassifiedOrHintedVertices.contains(source.asVertex()))
                        {
                            return false;
                        }
                    }
                }
                return true;
            }
        });

        ExecutionStatistics.get().end("FindFilesNotClassifiedOrHintedGremlinCriterion.total");
        return pipeline;
    }
}
