package no.valg.valgnatt.database.forberedvalg;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import no.valg.valgnatt.common.ValgnattException;
import no.valg.valgnatt.database.EvaAdminSkjema;
import no.valg.valgnatt.database.Konstanter;
import no.valg.valgnatt.database.domene.OpptaltStatus;
import no.valg.valgnatt.database.domene.SkjemaType;
import no.valg.valgnatt.database.domene.ValgType;
import no.valg.valgnatt.database.forberedvalg.bean.BeanBydel;
import no.valg.valgnatt.database.forberedvalg.bean.BeanHistorikkMandater;
import no.valg.valgnatt.database.forberedvalg.bean.BeanHistorikkMandaterMod;
import no.valg.valgnatt.database.forberedvalg.bean.BeanHistorikkStemmeberettigedeMod;
import no.valg.valgnatt.database.forberedvalg.bean.BeanHistorikkStemmetall;
import no.valg.valgnatt.database.forberedvalg.bean.BeanHistorikkStemmetallMod;
import no.valg.valgnatt.database.forberedvalg.bean.BeanKandidater;
import no.valg.valgnatt.database.forberedvalg.bean.BeanKommune;
import no.valg.valgnatt.database.forberedvalg.bean.BeanMandatberegning;
import no.valg.valgnatt.database.forberedvalg.bean.BeanPartilister;
import no.valg.valgnatt.database.forberedvalg.bean.BeanStemmekrets;
import no.valg.valgnatt.database.forberedvalg.bean.BeanValgdistrikter;
import no.valg.valgnatt.database.forberedvalg.csv.CsvBydelerOutput;
import no.valg.valgnatt.database.forberedvalg.csv.CsvHistorikkMandater;
import no.valg.valgnatt.database.forberedvalg.csv.CsvHistorikkMandaterMod;
import no.valg.valgnatt.database.forberedvalg.csv.CsvHistorikkStemmeberettigedeMod;
import no.valg.valgnatt.database.forberedvalg.csv.CsvHistorikkStemmetall;
import no.valg.valgnatt.database.forberedvalg.csv.CsvHistorikkStemmetallMod;
import no.valg.valgnatt.database.forberedvalg.csv.CsvKandidater;
import no.valg.valgnatt.database.forberedvalg.csv.CsvKommunerInput;
import no.valg.valgnatt.database.forberedvalg.csv.CsvKommunerOutput;
import no.valg.valgnatt.database.forberedvalg.csv.CsvMandatberegning;
import no.valg.valgnatt.database.forberedvalg.csv.CsvPartilister;
import no.valg.valgnatt.database.forberedvalg.csv.CsvStemmekretser;
import no.valg.valgnatt.database.forberedvalg.csv.CsvValgdistrikter;
import no.valg.valgnatt.database.forberedvalg.geografi.AlleValg;
import no.valg.valgnatt.database.forberedvalg.geografi.Krets;
import no.valg.valgnatt.database.forberedvalg.geografi.Parti;
import no.valg.valgnatt.database.forberedvalg.geografi.Valg;
import no.valg.valgnatt.database.forberedvalg.konvertering.KonverteringStrategi;
import no.valg.valgnatt.database.forberedvalg.konvertering.Konverteringer;
import no.valg.valgnatt.database.forberedvalg.util.JsonObjektSti;
import no.valg.valgnatt.database.forberedvalg.util.SkjemaFunksjoner;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;

import static no.valg.valgnatt.database.domene.ValgType.SA;

/**
 * ForberedValg kjøres for å bygge de nødvendige grunnlagsdata for et valg som skal kjøres. Det blir bygget 12 CSV-filer som er grunnlagsdata.
 * <p>
 * Følgende må gjøres *før* man kan kjøre ForberedValg:
 * <p>
 * 1. Bestem valgår som du skal forberede. Sette dette årstallet i konstanten VALGÅR.
 * 2. Bestem prefiks for valgår. Sette dette årstallet i konstanten DIR. Det gir en enkel måte å lese test-eksportskjemaer fra en annen katalog enn standard, og skrive til en annen grunnlagskatalog.
 * 3. De kataloger som indirekte bestemmes av DIR_VALGÅR må eksistere/opprettes.
 * 4. GSK- og GPVK-filer må eksistere for de valgtyper som du skal forberede. Disse skal ligge i EVA_EKSPORT_DIR. Disse filene lages enten gjennom en ekspor fra EVA Admin (for nye valg), eller ved å kjøre en eksport fra Valngatt (for gamle valg).
 * 5. Grunnlagsdatafiler fra forrige valg må finnes i SISTEVALG_GRUNNLAG_DIR.
 * 6. Historikkfiler fra forrige valg må finnes i SISTEVALG_HISTORIKK_DIR. Disse lages av ForberedHistorikk..
 */
public class ForberedValg {

    private static final String DIR = "test"; 
    private static final int VALGÅR = 2019;

    // Brukes for å lese fra json
    private static final String FYLKER = "fylker";
    private static final String KOMMUNER = "kommuner";
    private static final String KRETSER = "kretser";
    private static final String KOMMUNENUMMER = "kommunenummer";
    private static final String VALGDISTRIKT = "valgdistrikt";
    private static final String KOMMUNE = "kommune";
    private static final String VALGDISTRIKTER = "valgdistrikter";
    private static final String PARTIER = "partier";
    private static final String KANDIDATER = "kandidater";
    private static final String PARTIKODE = "partikode";
    private static final String PLASSNUMMER = "plassnummer";
    private static final String FØDSELSDATO = "fødselsdato";
    private static final String KJØNN = "kjønn";
    private static final String BOSTED = "bosted";
    private static final String NAVN = "navn";
    private static final String ANTALL_MANDATER = "antall_mandater";
    private static final String ANTALL_UTJEVNINGSMANDATER = "antall_utjevningsmandater";
    private static final String PARTINAVN = "partinavn";
    private static final String PARTIKATEGORI = "partikategori";

    private static String evaEksportDir = "db/grunnlagsdataskjema/" + DIR + "_" + VALGÅR + "/";
    private static String grunnlagForNyttValgDir = "db/initialdata/" + DIR + "_" + VALGÅR + "/";
    private static String sistevalgGrunnlagDir = "db/initialdata/full_" + (VALGÅR - 2) + "/";
    private static String sistevalgHistorikkDir = "db/historikk/" + (VALGÅR - 2) + "/";

