package no.valg.valgnatt.database;

import no.valg.valgnatt.database.domene.OpptaltStatus;
import no.valg.valgnatt.database.domene.ValgType;
import no.valg.valgnatt.database.geografi.Bydel;
import no.valg.valgnatt.database.geografi.Kommune;
import no.valg.valgnatt.database.geografi.Land;
import no.valg.valgnatt.database.geografi.Mandater;
import no.valg.valgnatt.database.geografi.PartiMandater;
import no.valg.valgnatt.database.geografi.PartiStemmer;
import no.valg.valgnatt.database.hibernate.Partiliste;
import no.valg.valgnatt.database.hibernate.PartilisteId;
import no.valg.valgnatt.database.hibernate.Stemmekrets;
import no.valg.valgnatt.database.hibernate.StemmekretsId;
import no.valg.valgnatt.database.hibernate.Valgdistrikt;
import no.valg.valgnatt.database.hibernate.ValgdistriktId;

import java.util.Map;
import java.util.TreeMap;

/**
 * Tanken er at denne datastrukturen skal lastes en gang, og at ingen tester skal endre innholdet i datastrukturen.
 * Datastrukturen skal være lik den geografiske datastruktren som bygges opp i GrunnlagsdataCache.initialiser(), men
 * svært begrenset i forhold til antall fylker, valgdistrikter, partier, etc. Dette danner dermed et grunnlag
 * for å kjøre diverse tester.
 *
 * Selve oppbyggingen i seg selv kunne også vært omfattet av enhetstester, men mye av dette er veldig enkelt,
 * og ikke så interessant å teste. Det meste kompliserte er add()-metodene som gjør litt logikk for å knytte
 * sammen alle de forskjellige objektene. Dette blir testet grundig gjennom enhetstestene, på den måten at man henter
 * ut data fra datastrukturen og sjekker at add()-metodene har lagt inn data på korrekt måte.
 */
public class GrunnlagsdataMockup {

    public static final String RÆLINGEN_KNR = "0228";
    public static final String GJERDRUM_KNR = "0234";
    public static final String HURDAL_KNR = "0239";
    public static final String SANDNES_KNR = "1102";
    public static final String TJØME_KNR = "0723";


    public Stemmekrets SANDNES_0999_STEMMEKRETS;
    public Bydel OSLO_1_BYDEL;
    public Valgdistrikt OSLO_VALGDISTRIKT;


    private Land land;
    private Map<PartilisteId, Partiliste> partilister = new TreeMap<>();


    public void initialiser(ValgType vt) {
        land = new Land(vt);
        initialiserPartilister(vt);
        opprettAkershus(vt);
        opprettOslo(vt);
        if (vt != ValgType.SA) {
            opprettRogaland(vt);
        }
        if (vt == ValgType.ST || vt == ValgType.FY) {
            land.setValgdistriktForUtjevningsmandaterOgLandsprognose(opprettValgdistrikt(vt, Konstanter.VALGDISTRIKT_NORGE, land.getFylker().size()));
        }
        if (vt == ValgType.ST) {
            opprettVestfold(vt);
        }
    }

