package no.valg.valgnatt.frontend;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
import no.valg.valgnatt.common.JsonObjektKollapser;
import no.valg.valgnatt.common.JsonTabellKollapser;

import java.io.IOException;
import java.io.StringReader;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

/**
 * Klassen samler alle rapporter knyttet til ett enkelt valg i ett enkelt år. Det finnes to
 * konstruktorer som leser inn rapportsamlingen på hver sin måte. Så finnes det to metoder
 * for å hente ut rapportsamlingen (på hver av de to måtene). Med dette grepet plasserer
 * vi parsing/bygging av json i denne klassen.
 */
class RapportSamling {

    private static final int SEKVENSNR_ER_IKKE_I_BRUK = -1;
    private static final String UJM = "ujm";
    private static final String TOTALRAPPORT = "totalrapport";
    private static final String RESULTAT = "resultat";
    private static final String PARTIKODE = "partikode";
    private static final String PROGNOSE = "prognose";
    private static final String ID = "id";
    private static final String TIDSPUNKT = "tidspunkt";
    private static final String MERKNADER = "merknader";
    private static final String MANDATER = "mandater";
    private static final String STEMMER = "stemmer";
    private static final String STEMMEGIVING = "stemmegiving";
    private static final String FRAMMOTE = "frammote";
    private static final String OPPTALT = "opptalt";
    private static final String REPRESENTANTER = "representanter";
    private static final String EKVIVALENT = "ekvivalent";
    private static final String RELATED = "related";
    private static final String SELF = "self";
    private static final String UP = "up";
    private static final String NAVN = "navn";
    private static final String NR = "nr";
    private static final String NESTE_KANDIDATER = "nesteKandidater";

    private boolean stemmetallMottatt;
    private Map<String, String> rapportSamlingMap = new TreeMap<>(new RapportComparator());
    private String rapportSamlingJson;
    private int sisteSekvensnr;
    private int valgår;
    private int antallRapporter;

    RapportSamling(Map<String, Map<String, String>> rapportSamlingerPrValgtype, int valgår, boolean stemmetallMottatt) {
        for (Map<String, String> etRapportSamlingMap : rapportSamlingerPrValgtype.values()) {
            rapportSamlingMap.putAll(etRapportSamlingMap);
        }
        this.valgår = valgår;
        this.stemmetallMottatt = stemmetallMottatt;
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        sb.append("{\n");
        sb.append("\"instance\":-1,\n");
        sb.append("\"valgaar\": ").append(valgår).append(",\n");
        sb.append("\"stemmetallmottatt\": ").append(stemmetallMottatt).append(",\n");
        sb.append("\"report\":[\n");

        for (Entry<String, String> rapportSamling : rapportSamlingMap.entrySet()) {
            if (!first) {
                sb.append(",\n");
            }
            sb.append("{");
            sb.append("\"fullName\":\"").append(rapportSamling.getKey()).append("\",");
            sb.append("\"seqNr\":").append(SEKVENSNR_ER_IKKE_I_BRUK).append(",");
            sb.append("\"content\":").append(rapportSamling.getValue());
            sb.append("}");
            first = false;
        }
        sb.append("\n]\n");
        sb.append("}\n");
        this.rapportSamlingJson = sb.toString();
    }


    RapportSamling(String rapportSamlingJson, boolean mottattFraBackend) throws IOException {
        this.rapportSamlingJson = rapportSamlingJson;
        if (rapportSamlingJson.length() > 0) {
            JsonReader reader = new JsonReader(new StringReader(rapportSamlingJson));
            reader.beginObject();
            reader.nextName();  // serverInstansId
            reader.nextLong();  // instance
            reader.nextName();
            this.valgår = reader.nextInt();
            reader.nextName(); // stemmetallmottatt
            this.stemmetallMottatt = reader.nextBoolean();
            reader.nextName();  // report
            reader.beginArray();
            while (reader.hasNext()) {
                parseReport(reader, mottattFraBackend);
                antallRapporter++;
            }
            reader.endArray();
            reader.endObject();
            reader.close();
        }
    }