    private static final int ÅR_2011 = 2011;
    private static final int ÅR_2013 = 2013;
    private static final int ÅR_2017 = 2017;
    private static final int BYDELNR_STRING_LENGDE = 4;
    private static final int KRETS_INDEKS = 2;
    private static final int KOMMUNE_INDEKS = 1;
    private static final int PARTI_INDEKS = 1;
    private static final int KANDIDAT_INDEKS = 2;
    private static final int VALGDISTRIKT_INDEKS = 0;
    private static final int INDEKS_DEFAULT = 10000;
    private static final int INDEKS_DEFAULT_INKREMET = 10;

    private static final Logger LOG = LogManager.getLogger(ForberedValg.class);

    // Disse partiene skal vises i en bestemt rekkefølge som IKKE er avgjort av alfabetet og partikategori
    private static Map<String, Integer> visningssekvensMap = new TreeMap<>();

    private static final int INDEKS_A = 10000;
    private static final int INDEKS_SV = 10020;
    private static final int INDEKS_RØDT = 10040;
    private static final int INDEKS_SP = 10060;
    private static final int INDEKS_KRF = 10080;
    private static final int INDEKS_MDG = 10099;
    private static final int INDEKS_V = 10100;
    private static final int INDEKS_H = 10120;
    private static final int INDEKS_FRP = 10140;

    static {
        visningssekvensMap.put("A", INDEKS_A);
        visningssekvensMap.put("SV", INDEKS_SV);
        visningssekvensMap.put("RØDT", INDEKS_RØDT);
        visningssekvensMap.put("SP", INDEKS_SP);
        visningssekvensMap.put("KRF", INDEKS_KRF);
        visningssekvensMap.put("MDG", INDEKS_MDG); // MDG ble introdusert i 2015 - her blir gjort en liten vri i koden nedenfor
        visningssekvensMap.put("V", INDEKS_V);
        visningssekvensMap.put("H", INDEKS_H);
        visningssekvensMap.put("FRP", INDEKS_FRP);
    }

    private ForberedValg() {
        // Hide default constructor
    }

    public static void main(String[] args) throws IOException {
        konfigurerKataloger(args);
        LOG.info("Kjører forbered valg med runtimekonfigurasjon: '" + isAutooppsett(args) + "'");
        LOG.info("Benytter '" + evaEksportDir + "' for lesing av grunnlagsdata");
        LOG.info("Benytter '" + grunnlagForNyttValgDir + "' for skriving av initialdata");

        try {

            // Data som bygges fra scratch
            Map<String, BeanPartilister> partilisterMap = new TreeMap<>(); // partilister.csv
            Map<String, BeanBydel> bydelerMap = new TreeMap<>(); // bydeler.csv
            Map<String, BeanHistorikkStemmeberettigedeMod> histStemmeberettigedeModMap = new TreeMap<>(); // historikk_stemmeberettigede_mod.csv
            Map<String, BeanHistorikkStemmetallMod> histStemmetallModMap = new TreeMap<>(); // historikk_stemmetall_mod.csv
            Map<String, BeanHistorikkMandaterMod> histMandaterModMap = new TreeMap<>(); // historikk_mandater_mod.csv
            Map<String, BeanKandidater> kandidaterMap = new TreeMap<>(); // kandidater.csv
            Map<String, BeanMandatberegning> mandaterMap = new TreeMap<>(); // mandatberegning.csv

            // Data som bygges opp fra gamle valg
            // Leser inn data - ingen logikk her, bare ren innlesing inn i map's
            Map<String, BeanKommune> kommuneMap = LoadSave.load(sistevalgGrunnlagDir, new CsvKommunerInput(), BeanKommune.class); // kommuner.csv

            CsvHistorikkStemmetall csvHistorikkStemmetall = new CsvHistorikkStemmetall();
            Map<String, BeanHistorikkStemmetall> historikkStemmetallMap = LoadSave.load(sistevalgGrunnlagDir, csvHistorikkStemmetall, BeanHistorikkStemmetall.class); // historikk_stemmetall.csv
            historikkStemmetallMap.putAll(LoadSave.load(sistevalgHistorikkDir, csvHistorikkStemmetall, BeanHistorikkStemmetall.class)); // legger til historikk fra siste valg
            if (VALGÅR == ÅR_2013) { // se readme.txt i SISTEVALG_HISTORIKK_DIR
                historikkStemmetallMap.putAll(LoadSave.load(sistevalgHistorikkDir, new CsvHistorikkStemmetall("historikk_stemmetall_BLANKE.csv"), BeanHistorikkStemmetall.class));
            }

            CsvHistorikkMandater csvHistorikkMandater = new CsvHistorikkMandater();
            Map<String, BeanHistorikkMandater> historikkMandaterMap = LoadSave.load(sistevalgGrunnlagDir, csvHistorikkMandater, BeanHistorikkMandater.class); // historikk_mandater.csv
            historikkMandaterMap.putAll(LoadSave.load(sistevalgHistorikkDir, csvHistorikkMandater, BeanHistorikkMandater.class)); // legger til historikk fra siste valg

            CsvStemmekretser csvStemmekretser = new CsvStemmekretser();
            Map<String, BeanStemmekrets> stemmekretserValgMap = LoadSave.load(sistevalgGrunnlagDir, csvStemmekretser, BeanStemmekrets.class); // stemmekretser.csv

            CsvValgdistrikter csvValgdistrikter = new CsvValgdistrikter();
            Map<String, BeanValgdistrikter> valgdistrikterMap = LoadSave.load(sistevalgGrunnlagDir, csvValgdistrikter, BeanValgdistrikter.class); // valgdistrikter.csv

            prosesserGskOgGvpk(partilisterMap, bydelerMap, histStemmeberettigedeModMap, histStemmetallModMap, histMandaterModMap, kandidaterMap, mandaterMap, kommuneMap,
                    historikkStemmetallMap, historikkMandaterMap, stemmekretserValgMap, valgdistrikterMap);

            // Skriv ut de nye CSV'ene - ingen logikk foregår her, bare dumper innholdet fra map'et til csv'en
            LoadSave.save(grunnlagForNyttValgDir, new CsvPartilister(), partilisterMap);
            LoadSave.save(grunnlagForNyttValgDir, new CsvBydelerOutput(), bydelerMap);
            LoadSave.save(grunnlagForNyttValgDir, new CsvKommunerOutput(), kommuneMap);
            LoadSave.save(grunnlagForNyttValgDir, csvHistorikkMandater, historikkMandaterMap);
            LoadSave.save(grunnlagForNyttValgDir, csvHistorikkStemmetall, historikkStemmetallMap);
            LoadSave.save(grunnlagForNyttValgDir, new CsvHistorikkStemmeberettigedeMod(), histStemmeberettigedeModMap);
            LoadSave.save(grunnlagForNyttValgDir, new CsvHistorikkStemmetallMod(), histStemmetallModMap);
            LoadSave.save(grunnlagForNyttValgDir, new CsvKandidater(), kandidaterMap);
            LoadSave.save(grunnlagForNyttValgDir, new CsvMandatberegning(), mandaterMap);
            LoadSave.save(grunnlagForNyttValgDir, new CsvHistorikkMandaterMod(), histMandaterModMap);
            LoadSave.save(grunnlagForNyttValgDir, csvStemmekretser, stemmekretserValgMap);
            LoadSave.save(grunnlagForNyttValgDir, csvValgdistrikter, valgdistrikterMap);

        } catch (Exception e) {
            LOG.error("Feil oppstod", e);
            throw e;
        }
    }

