package games.colonier.ld44.logic;

import games.colonier.ld44.chemistry.*;
import games.colonier.ld44.chemistry.rule.RuleGenerator;
import games.colonier.ld44.chemistry.rule.StructuralMolecularRule;
import games.colonier.ld44.simulation.Simulation;
import games.colonier.ld44.simulation.SimulationParameters;

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

public final class GameState {

    private static GameState INSTANCE = new GameState();
    private GameState () {};

    private Molecule currentMolecule;
    private Molecule diseaseMolecule;
    private TestCase best;
    private TestCase last;
    private TestCase lastSimulated;
    private int numTestCases;
    private boolean firstRun = true;
    private boolean firstRunStarted = false;

    public static GameState getInstance () { return INSTANCE; }

    //Todo: Fix this
    public void load () {
        currentMolecule = new Molecule(1);
        diseaseMolecule = DiseaseGenerator.generate (Difficulty.getChosen ().ruleComplexity);
        best = null;
        last = null;
        lastSimulated = null;
        numTestCases = 0;
    }

    public Molecule getCurrentMolecule() {
        return currentMolecule;
    }

    public void setCurrentMolecule(Molecule currentMolecule) {
        this.currentMolecule = currentMolecule;
    }

    public int getNumTestCases() {
        return numTestCases;
    }

    public void setNumTestCases(int numTestCases) {
        this.numTestCases = numTestCases;
    }

    public TestCase getBest () {
        return best;
    }

    public void setBest (TestCase best) {
        this.best = best;
    }

    public TestCase getLast () {
        return last;
    }

    public void setLast (TestCase last) {
        this.last = last;
    }

    public TestCase getLastSimulated () {
        return lastSimulated;
    }

    public void setLastSimulated (TestCase lastSimulated) {
        this.lastSimulated = lastSimulated;
    }

    public void executeTest () {

        Simulation simulation = new Simulation (
                diseaseMolecule,
                currentMolecule,
                // TODO: Diffuculty
                new SimulationParameters (
                        Difficulty.getChosen ().numTestParticipants,
                        Difficulty.getChosen ().nonexistentSymptomProbability,
                        Difficulty.getChosen ().undiscoveredSymptomProbability
                )
        );
        simulation.simulate ();

        lastSimulated = new TestCase ();

        lastSimulated.predictedSeverity = simulation.predictedSeverity;
        lastSimulated.canCureDisease = simulation.worksAgainstDisease;
        lastSimulated.simulationSymptoms = simulation.symptomResults;
        lastSimulated.reactedDiseaseMolecule = simulation.reactedDiseaseMolecule;

    }

    public void releaseLast () {

        if (lastSimulated != null) {

            last = lastSimulated;

            last.severity = lastSimulated.reactedDiseaseMolecule.getSymptoms ().stream ().mapToInt (s -> s.severity).sum ();
            last.symptoms = lastSimulated.reactedDiseaseMolecule.getSymptoms ();
            last.isSuccessful = last.canCureDisease && last.severity < 6;

            if (best == null || last.compareTo (best) < 0) best = last;

        }

    }

    public void analyze () {

        if (lastSimulated != null) {

            lastSimulated.analysisItems.clear ();

            for (Symptom s : Symptom.values ()) {

                List <StructuralMolecularRule.Edge> match = s.rule.firstMatch (lastSimulated.reactedDiseaseMolecule);
                if (match != null) {
                    List <Atom> atomsMatch = new ArrayList<> ();
                    match.forEach (
                            e -> {
                                if (!atomsMatch.contains (e.a)) atomsMatch.add (e.a);
                                if (!atomsMatch.contains (e.b)) atomsMatch.add (e.b);
                            }
                    );
                    AnalysisItem ai = new AnalysisItem (atomsMatch, s);
                    if (!lastSimulated.analysisItems.contains (ai)) {
                        lastSimulated.analysisItems.add (ai);
                    }
                }

            }

        }

    }

    public void setFirstRun(boolean b) {
        this.firstRun = b;
    }

    public boolean isFirstRun () {
        return this.firstRun;
    }

    public Molecule getDiseaseMolecule () {
        return diseaseMolecule;
    }

    public void setDiseaseMolecule (Molecule diseaseMolecule) {
        this.diseaseMolecule = diseaseMolecule;
    }

    public boolean isFirstRunStarted () {
        return firstRunStarted;
    }

    public void setFirstRunStarted (boolean firstRunStarted) {
        this.firstRunStarted = firstRunStarted;
    }

}
