package cz.muni.disa.adamiss.se;

import com.sanityinc.jargs.CmdLineParser;
import com.sanityinc.jargs.CmdLineParser.Option;
import cz.muni.disa.adamiss.core.objects.TransactionFile;
import cz.muni.disa.adamiss.se.common.Dataset;
import cz.muni.disa.adamiss.se.generators.SubsetGenerator;
import cz.muni.disa.adamiss.se.stats.collectors.DatabaseStatCollector;
import cz.muni.disa.adamiss.se.stats.objects.DBReport;
import cz.muni.disa.adamiss.se.stats.objects.ExperimentReport;
import cz.muni.disa.adamiss.se.stats.runners.ExperimentRunner;
import cz.muni.disa.adamiss.se.structures.DatasetContainer;
import javafx.util.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

import static cz.muni.disa.adamiss.se.loader.DatasetLoader.loadDataset;

public class Main {
    public static Logger logger = LogManager.getLogger(Main.class);

    private static void printUsage() {
        System.err.println(
                """
                        Usage: [-r,--runType {
                                        \"all\",
                                        \"single\",
                                        \"subset\"}] 
                               [-d,--datasets{
                                        \"BIBLE\",
                                        \"COVID\",
                                        \"GENERATED1\",
                                        \"GENERATED2\",
                                        \"GENERATED3\",
                                        \"KDDCUP\",
                                        \"LEVIATHAN\",
                                        \"SIGN\"}]
                               [-t,--thresholds] 
                               [-a,--algos{
                                        \"GSP\",
                                        \"PREFIX_SPAN\",
                                        \"SPAM\"
                               }]""");
    }

    public static void main(String[] args) {


        CmdLineParser parser = new CmdLineParser();
        Option<String> runTypeOption = parser.addStringOption('r',"runType");
        Option<String> datasetNameOption = parser.addStringOption('n', "datasetName");
        Option<Integer> amountOption = parser.addIntegerOption('a', "amount");
        Option<Long> seedOption = parser.addLongOption("seed");
        Option<Boolean> sizeOption = parser.addBooleanOption('s',"size");
        Option<Boolean> uniqueOption = parser.addBooleanOption('u',"unique");
        Option<Boolean> lengthOption = parser.addBooleanOption('l',"length");
        Option<Boolean> combinedOption = parser.addBooleanOption('c',"combined");


        try {
            parser.parse(args);
        } catch (CmdLineParser.OptionException e) {
            e.printStackTrace();
        }

        var size = parser.getOptionValue(sizeOption, false);
        var unique = parser.getOptionValue(uniqueOption, false);
        var length = parser.getOptionValue(lengthOption, false);
        var combined = parser.getOptionValue(combinedOption, false);

        logger.debug("Loading datasets.");
        List<Pair<String, TransactionFile>> datasets = new ArrayList<>();

        String runType = parser.getOptionValue(runTypeOption, "all");
        switch (runType) {
            case "all" -> {
                datasets = getDatasets();
            }
            case "single" -> {
                var datasetName = parser.getOptionValue(datasetNameOption);
                datasets = getDatasets(datasetName);
            }
            case "subset" -> {
                var datasetNames = parser.getOptionValue(datasetNameOption);
                datasets = getDatasets(datasetNames);
                Long seed = parser.getOptionValue(seedOption);
                Random random = seed == null? new Random(): new Random(seed);
                int amount = parser.getOptionValue(amountOption);
                Iterator<Pair<String, TransactionFile>> iterator = new ArrayList<>(datasets).iterator();
                while(iterator.hasNext()){
                    var dataset = iterator.next();
                    if(size) {
                        for (int i = 0; i < amount; i++)
                            datasets.add(SubsetGenerator.generateSizeSubset(dataset, random, i));
                    }
                    if (unique){
                        for (int i = 0; i < amount; i++)
                            datasets.add(SubsetGenerator.generateUniqueSubset(dataset, random, i));
                    }
                    if(length){
                        for (int i = 0; i < amount; i++)
                            datasets.add(SubsetGenerator.generateLengthSubset(dataset, random, i));
                    }
                    if(combined){
                        for (int i = 0; i < amount; i++)
                            datasets.add(SubsetGenerator.generateCombinedSubset(dataset, random, i));
                    }
                }
            }
            default -> {
                System.err.println("Unrecognised option for runType: " + runType);
                printUsage();
                System.exit(2);
            }
        }

        logger.debug("Analysing datasets.");
        List<DatasetContainer> containers = new ArrayList<>();
        List<DBReport> reports = new ArrayList<>();
        for (var pair : datasets) {
            logger.debug("Analysing: " + pair.getKey());
            DBReport report = getDatabaseStats(pair.getKey(), pair.getValue());
            reports.add(report);

            containers.add(new DatasetContainer(pair.getKey(), pair.getValue(), report));
        }

        SimpleDateFormat formatter = new SimpleDateFormat();
        String outputFileDate = formatter.format(new Date());

        logger.debug("Saving dataset stats.");
        String dbReportCSV = DBReport.toCSVWithHeader(reports);
        saveAsCSV(dbReportCSV.toString(), "DatasetStats" + outputFileDate);

        logger.debug("Starting experiments.");
        List<ExperimentReport> eReports = ExperimentRunner.runAll(containers);

        logger.debug("Saving experiments data.");
        ExperimentReport.generateGNUPlotFiles(eReports);


    }

    public static List<Pair<String, TransactionFile>> getDatasets() {
        return getDatasets(
                Arrays.stream(Dataset.values())
                        .map(Dataset::name)
                        .collect(Collectors.joining(","))
        );
    }

    public static List<Pair<String, TransactionFile>> getDatasets(String names) {
        List<Pair<String, TransactionFile>> result = new ArrayList<>();
        for (Dataset dataset : Dataset.values()) {
            if (Arrays.asList(names.split(",")).contains(dataset.name())) {
                result.add(new Pair<>(dataset.name(), loadDataset(dataset.path())));
            }
        }
        return result;
    }

    public static DBReport getDatabaseStats(String dataset, TransactionFile file) {
        DatabaseStatCollector collector = new DatabaseStatCollector();
        return collector.collect(dataset, file);
    }


    public static void saveAsCSV(String data, String name) {
        try {
            FileWriter report = new FileWriter(name + ".csv");
            report.write(data);
            report.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