    private static synchronized void konfigurerKataloger(String[] args) {
        // Hvis ForberedValg kalles fra klienten 
        if (isAutooppsett(args)) {
            evaEksportDir = Konstanter.AUTOOPPSETT_GRUNNLAGSDATAKATALOG.toString() + File.separator;
            grunnlagForNyttValgDir = Konstanter.AUTOOPPSETT_INITIALDATAKATALOG.toString() + File.separator;
        }
        // Hvis ForberedValg kalles fra en test
        else if (isTest(args)) {
            String resourcePath = "src/test/resources/";
            evaEksportDir = resourcePath + "db/grunnlagsdataskjema/full_2017/";
            grunnlagForNyttValgDir = resourcePath + "db/initialdata/full_2017/";
            sistevalgGrunnlagDir = resourcePath + "db/initialdata/full_2015/";
            sistevalgHistorikkDir = resourcePath + "db/historikk/2015/";
        }
        // Standard kjøring av ForberedValg
        else {
            String resourcePath = "valg_database/src/main/resources/";
            evaEksportDir = resourcePath + evaEksportDir;
            grunnlagForNyttValgDir = resourcePath + grunnlagForNyttValgDir;
            sistevalgGrunnlagDir = resourcePath + sistevalgGrunnlagDir;
            sistevalgHistorikkDir = resourcePath + sistevalgHistorikkDir;
        }
    }

    private static boolean isAutooppsett(String[] args) {
        return args != null && args.length > 0 && "runtimeconfig".equals(args[0]);
    }

    private static boolean isTest(String[] args) {
        return args != null && args.length > 0 && "test".equals(args[0]);
    }

    /**
     * Her gjøres utlesing av gsk- og gvpk-skjema. Data her går inn i forskjelige datastrukturer.
     * I noen tilfeller trenger vi å mellomlagre data i en AlleValg-klasse, for å gjøre litt mer tricky transformasjoner.
     * Typisk handler dette om hvordan vi skal håndtere utgåtte kretser og partier og hvordan enklest tilrettelegge for å skrive ut historiske data.
     */
    private static void prosesserGskOgGvpk(Map<String, BeanPartilister> partilisterMap, 
                                           Map<String, BeanBydel> bydelerMap, 
                                           Map<String, BeanHistorikkStemmeberettigedeMod> histStemmeberettigedeModMap,
                                           Map<String, BeanHistorikkStemmetallMod> histStemmetallModMap, 
                                           Map<String, BeanHistorikkMandaterMod> histMandaterModMap, 
                                           Map<String, BeanKandidater> kandidaterMap,
                                           Map<String, BeanMandatberegning> mandaterMap, 
                                           Map<String, BeanKommune> kommuneMap, 
                                           Map<String, BeanHistorikkStemmetall> historikkStemmetallMap,
                                           Map<String, BeanHistorikkMandater> historikkMandaterMap, 
                                           Map<String, BeanStemmekrets> stemmekretserValgMap, 
                                           Map<String, BeanValgdistrikter> valgdistrikterMap) throws IOException {
        for (ValgType valgType : ValgType.values()) {
            AlleValg alleValg = new AlleValg();
            File gskSkjema = finnSkjema(evaEksportDir, valgType, SkjemaType.GRUNNLAG_STEMMEKRETSER.kode);
            File gvpkSkjema = finnSkjema(evaEksportDir, valgType, SkjemaType.GRUNNLAG_VALGDISTRIKTER_PARTIER_KANDIDATER.kode);
            if (gskSkjema != null && gvpkSkjema != null) {
                prosesserGsk(stemmekretserValgMap, kommuneMap, bydelerMap, histStemmeberettigedeModMap, alleValg, gskSkjema);
                prosesserGvpk(partilisterMap, histStemmetallModMap, historikkStemmetallMap, histMandaterModMap, historikkMandaterMap, valgdistrikterMap, kandidaterMap, mandaterMap, alleValg, gvpkSkjema);
            } else if (gskSkjema == null && gvpkSkjema != null) {
                throw new ValgnattException("Mangler GSK-skjema for " + valgType);
            } else if (gskSkjema != null) {
                throw new ValgnattException("Mangler GVPK-skjema for " + valgType);
            }
        }
    }

    private static File finnSkjema(String eksportKatalog, ValgType valgType, String skjemaType) {
        File[] files = new File(eksportKatalog).listFiles((dir, name) -> name.contains(skjemaType + "-" + valgType.name()));
        if (files != null && files.length > 0) {
            return files[0];
        } else {
            LOG.warn("Fant ingen '" + skjemaType + "-" + valgType.name() + "'-filer i " + eksportKatalog);
        }
        return null;
    }