    private void opprettAkershus(ValgType vt) {
        Valgdistrikt vdAkershus = opprettValgdistrikt(vt, "02", 20);

        // Rælingen kommune har mottatt foreløpige stemmer i 0000 og oppgjør i 0001
        Kommune rælingenKomm = new Kommune(RÆLINGEN_KNR, "Rælingen", true, true);
        Stemmekrets skRælingen0000 = opprettStemmekrets(vt, rælingenKomm.getNr(), Konstanter.DEFAULT_KRETS, 400, OpptaltStatus.ALLE_ENDELIG.getStatus(), null);
        land.add(skRælingen0000, null, rælingenKomm.getNavn(), vdAkershus, true, true);
        leggTilStemmer(skRælingen0000, OpptaltStatus.ALLE_FORELØPIG, "A", 60, 60, 60, 60, 60, 60);
        leggTilStemmer(skRælingen0000, OpptaltStatus.ALLE_FORELØPIG, "H", 60, 60, 60, 60, 60, 60);
        Stemmekrets skRælingen0001 = opprettStemmekrets(vt, rælingenKomm.getNr(), "0001", 600, OpptaltStatus.ALLE_ENDELIG.getStatus(), null);
        land.add(skRælingen0001, null, rælingenKomm.getNavn(), vdAkershus, true, true);
        leggTilStemmer(skRælingen0001, OpptaltStatus.OPPGJØR, "A", 90, 90, 90, 90, 90, 90);
        leggTilStemmer(skRælingen0001, OpptaltStatus.OPPGJØR, "H", 90, 90, 90, 90, 90, 90);


        // Gjerdrum har bare mottatt forhåndsstemmer
        Kommune gjerdrumKomm = new Kommune(GJERDRUM_KNR, "Gjerdrum", true, true);
        Stemmekrets skGjerdrum0000 = opprettStemmekrets(vt, gjerdrumKomm.getNr(), Konstanter.DEFAULT_KRETS, 300, OpptaltStatus.ALLE_ENDELIG.getStatus(), null);
        land.add(skGjerdrum0000, null, gjerdrumKomm.getNavn(), vdAkershus, true, true);
        leggTilStemmer(skGjerdrum0000, OpptaltStatus.BARE_FHS_ENDELIG, "A", 20, 0, 22, 44, 24, 48);
        leggTilStemmer(skGjerdrum0000, OpptaltStatus.BARE_FHS_ENDELIG, "H", 20, 0, 22, 44, 24, 48);
        leggTilStemmer(skGjerdrum0000, OpptaltStatus.BARE_FHS_ENDELIG, "BLANKE", 3, 0, 4, 8, 4, 8);
        leggTilStemmer(skGjerdrum0000, OpptaltStatus.BARE_FHS_ENDELIG, "PP", 16, 0, 12, 24, 12, 24);
        leggTilStemmer(skGjerdrum0000, OpptaltStatus.BARE_FHS_ENDELIG, "BP", 1, 0, 10, 10, 10, 10);
        leggTilForkastedeOgStemmegivinger(skGjerdrum0000, 6, 5, 200, 100, 2, 1);

        Kommune hurdalKomm = new Kommune(HURDAL_KNR, "Hurdal", true, true);
        Stemmekrets skHurdal0000 = opprettStemmekrets(vt, hurdalKomm.getNr(), Konstanter.DEFAULT_KRETS, 700, OpptaltStatus.ALLE_ENDELIG.getStatus(), null);
        land.add(skHurdal0000, null, hurdalKomm.getNavn(), vdAkershus, true, true);
        leggTilStemmer(skHurdal0000, "A", 70, 140, 77, 154, 77, 154);
        leggTilStemmer(skHurdal0000, "H", 70, 140, 77, 154, 77, 154);

        leggTilPartiMandater(vdAkershus, "A", 10, true);
        leggTilPartiMandater(vdAkershus, "H", 9, true);
        leggTilPartiMandater(vdAkershus, "PP", 1, true);
        leggTilPartiMandater(vdAkershus, "BP", 0, true);

    }

    private void opprettVestfold(ValgType vt) {
        Valgdistrikt vdVestfold = opprettValgdistrikt(vt, "07", 20);

        Kommune tjømeKomm = new Kommune(TJØME_KNR, "Tjøme", true, true);
        Stemmekrets skTjøme0000 = opprettStemmekrets(vt, tjømeKomm.getNr(), Konstanter.DEFAULT_KRETS, 700, OpptaltStatus.ALLE_ENDELIG.getStatus(), null);
        land.add(skTjøme0000, null, tjømeKomm.getNavn(), vdVestfold, true, true);
        leggTilStemmer(skTjøme0000, OpptaltStatus.OPPGJØR, "A", 70, 140, 77, 154, 77, 154);
        leggTilStemmer(skTjøme0000, OpptaltStatus.OPPGJØR, "H", 70, 140, 77, 154, 77, 154);
    }

