package org.jboss.windup.rules.apps.tattletale;

import java.io.File;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

import com.google.common.base.StandardSystemProperty;
import org.jboss.tattletale.Main;
import org.jboss.windup.config.AbstractRuleProvider;
import org.jboss.windup.config.GraphRewrite;
import org.jboss.windup.config.loader.RuleLoaderContext;
import org.jboss.windup.config.metadata.MetadataBuilder;
import org.jboss.windup.config.operation.GraphOperation;
import org.jboss.windup.graph.GraphContext;
import org.jboss.windup.graph.model.ProjectModel;
import org.jboss.windup.graph.model.WindupConfigurationModel;
import org.jboss.windup.graph.model.resource.FileModel;
import org.jboss.windup.graph.service.WindupConfigurationService;
import org.jboss.windup.reporting.model.ApplicationReportModel;
import org.jboss.windup.reporting.model.TemplateType;
import org.jboss.windup.reporting.service.ApplicationReportService;
import org.jboss.windup.reporting.service.ReportService;
import org.jboss.windup.util.exception.WindupException;
import org.ocpsoft.rewrite.config.Configuration;
import org.ocpsoft.rewrite.config.ConfigurationBuilder;
import org.ocpsoft.rewrite.context.EvaluationContext;

/**
 * Runs Tattletale on the Windup's input.
 */
public class TattletaleRuleProvider extends AbstractRuleProvider
{
    public static final String REPORT_TEMPLATE = "/reports/templates/embedded.ftl";
    private static final String TATTLETALE_REPORT_SUBDIR = "tattletale";
    public static final String REPORT_DESCRIPTION = "This report contains the results of running Tattletale on the input application.";

    public TattletaleRuleProvider()
    {
        super(MetadataBuilder.forProvider(TattletaleRuleProvider.class));
    }

    @Override
    public Configuration getConfiguration(RuleLoaderContext ruleLoaderContext)
    {
        return ConfigurationBuilder.begin()
                    .addRule()
                    .perform(new TattletaleOperation());
    }

    private class TattletaleOperation extends GraphOperation
    {
        private static final String TTALE_CONFIG_FILE_NAME = "tattletale-config.properties";

        @Override
        public void perform(GraphRewrite event, EvaluationContext context)
        {
            Boolean generateReport = (Boolean) event.getGraphContext().getOptionMap().get(EnableTattletaleReportOption.NAME);
            if (generateReport == null || !generateReport)
                return;

            WindupConfigurationModel configuration = WindupConfigurationService.getConfigurationModel(event.getGraphContext());
            for (FileModel input : configuration.getInputPaths())
            {
                String inputPath = input.getFilePath();
                Path reportDirectory = new ReportService(event.getGraphContext()).getReportDirectory();

                String tattletaleRelativePath = TATTLETALE_REPORT_SUBDIR + File.separator + input.getFileName();
                Path tattletaleReportPath = reportDirectory.resolve(tattletaleRelativePath);

                for (int i = 1; Files.exists(tattletaleReportPath); i++)
                {
                    tattletaleRelativePath = TATTLETALE_REPORT_SUBDIR + File.separator + input.getFileName() + "." + i;
                    tattletaleReportPath = reportDirectory.resolve(tattletaleRelativePath);
                }
                String tattletaleDir = tattletaleReportPath.toString();

                Main main = new Main();
                main.setSource(inputPath);
                main.setDestination(tattletaleDir);

                try
                {
                    // The only way Tattletale accepts configuration is through a file.
                    new File(tattletaleDir).mkdirs();
                    File configPath = new File(tattletaleDir, TTALE_CONFIG_FILE_NAME);
                    try (PrintStream str = new PrintStream(configPath))
                    {
                        str.append("enableDot=false\n"); // Whether to generate .dot and .png
                        str.append("graphvizDot=dot\n"); // Dot executable
                        str.close();
                    }
                    main.setConfiguration(configPath.getAbsolutePath());

                    /*
                     * HACK - reset temp directory temporarily as otherwise there are cases where tattletale
                     * will actually overwrite the input file. This can happen in cases where the application
                     * being analyzed is actually in the temp directory, due to a bug in Tattletale. (2016/05/19)
                     */
                    String previousTmpDir = StandardSystemProperty.JAVA_IO_TMPDIR.value();
                    System.setProperty(StandardSystemProperty.JAVA_IO_TMPDIR.key(), Paths.get(tattletaleDir).resolve("tattletaletmp").toString());
                    main.execute();
                    System.setProperty(StandardSystemProperty.JAVA_IO_TMPDIR.key(), previousTmpDir);

                    createReportModel(event.getGraphContext(), input, tattletaleRelativePath);
                }
                catch (Exception e)
                {
                    throw new WindupException("Failed to run Tattletale due to: " + e.getMessage());
                }
            }
        }

        private void createReportModel(GraphContext context, FileModel input, String reportRelativePath)
        {
            ProjectModel inputProjectModel = input.getProjectModel();

            ApplicationReportModel applicationReportModel = new ApplicationReportService(context).create();
            applicationReportModel.setReportName("Tattletale");
            applicationReportModel.setDescription(REPORT_DESCRIPTION);
            applicationReportModel.setReportIconClass("glyphicon tattletale-nav-logo");
            applicationReportModel.setReportPriority(500);

            applicationReportModel.setDisplayInApplicationReportIndex(true);
            applicationReportModel.setProjectModel(inputProjectModel);
            applicationReportModel.setTemplatePath(REPORT_TEMPLATE);
            applicationReportModel.setTemplateType(TemplateType.FREEMARKER);

            Map<String, String> reportProperties = new HashMap<>();
            reportProperties.put("embeddedTitle", "Tattletale Report");
            reportProperties.put("embeddedUrl", reportRelativePath + "/index.html");

            applicationReportModel.setReportProperties(reportProperties);

            ReportService reportService = new ReportService(context);
            reportService.setUniqueFilename(applicationReportModel, "tattletale" + "_" + inputProjectModel.getName(), "html");
        }
    }
}