    private static void prosesserGsk(Map<String, BeanStemmekrets> stemmekretserValgMap,
                                     Map<String, BeanKommune> kommuneMap,
                                     Map<String, BeanBydel> bydelerMap,
                                     Map<String, BeanHistorikkStemmeberettigedeMod> histStemmeberettigedeModMap,
                                     AlleValg alleValg,
                                     File skjema) throws IOException {
        LOG.info("Behandler " + skjema.getName());
        EvaAdminSkjema gskSkjema = new EvaAdminSkjema(skjema);
        if (gskSkjema.getValgtype() == ValgType.FY) {
            EvaAdminSkjema koSkjema = new EvaAdminSkjema(new File(skjema.getAbsolutePath().replaceAll("-FY-", "-KO-")));
            gskSkjema = SkjemaFunksjoner.kopierOsloFylkeFraKOValg(gskSkjema, koSkjema);
            Objects.requireNonNull(gskSkjema);
        }
        populerStemmekretser(gskSkjema, stemmekretserValgMap);
        populerBydeler(gskSkjema, bydelerMap);
        populerKommuner(gskSkjema, kommuneMap);

        // Mellomlagring i AlleValg-datastrukturen, for lettere å bygge de siste tabellene
        alleValg.populerStemmekretser(stemmekretserValgMap.values());
        alleValg.populerValgdistrikter(kommuneMap);
        Valg detteValget = alleValg.getValg(gskSkjema.getValgtype(), gskSkjema.getValgår());
        detteValget.oppdaterSbForUtgåtteKretser();

        populerHiststemmeberettigede(detteValget, histStemmeberettigedeModMap);
    }

    private static void populerStemmekretser(EvaAdminSkjema evaAdminSkjema, Map<String, BeanStemmekrets> stemmekretsMap) {
        List<JsonObjektSti> stiListe = SkjemaFunksjoner.byggListeAvTrestruktur(evaAdminSkjema, FYLKER, KOMMUNER, KRETSER);
        for (JsonObjektSti sti : stiListe) {
            String kommnr = sti.get(KOMMUNE_INDEKS).get(KOMMUNENUMMER).getAsString();
            String kretsnr = sti.get(KRETS_INDEKS).get("kretsnummer").getAsString();
            BeanStemmekrets stemmekrets = new BeanStemmekrets();
            stemmekrets.setValgaar(evaAdminSkjema.getValgår());
            stemmekrets.setValgtype("" + evaAdminSkjema.getValgtype());
            stemmekrets.setKommnr(kommnr);
            stemmekrets.setKretsnr(kretsnr);
            String kretsnavn = sti.get(KRETS_INDEKS).get("kretsnavn").getAsString();
            if (sti.get(KOMMUNE_INDEKS).get(KRETSER).getAsJsonArray().size() > 1 && kretsnr.equals(Konstanter.DEFAULT_KRETS)) {
                kretsnavn = "Uoppgitt krets";
            }
            stemmekrets.setKretsnavn(kretsnavn);
            stemmekrets.setOpptaltstatusmax(opptaltStatusMaxFor(evaAdminSkjema.getValgår(), kommnr, kretsnr));
            if (kommnr.equals(Konstanter.OSLO_KNR)) {
                stemmekrets.setBydelnr(Integer.parseInt(sti.get(KRETS_INDEKS).get("bydelsnummer").getAsString().substring(BYDELNR_STRING_LENGDE)));
            }
            stemmekrets.setStemmeberettigede(sti.get(KRETS_INDEKS).get("stemmeberettigede").getAsInt());
            stemmekretsMap.put(stemmekrets.getKey(), stemmekrets);
        }
    }

    // Opptaltstatusmax settes litt ulikt for bykommunene i forskjellige år, avhengig av opptellingsmåte.
    // For tiden (>2011) gjelder sentral opptelling av FHS i 0000-kretsen, mens VTS kan komme i alle kretser.
    // Det må også tas høyde for opptellingskommunen i valgdistrikt med kun 30+ kommuner i sametinget. Dette gjelder fra og med 2017.
    private static int opptaltStatusMaxFor(Integer valgÅr, String kommnr, String kretsnr) {
        return valgÅr <= ÅR_2011 ? opptaltStatusMaxFør2011(kommnr, kretsnr) : opptaltStatusMaxDefault(valgÅr, kommnr, kretsnr);
    }

    private static int opptaltStatusMaxFør2011(String kommnr, String kretsnr) {
        if (Konstanter.isStorby(kommnr)) {
            int ikkeDefaultKretsOS = kommnr.equals(Konstanter.STAVANGER_KNR) ? OpptaltStatus.BARE_VTS_ENDELIG.getStatus() : OpptaltStatus.ALLE_ENDELIG.getStatus();
            return kretsnr.equals(Konstanter.DEFAULT_KRETS) ? OpptaltStatus.BARE_FHS_ENDELIG.getStatus() : ikkeDefaultKretsOS;
        } else {
            return OpptaltStatus.ALLE_ENDELIG.getStatus();
        }
    }

    private static int opptaltStatusMaxDefault(Integer valgÅr, String kommnr, String kretsnr) {
        if (isOpptellingskommune(valgÅr, kommnr)) {
            if (Konstanter.erKommuneUtenStemmer(kommnr)) {
                return OpptaltStatus.INGEN.getStatus();
            }
            return OpptaltStatus.BARE_FHS_ENDELIG.getStatus();
        } else if (kretsnr.equals(Konstanter.DEFAULT_KRETS)) {
            return OpptaltStatus.ALLE_ENDELIG.getStatus();
        } else {
            return OpptaltStatus.BARE_VTS_ENDELIG.getStatus();
        }
    }

    // Opptellingskommunene brukes i sametinget, og har kommunenummer som begynner på 00.
    private static boolean isOpptellingskommune(Integer valgÅr, String kommnr) {
        return valgÅr >= ÅR_2017 && kommnr.startsWith("00");
    }

    private static void populerBydeler(EvaAdminSkjema evaAdminSkjema, Map<String, BeanBydel> bydelerMap) {
        List<JsonObjektSti> stiListe = SkjemaFunksjoner.byggListeAvTrestruktur(evaAdminSkjema, FYLKER, KOMMUNER, KRETSER);
        for (JsonObjektSti sti : stiListe) {
            int bydelsnr = Integer.parseInt(sti.get(2).get("bydelsnummer").getAsString().substring(BYDELNR_STRING_LENGDE));
            String bydelsnavn = sti.get(2).get("bydelsnavn").getAsString();
            String kommNr = sti.get(1).get(KOMMUNENUMMER).getAsString();
            if (kommNr.equals(Konstanter.OSLO_KNR)) { // bare Oslo har bydeler, med bydelsnumre 0-15 uten ledende nuller
                BeanBydel bydel = new BeanBydel();
                bydel.setKommnr(kommNr);
                bydel.setBydelnavn(bydelsnavn);
                if (bydelsnr == 0) {
                    bydel.setBydelnavn("Bydel 00"); // Hardkoder denne - alternativet er at det står "Hele kommunen"
                }
                bydel.setBydelnr(bydelsnr);
                bydel.setSsammenlignbar(Konverteringer.ssammenlignbar(kommNr, bydelsnr, VALGÅR));
                bydel.setNssammenlignbar(Konverteringer.nssammenlignbar(kommNr, bydelsnr, VALGÅR));
                bydelerMap.put(bydel.getKey(), bydel);
            }
        }
    }