    private void opprettRogaland(ValgType vt) {
        Valgdistrikt vdRogalandFylke = opprettValgdistrikt(vt, "11", 110);

        // Stavanger kommune: 3 stemmekretser, ingen partier/stemmer
        Kommune stavangerKomm = new Kommune(Konstanter.STAVANGER_KNR, "Stavanger", true, true);
        Stemmekrets skStavanger0000 = opprettStemmekrets(vt, stavangerKomm.getNr(), Konstanter.DEFAULT_KRETS, 1000, OpptaltStatus.ALLE_ENDELIG.getStatus(), null);
        Stemmekrets skStavanger0001 = opprettStemmekrets(vt, stavangerKomm.getNr(), "0001", 4000, OpptaltStatus.BARE_VTS_ENDELIG.getStatus(), null);
        Stemmekrets skStavanger00002 = opprettStemmekrets(vt, stavangerKomm.getNr(), "0004", 5000, OpptaltStatus.BARE_VTS_ENDELIG.getStatus(), null);

        land.add(skStavanger0000, null, stavangerKomm.getNavn(), vdRogalandFylke, true, true);
        land.add(skStavanger0001, null, stavangerKomm.getNavn(), vdRogalandFylke, true, true);
        land.add(skStavanger00002, null, stavangerKomm.getNavn(), vdRogalandFylke, true, true);

        // Sandnes kommune: 2 stemmekretser, ingen partier/stememer
        Kommune sandnesKomm = new Kommune(SANDNES_KNR, "Sandnes", true, true);
        Stemmekrets skSandnes0000 = opprettStemmekrets(vt, sandnesKomm.getNr(), Konstanter.DEFAULT_KRETS, 100, OpptaltStatus.ALLE_ENDELIG.getStatus(), null);
        SANDNES_0999_STEMMEKRETS = opprettStemmekrets(vt, sandnesKomm.getNr(), "0999", 900, OpptaltStatus.BARE_VTS_ENDELIG.getStatus(), null);

        land.add(skSandnes0000, null, sandnesKomm.getNavn(), vdRogalandFylke, true, true);
        land.add(SANDNES_0999_STEMMEKRETS, null, sandnesKomm.getNavn(), vdRogalandFylke, true, true);
    }

    private void opprettOslo(ValgType vt) {
        OSLO_VALGDISTRIKT = opprettValgdistrikt(vt, Konstanter.OSLO_FNR, 30);

        // Oslo kommune: 2 bydeler, 4 stemmekretser og 2 partier i alle kretser
        Kommune osloKomm = new Kommune(Konstanter.OSLO_KNR, "Oslo", true, true);
        Map<String, Bydel> bydelerIOslo = new TreeMap<>();
        bydelerIOslo.put("0", new Bydel("0", "Bydel 00", osloKomm.getNr(), true, true));
        OSLO_1_BYDEL = new Bydel("1", "Gamle Oslo", osloKomm.getNr(), true, true);
        bydelerIOslo.put("1", OSLO_1_BYDEL);
        Stemmekrets skOslo0000 = opprettStemmekrets(vt, osloKomm.getNr(), Konstanter.DEFAULT_KRETS, 0, OpptaltStatus.ALLE_ENDELIG.getStatus(), "0");
        Stemmekrets skOslo0101 = opprettStemmekrets(vt, osloKomm.getNr(), "0101", 1000, OpptaltStatus.BARE_VTS_ENDELIG.getStatus(), "1");
        Stemmekrets skOslo0102 = opprettStemmekrets(vt, osloKomm.getNr(), "0102", 1000, OpptaltStatus.BARE_VTS_ENDELIG.getStatus(), "1");
        Stemmekrets skOslo0103 = opprettStemmekrets(vt, osloKomm.getNr(), "0103", 1000, OpptaltStatus.BARE_VTS_ENDELIG.getStatus(), "1");


        land.add(skOslo0000, bydelerIOslo, osloKomm.getNavn(), OSLO_VALGDISTRIKT, true, true);
        land.add(skOslo0101, bydelerIOslo, osloKomm.getNavn(), OSLO_VALGDISTRIKT, true, true);
        land.add(skOslo0102, bydelerIOslo, osloKomm.getNavn(), OSLO_VALGDISTRIKT, true, true);
        land.add(skOslo0103, bydelerIOslo, osloKomm.getNavn(), OSLO_VALGDISTRIKT, true, true);

        leggTilStemmer(skOslo0000, "A", 0, 1, 0, 0, 0, 0); // Joda, det kan komme stemmer i kretsen, selv om det er 0 stemmeberettigede..
        leggTilStemmer(skOslo0000, "H", 0, 1, 0, 0, 0, 0); // Joda, det kan komme stemmer i kretsen, selv om det er 0 stemmeberettigede..
        leggTilStemmer(skOslo0101, "A", 100, 200, 100, 200, 100, 199);
        leggTilStemmer(skOslo0101, "H", 100, 200, 100, 200, 100, 199);
        leggTilStemmer(skOslo0102, "A", 100, 200, 100, 200, 100, 199);
        leggTilStemmer(skOslo0102, "H", 100, 200, 100, 200, 100, 199);
        leggTilStemmer(skOslo0103, "A", 100, 199, 100, 200, 100, 199);
        leggTilStemmer(skOslo0103, "H", 100, 199, 100, 200, 100, 199);
        leggTilPartiSomIkkeStiller(skOslo0000, OpptaltStatus.ALLE_ENDELIG, "BP", 0, 0, 0, 0);
        leggTilPartiSomIkkeStiller(skOslo0101, OpptaltStatus.ALLE_ENDELIG, "BP", 0, 0, 0, 2);
        leggTilPartiSomIkkeStiller(skOslo0102, OpptaltStatus.ALLE_ENDELIG, "BP", 0, 0, 0, 2);
        leggTilPartiSomIkkeStiller(skOslo0103, OpptaltStatus.ALLE_ENDELIG, "BP", 0, 0, 0, 2);

        leggTilPartiMandater(OSLO_VALGDISTRIKT, "A", 15, true);
        leggTilPartiMandater(OSLO_VALGDISTRIKT, "H", 15, true);
        leggTilPartiMandater(OSLO_VALGDISTRIKT, "BP", 0, false);
    }