    private void parseReport(JsonReader reader, boolean mottattFraBackend) throws IOException {
        reader.beginObject();
        reader.nextName();
        String rapportnavn = reader.nextString();
        reader.nextName();
        int sekvensnr = reader.nextInt();
        reader.nextName();  // content
        JsonObject jsonObject = new JsonParser().parse(reader).getAsJsonObject();
        Gson gs = new GsonBuilder().setPrettyPrinting().serializeNulls().create();
        String json = gs.toJson(jsonObject);
        reader.endObject();
        if (rapportnavn.contains(UJM)) {
            json = kollapsUjm(json);
        } else if (!rapportnavn.contains(TOTALRAPPORT)) {
            json = kollapsEndelObjekter(json);
        }
        if (mottattFraBackend && sekvensnr > sisteSekvensnr) {
            sisteSekvensnr = sekvensnr;
        }
        rapportSamlingMap.put(rapportnavn, json);
    }

    private String kollapsUjm(String formatertJson) {
        String kollapsetJson = formatertJson;
        kollapsetJson = JsonTabellKollapser.kollapsTabell(kollapsetJson, RESULTAT, PARTIKODE);
        kollapsetJson = JsonTabellKollapser.kollapsTabell(kollapsetJson, PROGNOSE, PARTIKODE);
        return kollapsetJson;
    }

    /**
     * MSI feb 2016:
     * Her ønsker vi å formatere JSON ytterligere (den *er* allerede prettyprinted), slik
     * at vi kollapser enkelte objekter på en linje. Dette er litt tricky, så det blir noen
     * kodelinjer. Formålet er å redusere antall linjer JSON så det blir mer menneskevennlig.
     */
    private String kollapsEndelObjekter(String formatertJson) {
        String kollapsetJson = formatertJson;
        kollapsetJson = JsonObjektKollapser.kollapsObjekt(kollapsetJson, ID, false);
        kollapsetJson = JsonObjektKollapser.kollapsObjekt(kollapsetJson, TIDSPUNKT, true);
        kollapsetJson = JsonTabellKollapser.kollapsTabellUtenObjekter(kollapsetJson, MERKNADER);
        kollapsetJson = JsonObjektKollapser.kollapsObjekt(kollapsetJson, MANDATER, true);
        kollapsetJson = JsonObjektKollapser.kollapsObjekt(kollapsetJson, STEMMER, true);
        kollapsetJson = JsonObjektKollapser.kollapsObjekt(kollapsetJson, STEMMEGIVING, true);
        kollapsetJson = JsonObjektKollapser.kollapsObjekt(kollapsetJson, FRAMMOTE, true);
        kollapsetJson = JsonObjektKollapser.kollapsObjekt(kollapsetJson, OPPTALT, true);
        kollapsetJson = JsonObjektKollapser.kollapsObjekt(kollapsetJson, PROGNOSE, false, REPRESENTANTER);
        kollapsetJson = JsonObjektKollapser.kollapsObjekt(kollapsetJson, RESULTAT, false, REPRESENTANTER);
        kollapsetJson = JsonObjektKollapser.kollapsObjekt(kollapsetJson, EKVIVALENT, false);
        kollapsetJson = JsonTabellKollapser.kollapsTabell(kollapsetJson, REPRESENTANTER, NAVN);
        kollapsetJson = JsonTabellKollapser.kollapsTabell(kollapsetJson, NESTE_KANDIDATER, NAVN);
        kollapsetJson = JsonTabellKollapser.kollapsTabell(kollapsetJson, RELATED, NR);
        kollapsetJson = JsonObjektKollapser.kollapsObjekt(kollapsetJson, SELF, false);
        kollapsetJson = JsonObjektKollapser.kollapsObjekt(kollapsetJson, UP, false);
        return kollapsetJson;
    }

    Map<String, String> hentRapportSamlingMap() {
        return rapportSamlingMap;
    }

    String hentRapportSamlingJson() {
        return rapportSamlingJson;
    }

    int getValgår() {
        return this.valgår;
    }

    int getSisteSekvensnr() {
        return this.sisteSekvensnr;
    }

    int getAntallRapporter() {
        return this.antallRapporter;
    }

    boolean erStemmetallMottatt() {
        return stemmetallMottatt;
    }

    boolean nullRapporter() {
        return !stemmetallMottatt;
    }

}