    private static void populerKommuner(EvaAdminSkjema evaAdminSkjema, Map<String, BeanKommune> kommuneMap) {
        List<JsonObjektSti> stiListe = SkjemaFunksjoner.byggListeAvTrestruktur(evaAdminSkjema, FYLKER, KOMMUNER);
        for (JsonObjektSti sti : stiListe) {
            String kommNr = sti.get(1).get(KOMMUNENUMMER).getAsString();
            String kommNavn = sti.get(1).get(KOMMUNE).getAsString();
            String valgdistrikt = sti.get(1).get(VALGDISTRIKT).getAsString();
            BeanKommune beanKommune = new BeanKommune();
            beanKommune.setValgtype(evaAdminSkjema.getValgtype().name());
            beanKommune.setValgaar(evaAdminSkjema.getValgår());
            beanKommune.setKommnavn(kommNavn);
            beanKommune.setKommnr(kommNr);
            beanKommune.setValgdistriktnr(valgdistrikt);
            beanKommune.setSsammenlignbar(Konverteringer.ssammenlignbar(kommNr, VALGÅR));
            beanKommune.setNssammenlignbar(Konverteringer.nssammenlignbar(kommNr, VALGÅR));
            kommuneMap.put(beanKommune.getKey(), beanKommune);
        }
    }

    // DV = Dette Valget, SV = Siste Valget, NSV = Nest Siste Valget
    private static void populerHiststemmeberettigede(Valg dv, Map<String, BeanHistorikkStemmeberettigedeMod> histStemmeberettigedeModMap) {
        for (Krets krets : dv.getKretser()) {
            BeanHistorikkStemmeberettigedeMod histstemmeberettigede = new BeanHistorikkStemmeberettigedeMod();
            histstemmeberettigede.setKommnr(krets.getKommnr());
            histstemmeberettigede.setKretsnr(krets.getKretsnr());
            histstemmeberettigede.setValgtype(dv.getValgType().name());
            if (dv.getValgType() != SA) {
                List<Krets> kretserSV = dv.getSisteValg().getKretser(krets.getKommnr(), krets.getKretsnr(), dv.getValgår(), KonverteringStrategi.NY_TIL_GAMMEL);
                for (Krets kretsSV : kretserSV) {
                    histstemmeberettigede.setSsb(histstemmeberettigede.getSsb() + kretsSV.getSb());
                }
            }
            List<Krets> kretserNSV = dv.getNestSisteValg().getKretser(krets.getKommnr(), krets.getKretsnr(), dv.getValgår(), KonverteringStrategi.NY_TIL_GAMMEL);
            for (Krets kretsNSV : kretserNSV) {
                histstemmeberettigede.setNssb(histstemmeberettigede.getNssb() + kretsNSV.getSb());
            }
            histStemmeberettigedeModMap.put(BeanHistorikkStemmeberettigedeMod.makeKey("" + dv.getValgType(), krets.getKommnr(), krets.getKretsnr()), histstemmeberettigede);
        }
    }

    private static void prosesserGvpk(Map<String, BeanPartilister> partilisterMap,
                                      Map<String, BeanHistorikkStemmetallMod> histStemmetallModMap,
                                      Map<String, BeanHistorikkStemmetall> historikkStemmetallMap,
                                      Map<String, BeanHistorikkMandaterMod> histMandaterModMap,
                                      Map<String, BeanHistorikkMandater> historikkMandaterMap,
                                      Map<String, BeanValgdistrikter> valgdistrikterMap,
                                      Map<String, BeanKandidater> kandidaterMap,
                                      Map<String, BeanMandatberegning> mandaterMap,
                                      AlleValg alleValg,
                                      File skjema) throws IOException {
        LOG.info("Behandler " + skjema.getName());
        EvaAdminSkjema gvpkSkjema = new EvaAdminSkjema(skjema);
        if (gvpkSkjema.getValgtype() == ValgType.FY) {
            EvaAdminSkjema koSkjema = new EvaAdminSkjema(new File(skjema.getAbsolutePath().replaceAll("-FY-", "-KO-")));
            gvpkSkjema = SkjemaFunksjoner.kopierOsloValgdistriktFraKOTilFY(gvpkSkjema, koSkjema);
            Objects.requireNonNull(gvpkSkjema);
        }
        populerPartilister(gvpkSkjema, partilisterMap);
        populerValgdistrikter(gvpkSkjema, valgdistrikterMap);
        populerKandidater(gvpkSkjema, kandidaterMap);
        populerMandatberegning(gvpkSkjema, mandaterMap);

        // Mellomlagring i AlleValg-datastrukturen, for lettere å bygge de siste tabellene.
        // NB! alleValg ALLEREDE er populert med stemmekrets-info gjennom prosesserGsk()-metoden, her skal vi legge på parti-info

        Valg detteValget = alleValg.getValg(gvpkSkjema.getValgtype(), gvpkSkjema.getValgår());

        Valg sisteValget = detteValget.getSisteValg();
        if (sisteValget != null) {
            sisteValget.leggTilPartierIHverKrets(finnPartikoderPrValgdistrikt(sisteValget.getValgår(), sisteValget.getValgType(), historikkMandaterMap));
            sisteValget.leggTilStemmetall(historikkStemmetallMap);
        }

        Valg nestSisteValget = detteValget.getNestSisteValg();
        nestSisteValget.leggTilPartierIHverKrets(finnPartikoderPrValgdistrikt(nestSisteValget.getValgår(), nestSisteValget.getValgType(), historikkMandaterMap));
        nestSisteValget.leggTilStemmetall(historikkStemmetallMap);

        detteValget.leggTilPartierIHverKrets(finnPartikoderPrValgdistrikt(gvpkSkjema));
        detteValget.kopierStemmerTil0000KretsenDersomKretsenIkkeFinnesITidligereValg();
        detteValget.opprettPartierSomBareStilteITidligereValg();

        populerHistorikkStemmetallMod(detteValget, histStemmetallModMap);
        populerHistorikkMandaterMod(histMandaterModMap, detteValget, historikkMandaterMap);
    }