    private void initialiserPartilister(ValgType vt) {
        Partiliste parti;
        parti = initialiserPartiliste(vt, "A", "Arbeiderpartiet", 10000);
        partilister.put(parti.getId(), parti);
        parti = initialiserPartiliste(vt, "H", "Høyre", 10010);
        partilister.put(parti.getId(), parti);
        parti = initialiserPartiliste(vt, "V", "Venstre", 10020);
        partilister.put(parti.getId(), parti);
        parti = initialiserPartiliste(vt, "PP", "Pensjonistpartiet", 20000);
        partilister.put(parti.getId(), parti);
        parti = initialiserPartiliste(vt, "BP", "Bondepartiet", 20010); // stiller bare i Akershus/Gjerdrum
        partilister.put(parti.getId(), parti);
        parti = initialiserPartiliste(vt, "BLANKE", " Blanke", 30000);
        partilister.put(parti.getId(), parti);
    }

    private Partiliste initialiserPartiliste(ValgType vt, String partikode, String partinavn, int visningssekvens) {
        Partiliste parti = new Partiliste();
        parti.setId(new PartilisteId(vt, partikode));
        parti.setPartikategori(visningssekvens / 10000); // partikategori 1-3, visningssekvenser settes fra 10000-39999
        parti.setPartinavn(partinavn);
        parti.setVisningssekvens(visningssekvens);
        return parti;
    }

    private Valgdistrikt opprettValgdistrikt(ValgType vt, String valgdistriktnr, int antallmandater) {
        Valgdistrikt vd = new Valgdistrikt();
        ValgdistriktId valgdistriktId = new ValgdistriktId();
        valgdistriktId.setNr(valgdistriktnr);
        valgdistriktId.setValgType(vt);
        vd.setId(valgdistriktId);
        vd.setAntallmandater(antallmandater);
        return vd;
    }

    private Stemmekrets opprettStemmekrets(ValgType vt, String kommnr, String kretsnr, int stemmeberettigede, int opptaltstatusMax, String bydelnr) {
        StemmekretsId stemmekretsId = new StemmekretsId();
        stemmekretsId.setValgtype(vt);
        stemmekretsId.setKommnr(kommnr);
        stemmekretsId.setKretsnr(kretsnr);
        Stemmekrets stemmekrets = new Stemmekrets();
        stemmekrets.setId(stemmekretsId);
        stemmekrets.setStemmeberettigede(stemmeberettigede);
        stemmekrets.setOpptaltStatusMax(opptaltstatusMax);
        stemmekrets.setBydelnr(bydelnr);
        return stemmekrets;
    }

