package nogui;

import alg.XML_processing;
import data.*;
import gui.GUIController;
import gui.I18N;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.regex.Pattern;

import static nogui.Utils.*;

public class Characters {
    public final static Logger logger = LogManager.getLogger(GUIController.class);

    public static void characters(JSONObject settings, Corpus corpus) {
        Filter filter = new Filter();
        // fixed values
        filter.setNgramValue(0);
        filter.setAl(AnalysisLevel.STRING_LEVEL);
        filter.setSkipValue(0);
        filter.setIsCvv(false);
        filter.setMultipleKeys(new ArrayList<>());

        // tab specific values
        filter.setStringLength(Math.toIntExact((Long) settings.get("stringLength")));
        String calculateForString = (String) settings.get("calculateFor");
        CalculateFor calculateFor = CalculateFor.factory(I18N.get(calculateForString));
        filter.setCalculateFor(calculateFor);
        filter.setDisplayTaxonomy((boolean) settings.get("displayTaxonomy"));

        // right part
        ArrayList<Pattern> msd = getMsd((String) settings.get("msd"));
        filter.setMsd(msd);
        filter.setTaxonomySetOperation(I18N.get((String) settings.get("taxonomySetOperation")));
        ArrayList<Taxonomy> taxonomy = getTaxonomy((JSONArray) settings.get("taxonomy"), corpus);
        filter.setTaxonomy(taxonomy);
        filter.setMinimalOccurrences(Math.toIntExact((Long) settings.get("minimalOccurrences")));
        filter.setMinimalTaxonomy(Math.toIntExact((Long) settings.get("minimalTaxonomy")));

        String message = Validation.validateForStringLevel(filter);
        if (message == null) {
            // no errors
            logger.info("Executing: ", filter.toString());
            StatisticsNew statistic = new StatisticsNew(corpus, filter, false);
            execute(statistic);
            try {
                boolean successullySaved = statistic.saveResultToDisk();
                if (successullySaved) {
                    logger.info(I18N.get("message.NOTIFICATION_ANALYSIS_COMPLETED"));
                } else {
                    logger.info(I18N.get("message.NOTIFICATION_ANALYSIS_COMPLETED_NO_RESULTS"));
                }
            } catch (UnsupportedEncodingException e1) {
                logger.error(I18N.get("message.ERROR_WHILE_SAVING_RESULTS_TO_CSV"));
            }
        } else {
            logger.error(message);
        }


    }

    private static void execute(StatisticsNew statistic) {
        logger.info("Started execution: ", statistic.getFilter());

        Collection<File> corpusFiles = statistic.getCorpus().getDetectedCorpusFiles();

        final boolean multipleFiles = CorpusType.multipleFilesCorpuses().contains(statistic.getCorpus().getCorpusType());
        int i = 0;
        Date startTime = new Date();
        Date previousTime = new Date();
        int remainingSeconds = -1;
        for (File f : corpusFiles) {
            final int iFinal = i;
            XML_processing xml_processing = new XML_processing();
            i++;
            if (multipleFiles) {
                if ((new Date()).getTime() - previousTime.getTime() > 500 || remainingSeconds == -1){
                    remainingSeconds = (int) (((new Date()).getTime() - startTime.getTime()) * (1.0/i) * (corpusFiles.size() - i) / 1000);
                    previousTime = new Date();
                }
                updateProgress(i, corpusFiles.size(), String.format(I18N.get("message.ONGOING_NOTIFICATION_ANALYZING_FILE_X_OF_Y"), i, corpusFiles.size(), f.getName(), remainingSeconds));
            } else {

                xml_processing.progressBarListener = new InvalidationListener() {
                    int remainingSeconds = -1;
                    Date previousTime = new Date();
                    @Override
                    public void invalidated(Observable observable) {
                        if ((new Date()).getTime() - previousTime.getTime() > 500 || remainingSeconds == -1){
                            remainingSeconds = (int) (((new Date()).getTime() - xml_processing.startTime.getTime()) *
                                    (1.0/(iFinal * 100 + ((ReadOnlyDoubleWrapper) observable).get() + 1)) *
                                    ((corpusFiles.size() - iFinal - 1) * 100 + 100 - ((ReadOnlyDoubleWrapper) observable).get()) / 1000);
                            previousTime = new Date();
                        }
                        updateProgress((iFinal * 100) + ((ReadOnlyDoubleWrapper) observable).get() + 1, corpusFiles.size() * 100, String.format(I18N.get("message.ONGOING_NOTIFICATION_ANALYZING_FILE_X_OF_Y"), iFinal + 1, corpusFiles.size(), f.getName(), remainingSeconds));
                    }
                };

                xml_processing.progressProperty().addListener(xml_processing.progressBarListener);
            }
            xml_processing.readXML(f.toString(), statistic);
        }

    }
}