    private static Map<String, List<String>> finnPartikoderPrValgdistrikt(int valgår, ValgType valgType, Map<String, BeanHistorikkMandater> historikkMandaterMap) {
        Map<String, List<String>> partikoderPrValgdistrikt = new TreeMap<>();
        for (BeanHistorikkMandater hm : historikkMandaterMap.values()) {
            if (hm.getValgaar() == valgår && hm.getValgtype().equals(valgType.name())) {
                String valgdistriktnr = hm.getValgdistriktnr();
                List<String> partikoder = partikoderPrValgdistrikt.computeIfAbsent(valgdistriktnr, k -> {
                    ArrayList<String> list = new ArrayList<>();
                    if (VALGÅR >= ÅR_2013) {
                        list.add(Konstanter.BLANKE);
                    }
                    return list;
                });
                partikoder.add(hm.getPartikode());
            }
        }
        return partikoderPrValgdistrikt;
    }

    private static void populerMandatberegning(EvaAdminSkjema evaAdminSkjema, Map<String, BeanMandatberegning> mandatberegningMap) {
        List<JsonObjektSti> stiListe = SkjemaFunksjoner.byggListeAvTrestruktur(evaAdminSkjema, VALGDISTRIKTER, PARTIER);
        for (JsonObjektSti sti : stiListe) {
            String partikode = sti.get(PARTI_INDEKS).get(PARTIKODE).getAsString();
            BeanMandatberegning mandaterVD = new BeanMandatberegning(evaAdminSkjema.getValgtype().name(), sti.get(VALGDISTRIKT_INDEKS).get(VALGDISTRIKT).getAsString(), partikode, "0");
            mandatberegningMap.put(mandaterVD.getKey(), mandaterVD);
            // I ST/FY-valg gjøres det mandatberegning for hele landet (grunnet prognose/utjevningsmandater) - så her trenger man et valgdistrikt for Norge (00)
            // I tillegg oppretter vi en rad for hver prognose-beregning som skal gjøres for hvert valgdistrikt og for Norge som helhet
            if (evaAdminSkjema.getValgtype() == ValgType.FY || evaAdminSkjema.getValgtype() == ValgType.ST) {
                BeanMandatberegning mandaterVDProg = new BeanMandatberegning(evaAdminSkjema.getValgtype().name(), sti.get(VALGDISTRIKT_INDEKS).get(VALGDISTRIKT).getAsString(), partikode, "1");
                mandatberegningMap.put(mandaterVDProg.getKey(), mandaterVDProg);
                BeanMandatberegning mandaterNorge = new BeanMandatberegning(evaAdminSkjema.getValgtype().name(), Konstanter.VALGDISTRIKT_NORGE, partikode, "0"); // overskriver for samme parti, men det gjør ikke noe
                mandatberegningMap.put(mandaterNorge.getKey(), mandaterNorge);
                BeanMandatberegning mandaterNorgeProg = new BeanMandatberegning(evaAdminSkjema.getValgtype().name(), Konstanter.VALGDISTRIKT_NORGE, partikode, "1"); // overskriver for samme parti, men det gjør ikke noe
                mandatberegningMap.put(mandaterNorgeProg.getKey(), mandaterNorgeProg);
            }

        }
    }

    private static void populerKandidater(EvaAdminSkjema evaAdminSkjema, Map<String, BeanKandidater> kandidaterMap) {
        List<JsonObjektSti> stiListe = SkjemaFunksjoner.byggListeAvTrestruktur(evaAdminSkjema, VALGDISTRIKTER, PARTIER, KANDIDATER);
        for (JsonObjektSti sti : stiListe) {
            if (sti.size() == KANDIDAT_INDEKS + 1) {
                BeanKandidater bk = new BeanKandidater();
                bk.setValgaar(evaAdminSkjema.getValgår());
                bk.setValgtype(evaAdminSkjema.getValgtype().name());
                bk.setValgdistriktnr(sti.get(VALGDISTRIKT_INDEKS).get(VALGDISTRIKT).getAsString());
                bk.setPartikode(sti.get(PARTI_INDEKS).get(PARTIKODE).getAsString());
                bk.setPlassnr(sti.get(KANDIDAT_INDEKS).get(PLASSNUMMER).getAsInt());
                bk.setNavn(sti.get(KANDIDAT_INDEKS).get(NAVN).getAsString());
                bk.setFodselsdato(sti.get(KANDIDAT_INDEKS).get(FØDSELSDATO).getAsString());
                bk.setKjonn(sti.get(KANDIDAT_INDEKS).get(KJØNN).getAsString());
                bk.setBosted(sti.get(KANDIDAT_INDEKS).get(BOSTED).getAsString());
                kandidaterMap.put(bk.getKey(), bk);
            }
        }
    }

    private static Map<String, List<String>> finnPartikoderPrValgdistrikt(EvaAdminSkjema evaAdminSkjema) {
        Map<String, List<String>> partierPrValgdistrikt = new TreeMap<>();
        JsonObject jsonObject = evaAdminSkjema.getJsonObject();
        JsonArray valgdistrikter = jsonObject.get(VALGDISTRIKTER).getAsJsonArray();
        for (int i = 0; i < valgdistrikter.size(); i++) {
            JsonObject valgdistrikt = valgdistrikter.get(i).getAsJsonObject();
            List<String> partikoder = new ArrayList<>();
            JsonArray partier = valgdistrikt.get(PARTIER).getAsJsonArray();
            for (int j = 0; j < partier.size(); j++) {
                JsonObject parti = partier.get(j).getAsJsonObject();
                String partikode = parti.get(PARTIKODE).getAsString();
                partikoder.add(partikode);
            }
            String valgdistriktnr = valgdistrikt.get(VALGDISTRIKT).getAsString();
            partierPrValgdistrikt.put(valgdistriktnr, partikoder);
        }
        return partierPrValgdistrikt;
    }