    // Følgende verdier er default:
    //  OpptaltStatus = ALLE_ENDELIG(8)
    //  Stillerliste = true
    private void leggTilStemmer(Stemmekrets stemmekrets, String partikode, int fhs, int vts, int sfhs, int svts, int nsfhs, int nsvts) {
        leggTilStemmer(stemmekrets, OpptaltStatus.ALLE_ENDELIG, partikode, fhs, vts, sfhs, svts, nsfhs, nsvts, true);
    }

    // Følgende verdier er default:
    //  Stillerliste = true
    private void leggTilStemmer(Stemmekrets stemmekrets, OpptaltStatus os, String partikode, int fhs, int vts, int sfhs, int svts, int nsfhs, int nsvts) {
        leggTilStemmer(stemmekrets, os, partikode, fhs, vts, sfhs, svts, nsfhs, nsvts, true);
    }

    // Følgende verdier er default:
    //  Stillerliste = false
    private void leggTilPartiSomIkkeStiller(Stemmekrets stemmekrets, OpptaltStatus os, String partikode, int sfhs, int svts, int nsfhs, int nsvts) {
        leggTilStemmer(stemmekrets, os, partikode, 0, 0, sfhs, svts, nsfhs, nsvts, false);
    }

    private void leggTilStemmer(Stemmekrets stemmekrets, OpptaltStatus os, String partikode, int fhs, int vts, int sfhs, int svts, int nsfhs, int nsvts, boolean stillerliste) {
        stemmekrets.setOpptaltStatus(os.getStatus());
        Partiliste parti = partilister.get(new PartilisteId(stemmekrets.getValgType(), partikode));
        PartiStemmer partiStemmer = new PartiStemmer(parti.getId().getPartikode(), parti.getPartinavn(), "" + parti.getPartikategori(), "" + parti.getVisningssekvens(), stillerliste);
        partiStemmer.setFhs(fhs);
        partiStemmer.setVts(vts);
        partiStemmer.setSisteFhs(sfhs);
        partiStemmer.setSisteVts(svts);
        partiStemmer.setNestsisteFhs(nsfhs);
        partiStemmer.setNestsisteVts(nsvts);
        stemmekrets.setPartistemmer(partiStemmer);
    }

    private void leggTilForkastedeOgStemmegivinger(Stemmekrets sk, int stsFk, int stsFhsFk, int stgGd, int stgFhsGd, int stgFk, int stgFhsFk) {
        sk.setStsForkastede(stsFk);
        sk.setStsFhsForkastede(stsFhsFk);
        sk.setStgGodkjente(stgGd);
        sk.setStgFhsGodkjente(stgFhsGd);
        sk.setStgForkastede(stgFk);
        sk.setStgFhsForkastede(stgFhsFk);
    }

    private void leggTilPartiMandater(Valgdistrikt vd, String partikode, int antall, boolean stillerListe) {
        Partiliste parti = partilister.get(new PartilisteId(vd.getId().getValgtype(), partikode));
        PartiMandater pm = new PartiMandater(parti.getId().getPartikode(), parti.getPartinavn(), "" + parti.getPartikategori(), "" + parti.getVisningssekvens(), stillerListe);
        Mandater mandater = new Mandater(antall, 0, 0);
        pm.getResultatData().setDistriktsmandater(mandater);
        vd.addPartiMandater(pm);
    }

    public Land getLand() {
        return land;
    }

    // Noen enkle getMetoder for å kunne skrive testene litt penere:

    public Stemmekrets getOsloStemmekrets(String kretsnr) {
        return land.getKommune(Konstanter.OSLO_KNR).getStemmekrets(kretsnr);
    }

    public Kommune getOsloKommune() {
        return land.getKommune(Konstanter.OSLO_KNR);
    }

    public Kommune getStavangerKommune() {
        return land.getKommune(Konstanter.STAVANGER_KNR);
    }

    public Kommune getSandnesKommune() {
        return land.getKommune(SANDNES_KNR);
    }

    public Kommune getHurdalKommune() {
        return land.getKommune(HURDAL_KNR);
    }

    public Kommune getGjerdrumKommune() {
        return land.getKommune(GJERDRUM_KNR);
    }

    public Kommune getRælingenKommune() {
        return land.getKommune(RÆLINGEN_KNR);
    }
}
