package no.valg.valgnatt.database.geografi;

import lombok.Getter;
import lombok.Setter;
import no.valg.valgnatt.database.Konstanter;
import no.valg.valgnatt.database.domene.OpptaltStatus;
import no.valg.valgnatt.database.domene.ValgType;
import no.valg.valgnatt.database.hibernate.Stemmekrets;
import no.valg.valgnatt.database.hibernate.Valgdistrikt;

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

public class Kommune extends Område {

    @Getter @Setter private String nr;
    @Getter @Setter private String navn;
    @Getter private Map<String, Bydel> bydeler = new TreeMap<>();
    @Getter private Map<String, Stemmekrets> stemmekretser = new TreeMap<>();
    @Getter @Setter private Valgdistrikt valgdistrikt; // Har verdi i alle valg, men valgdistriktet er fylke/samevalgdistrikt i ST/FY/SA-valg
    @Getter @Setter private Fylke fylke; // settes bare i ST/FY/KO-valg
    @Getter @Setter private Samevalgdistrikt samevalgdistrikt; // settes bare i SA-valg
    @Getter @Setter private boolean sisteSammenlignbar;
    @Getter @Setter private boolean nestSisteSammenlignbar;

    public Kommune(String nr, String navn, boolean sisteSammenlignbar, boolean nestSisteSammenlignbar) {
        this.nr = nr;
        this.navn = navn;
        this.sisteSammenlignbar = sisteSammenlignbar;
        this.nestSisteSammenlignbar = nestSisteSammenlignbar;
    }

    @Override
    public Map<String, Kommune> getKommuner() {
        return null;
    }

    public Stemmekrets getStemmekrets(String kretsnr) {
        return stemmekretser.get(kretsnr);
    }

    public Bydel getBydel(String bydelsnr) {
        return bydeler.get(bydelsnr);
    }

    public void add(Stemmekrets stemmekrets, Map<String, Bydel> alleBydelerIOslo) {
        stemmekretser.put(stemmekrets.getId().getKretsnr(), stemmekrets);
        String bydelnr = stemmekrets.getBydelnr();
        if (bydelnr != null && alleBydelerIOslo != null && alleBydelerIOslo.get(bydelnr) != null) { // Settes bare for Oslo kommune i ST/FY/KO-valg
            Bydel bydel = alleBydelerIOslo.get(bydelnr);
            bydel.setLand(stemmekrets.getLand());
            bydel.add(stemmekrets);
            bydeler.put(bydelnr, alleBydelerIOslo.get(bydelnr));
        }
    }

    public boolean harKretsvisOpptelling() {
        return !(stemmekretser == null || stemmekretser.size() == 1);
    }

    /**
     * Grunnformel : Frammøte = (FHS+VTS)/SB
     * <p/>
     * Problem-1: Metoden skal levere fremmøteprosent i kommuner hvor ikke alle kretser er fullt opptalt, altså
     * skal den levere en prognose (av og til kalt FM-prognose)
     * Løsning-1: Prognosens hovedprinsipp er at man bare skal bruke frammøteprosenten i kretser med både VTS og FHS opptalt.
     * <p/>
     * Problem-2: Opptelling av FHS skjer som bare i krets 0000. Opptelling av VTS skjer i alle kretser.
     * Løsning-2: Vi kretsfordeler FHS etter kretsens størrelse (målt i stemmeberettigede)
     * <p/>
     * Problem-3: Dersom en kommune har flere kretser, så er krets 0000 alltid spesiell (alle FHS, veldig få SB og VTS). Dette betyr
     * at dersom vi beregner krets 0000's frammøte så vil det som regel kunne bli svært lavt.
     * Løsning-3: Vi ignorerer kretsen i beregningen av fremmøte, inntil minst en annen krets er fullt opptalt. Dette skal gi en
     * pen "innfasing" av krets 0000 i prognoseberegningen.
     */
    @Override
    public Float beregnFrammoteProsentIaar() {
        int antallSB = 0;
        float antallStemmer = 0f;
        Stemmekrets krets0000 = getStemmekrets(Konstanter.DEFAULT_KRETS);
        if (krets0000.getOS() == OpptaltStatus.INGEN) {
            return null;
        }
        boolean vanligKretsFulltOpptalt = false;
        int tekniskKretsFhs = krets0000.sumStemmerTotal().getFhs();
        for (Stemmekrets stemmekrets : getStemmekretser().values()) {
            if (stemmekrets.getOpptaltStatus() <= OpptaltStatus.BARE_FHS_ENDELIG.getStatus()) { // Har bare fhs - ingen grunn til å hente ut vts
                continue;
            }
            antallSB += stemmekrets.getStemmeberettigede();
            if (stemmekrets == krets0000) {
                if (getStemmekretser().size() == 1) {
                    vanligKretsFulltOpptalt = true;
                }
                antallStemmer += stemmekrets.sumStemmerTotal().getVts();
            } else { // Vi må hente ut alle stemmer fra øvrige kretser, i tilfelle FHS *ikke* er kretsfordelt - slik det er for byene i 2011
                vanligKretsFulltOpptalt = true;
                antallStemmer += stemmekrets.sumStemmerTotal().getStemmer();
            }
            antallStemmer += stemmekrets.andelAvKommune() * tekniskKretsFhs;
        }
        return (antallSB > 0 && vanligKretsFulltOpptalt) ? antallStemmer / antallSB : null;
    }

    @Override
    public Område getOverordnetOmråde() {
        if (samevalgdistrikt != null) {
            return samevalgdistrikt;
        } else {
            return fylke;
        }
    }

    @Override
    public Område[] getUnderordnedeRapportOmråder() {
        if (nr.equals(Konstanter.OSLO_KNR)) {
            if (getValgType() == ValgType.FY) {
                return new Område[]{};
            } else {
                return bydeler.values().toArray(new Område[]{});
            }
        } else {
            if (harKretsvisOpptelling()) {
                return stemmekretser.values().toArray(new Område[]{});
            } else {
                return new Område[]{};
            }
        }
    }

}