    private static void populerValgdistrikter(EvaAdminSkjema evaAdminSkjema, Map<String, BeanValgdistrikter> valgdistrikterMap) {
        JsonObject jsonObject = evaAdminSkjema.getJsonObject();
        JsonArray valgdistrikter = jsonObject.get(VALGDISTRIKTER).getAsJsonArray();
        int valgår = evaAdminSkjema.getValgår();
        ValgType valgType = evaAdminSkjema.getValgtype();
        for (int i = 0; i < valgdistrikter.size(); i++) {
            JsonObject valgdistrikt = valgdistrikter.get(i).getAsJsonObject();
            String valgdistriktnr = valgdistrikt.get(VALGDISTRIKT).getAsString();
            String navn = valgdistrikt.get(NAVN).getAsString();
            int antallMandater = valgdistrikt.get(ANTALL_MANDATER).getAsInt();
            BeanValgdistrikter bvb = new BeanValgdistrikter();
            bvb.setValgtype(valgType.name());
            bvb.setValgaar(valgår);
            bvb.setNr(valgdistriktnr);
            bvb.setNavn(navn);
            bvb.setAntallmandater(antallMandater);
            valgdistrikterMap.put(bvb.getKey(), bvb);
        }
        // Vi må gjøre en spesialbehandling for ST/FY - disse trenger et toppnivå valgdistrikt "00" for å lagre mandatberegninger+utjevningsmandater på landsnivå.
        // Dette er et slags hack i Valgnatt, men det virker å være den peneste måten å gjøre dette på for tiden.
        if (valgType == ValgType.ST || valgType == ValgType.FY) {
            BeanValgdistrikter bvb = new BeanValgdistrikter();
            bvb.setValgtype(valgType.name());
            bvb.setValgaar(valgår);
            bvb.setNr(Konstanter.VALGDISTRIKT_NORGE);
            bvb.setNavn("Norge");
            bvb.setAntallmandater(jsonObject.get(ANTALL_UTJEVNINGSMANDATER).getAsInt()); // antall utjevningsmandater
            valgdistrikterMap.put(bvb.getKey(), bvb);
        }
    }

    private static void populerHistorikkMandaterMod(Map<String, BeanHistorikkMandaterMod> modMap, Valg detteValget, Map<String, BeanHistorikkMandater> historikkMandaterMap) {
        Map<String, Map<String, Parti>> partierPrValgdistrikter = detteValget.getPartierPrValgdistrikt();
        for (Map.Entry<String, Map<String, Parti>> partierPrValgdistrikt : partierPrValgdistrikter.entrySet()) {
            String valgdistriktnr = partierPrValgdistrikt.getKey();
            Map<String, Parti> partier = partierPrValgdistrikt.getValue();
            for (String partikode : partier.keySet()) {
                BeanHistorikkMandaterMod mandaterVD = modMap.get(BeanMandatberegning.makeKey(detteValget.getValgType().name(), valgdistriktnr, partikode, ""));
                if (mandaterVD == null) {
                    mandaterVD = initialiserMandathistorikk(modMap, detteValget.getValgType(), valgdistriktnr, partikode);
                }
                populerHistorikkMandaterModSV(detteValget, historikkMandaterMap, valgdistriktnr, partikode, mandaterVD);
                populerHistorikkMandaterModNSV(detteValget, historikkMandaterMap, valgdistriktnr, partikode, mandaterVD);
                populerHistorikkMandaterModNorge(detteValget, historikkMandaterMap, valgdistriktnr, partikode, modMap);
            }
        }
    }

    private static BeanHistorikkMandaterMod initialiserMandathistorikk(Map<String, BeanHistorikkMandaterMod> modMap, ValgType valgType, String valgdistriktnr, String partikode) {
        BeanHistorikkMandaterMod mandater;
        mandater = new BeanHistorikkMandaterMod();
        mandater.setValgtype(valgType.name());
        mandater.setValgdistriktnr(valgdistriktnr);
        mandater.setPartikode(partikode);
        mandater.setSantall(0);
        mandater.setSantall_utj(0);
        mandater.setNsantall(0);
        mandater.setNsantall_utj(0);
        modMap.put(mandater.getKey(), mandater);
        return mandater;
    }

    private static void populerHistorikkMandaterModSV(Valg detteValget, Map<String, BeanHistorikkMandater> historikkMandaterMap, String valgdistriktnr, String partikode, BeanHistorikkMandaterMod mandaterVD) {
        Valg sisteValg = detteValget.getSisteValg();
        if (sisteValg == null) {
            return;
        }
        BeanHistorikkMandater hm = historikkMandaterMap.get(BeanHistorikkMandater.makeKey(sisteValg.getValgår(), sisteValg.getValgType().name(), valgdistriktnr, partikode));
        if (hm == null && sisteValg.getValgType() == ValgType.KO) {
            for (String nyttKommnr : Konverteringer.kommune(valgdistriktnr, detteValget.getValgår(), KonverteringStrategi.NY_TIL_GAMMEL)) {
                hm = historikkMandaterMap.get(BeanHistorikkMandater.makeKey(sisteValg.getValgår(), sisteValg.getValgType().name(), nyttKommnr, partikode));
                if (hm != null) {
                    mandaterVD.setSantall(mandaterVD.getSantall() + hm.getAntall());
                    mandaterVD.setSantall_utj(mandaterVD.getSantall_utj() + hm.getAntall_utj());
                }
            }
        } else if (hm != null) {
            mandaterVD.setSantall(hm.getAntall());
            mandaterVD.setSantall_utj(hm.getAntall_utj());
        }
    }

    private static void populerHistorikkMandaterModNSV(Valg detteValget, Map<String, BeanHistorikkMandater> historikkMandaterMap, String valgdistriktnr, String partikode, BeanHistorikkMandaterMod mandaterVD) {
        Valg nestSisteValg = detteValget.getNestSisteValg();
        if (nestSisteValg == null) {
            return;
        }
        BeanHistorikkMandater hm = historikkMandaterMap.get(BeanHistorikkMandater.makeKey(nestSisteValg.getValgår(), nestSisteValg.getValgType().name(), valgdistriktnr, partikode));
        if (hm == null && nestSisteValg.getValgType() == ValgType.KO) { // håndterer sammenslåing/splitting av kommuner
            for (String nyttKommnr : Konverteringer.kommune(valgdistriktnr, detteValget.getValgår(), KonverteringStrategi.NY_TIL_GAMMEL)) {
                hm = historikkMandaterMap.get(BeanHistorikkMandater.makeKey(nestSisteValg.getValgår(), nestSisteValg.getValgType().name(), nyttKommnr, partikode));
                if (hm != null) {
                    mandaterVD.setNsantall(mandaterVD.getNsantall() + hm.getAntall());
                    mandaterVD.setNsantall_utj(mandaterVD.getNsantall_utj() + hm.getAntall_utj());
                }
            }
        } else if (hm != null) {
            mandaterVD.setNsantall(hm.getAntall());
            mandaterVD.setNsantall_utj(hm.getAntall_utj());
        }
    }

