package no.valg.valgnatt.backend.mandatberegning.beregning;

import static no.valg.valgnatt.backend.mandatberegning.beregning.Stemmetall.StemmetallberegningStatus.INGEN_STEMMER_FUNNET;
import static no.valg.valgnatt.backend.mandatberegning.beregning.Stemmetall.StemmetallberegningStatus.OPPTALTE_STEMMER;
import static no.valg.valgnatt.backend.mandatberegning.beregning.Stemmetall.StemmetallberegningStatus.PROGNOSTISERTE_STEMMER;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import no.valg.valgnatt.backend.mandatberegning.MandatBeregningArgumenter;
import no.valg.valgnatt.backend.mandatberegning.domene.Kvotient;
import no.valg.valgnatt.backend.mandatberegning.prognose.Prognose;
import no.valg.valgnatt.database.domene.OpptaltStatus;
import no.valg.valgnatt.database.geografi.Kommune;
import no.valg.valgnatt.database.geografi.Område;
import no.valg.valgnatt.database.geografi.PartiMandater;
import no.valg.valgnatt.database.geografi.PartiStemmer;

/**
 * Stemmetall-klassen beregner (naturlig nok) stemmetall i et område. Dette gjøres enten ved å telle opp stemmer (som man finner i data/geografi-modellen
 * i hver stemmekrets) eller ved å beregne en prognose. Etter man har telt opp eller beregnet stemmetallene, så legges disse tilbake igjen i datamodellen
 * men da under objektet "PartiMandater" som man finner på hvert valgdistrikt. Dette gjøres fordi neste skritt i prosessen er å beregne mandatfordeling, og
 * det er noe som gjøres pr valgdistrikt.
 */
public class Stemmetall {

	public static StemmetallberegningStatus beregn(MandatBeregningArgumenter mba, Område område, Set<String> betaPartier) {
		if (mba.forsøkPrognose()) {
			Set<String> partikoderSomStillerTilValg = område.getValgdistrikt().getPartiMandater().stream()
                    .filter(PartiMandater::isStillerliste)
                    .map(PartiMandater::getPartikode)
                    .collect(Collectors.toCollection(HashSet::new));
			Prognose po = new Prognose(område, betaPartier, partikoderSomStillerTilValg);
			Map<String, Kvotient> kvotientPrParti = po.beregn();
			if (!kvotientPrParti.isEmpty()) {
				po.oppdaterPartiMandaterMedPrognoseStemmetall(kvotientPrParti);
				return PROGNOSTISERTE_STEMMER;
			}
		}
		nullstillPrognoseData(område);
		return oppdaterPartiMandaterMedOpptalteStemmetall(område);
	}

	private static void nullstillPrognoseData(Område område) {
        for (PartiMandater pm : område.getValgdistrikt().getPartiMandater()) {
            pm.getPrognoseData().nullstill();
        }
    }// Oppdaterer PartiMandater med opptalte stemmer (hentet fra PartiStemmer)
	private static StemmetallberegningStatus oppdaterPartiMandaterMedOpptalteStemmetall(Område område) {
		if (område.sumStemmerTotal().getStemmer() == 0) {
			return INGEN_STEMMER_FUNNET;
		}

        for (PartiStemmer ps : område.aggregerStemmerPrParti(Område.Summering.PARTIER_SOM_STILLER)) {
            PartiMandater partiMandater = område.getPartiMandater(ps.getPartikode());
            partiMandater.getResultatData().setStemmer(ps.getStemmer());
            partiMandater.getResultatData().setPrognose("0");
            if (område.getClass() == Kommune.class && område.getLavesteOS() == OpptaltStatus.OPPGJØR) {
                partiMandater.getResultatData().setListestemmer(ps.getListestemmer());
            }
        }
        return OPPTALTE_STEMMER;
    }

    public enum StemmetallberegningStatus {
        INGEN_STEMMER_FUNNET, OPPTALTE_STEMMER, PROGNOSTISERTE_STEMMER
    }


}