    private static void populerHistorikkMandaterModNorge(Valg detteValget, Map<String, BeanHistorikkMandater> historikkMandaterMap, String valgdistriktnr, String partikode, Map<String, BeanHistorikkMandaterMod> modMap) {
        BeanHistorikkMandaterMod mandaterNorge;
        if (detteValget.getValgType() == ValgType.ST || detteValget.getValgType() == ValgType.FY) {
            mandaterNorge = modMap.get(BeanMandatberegning.makeKey(detteValget.getValgType().name(), Konstanter.VALGDISTRIKT_NORGE, partikode, ""));
            if (mandaterNorge == null) {
                mandaterNorge = initialiserMandathistorikk(modMap, detteValget.getValgType(), Konstanter.VALGDISTRIKT_NORGE, partikode);
            }
            Valg sisteValg = detteValget.getSisteValg();
            BeanHistorikkMandater hmSisteValg = historikkMandaterMap.get(BeanHistorikkMandater.makeKey(sisteValg.getValgår(), sisteValg.getValgType().name(), valgdistriktnr, partikode));
            if (hmSisteValg != null) {
                mandaterNorge.setSantall(mandaterNorge.getSantall() + hmSisteValg.getAntall());
                mandaterNorge.setSantall_utj(mandaterNorge.getSantall_utj() + hmSisteValg.getAntall_utj());
            }

            Valg nestSisteValg = detteValget.getNestSisteValg();
            BeanHistorikkMandater hmNestsisteValg = historikkMandaterMap.get(BeanHistorikkMandater.makeKey(nestSisteValg.getValgår(), nestSisteValg.getValgType().name(), valgdistriktnr, partikode));
            if (hmNestsisteValg != null) { // finner bare noe slikt for ST/FY-valg
                mandaterNorge.setNsantall(mandaterNorge.getNsantall() + hmNestsisteValg.getAntall());
                mandaterNorge.setNsantall_utj(mandaterNorge.getNsantall_utj() + hmNestsisteValg.getAntall_utj());
            }
        }
    }

    // dv = Dette Valget, SV = Siste Valget, NSV = Nest Siste Valget
    private static void populerHistorikkStemmetallMod(Valg dv, Map<String, BeanHistorikkStemmetallMod> histstemmerMap) {
        for (Krets kretsDV : dv.getKretser()) {
            List<Krets> kretserSV = dv.getSisteValg() == null ? new ArrayList<>() : dv.getSisteValg().getKretser(kretsDV.getKommnr(), kretsDV.getKretsnr(), dv.getValgår(), KonverteringStrategi.NY_TIL_GAMMEL); // kretsSV = null i SA-valg
            List<Krets> kretserNSV = dv.getNestSisteValg().getKretser(kretsDV.getKommnr(), kretsDV.getKretsnr(), dv.getValgår(), KonverteringStrategi.NY_TIL_GAMMEL);
            for (Parti parti : kretsDV.getPartier()) {
                BeanHistorikkStemmetallMod bh = new BeanHistorikkStemmetallMod();
                bh.setValgtype(kretsDV.getValg().getValgType().name());
                bh.setKommnr(kretsDV.getKommnr());
                bh.setKretsnr(kretsDV.getKretsnr());
                bh.setPartikode(parti.getPartikode());
                bh.setStemmerFraSV(kretserSV, parti);
                bh.setStemmerFraNSV(kretserNSV, parti);
                histstemmerMap.put(bh.getKey(), bh);
            }
        }
    }

    private static void populerPartilister(EvaAdminSkjema evaAdminSkjema, Map<String, BeanPartilister> partilisterMap) {
        List<JsonObjektSti> stiListe = SkjemaFunksjoner.byggListeAvTrestruktur(evaAdminSkjema, VALGDISTRIKTER, PARTIER);
        for (JsonObjektSti sti : stiListe) {
            BeanPartilister partiliste = new BeanPartilister();
            partiliste.setPartikode(sti.get(PARTI_INDEKS).get(PARTIKODE).getAsString());
            partiliste.setPartinavn(sti.get(PARTI_INDEKS).get(PARTINAVN).getAsString());
            partiliste.setValgtype(evaAdminSkjema.getValgtype().name());
            // BLANKE-partiet har ikke angitt noen partikategori i eksporten fra EVA Admin, og må derfor håndteres slik:
            String partikategoriStr = sti.get(PARTI_INDEKS).get(PARTIKATEGORI).getAsString();
            partiliste.setPartikategori(Integer.parseInt(partikategoriStr.length() > 0 ? partikategoriStr : Konstanter.DEFAULT_PARTIKATEGORI));
            partilisterMap.put(partiliste.getKey(), partiliste);
        }
        oppdaterPartilisteVisningssekvens(partilisterMap, evaAdminSkjema.getValgår());
    }

    private static void oppdaterPartilisteVisningssekvens(Map<String, BeanPartilister> partilisterMap, int valgÅr) {
        Map<String, Integer> tellerMap = new TreeMap<>();

        for (Map.Entry<String, BeanPartilister> partilisteEntry : partilisterMap.entrySet()) {
            String valgTypePartikode = partilisteEntry.getKey();
            ValgType valgType = ValgType.valueOf(valgTypePartikode.substring(0, 2));
            String partikode = valgTypePartikode.substring(2);
            BeanPartilister partiliste = partilisteEntry.getValue();

            if (valgType == SA || visningssekvensMap.get(partikode) == null || erStortingsparti(valgÅr, partikode, partiliste)) {
                defaultSortering(tellerMap, valgType, partiliste);
            } else {
                spesialsorteringForStortingspartierIStFyKoValg(partikode, partiliste);
            }
        }
    }

    private static boolean erStortingsparti(int valgÅr, String partikode, BeanPartilister partiliste) {
        return partiliste.getPartikategori() > 1 || ("MDG".equals(partikode) && valgÅr <= ÅR_2013);
    }

    private static void defaultSortering(Map<String, Integer> tellerMap, ValgType valgType, BeanPartilister partiliste) {
        String telleMapKey = valgType.name() + partiliste.getPartikategori();
        Integer visningssekvens = tellerMap.get(telleMapKey);
        if (visningssekvens == null) {
            visningssekvens = partiliste.getPartikategori() * INDEKS_DEFAULT;
        } else {
            visningssekvens += INDEKS_DEFAULT_INKREMET;
        }
        tellerMap.put(telleMapKey, visningssekvens);
        partiliste.setVisningssekvens(visningssekvens);
    }

    private static void spesialsorteringForStortingspartierIStFyKoValg(String partikode, BeanPartilister partiliste) {
        partiliste.setVisningssekvens(visningssekvensMap.get(partikode));
    }
}
