package de.ivu.elect.dokumente.ergebnis.p22;

import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;

import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;

import de.ivu.document.generator.template.BaseDocument;
import de.ivu.document.generator.template.annotations.DocumentComplexField;
import de.ivu.document.generator.template.annotations.DocumentRepeat;
import de.ivu.document.generator.template.annotations.DocumentSimpleField;
import de.ivu.elect.business.ergebnis.control.NlErgebnisDaten;
import de.ivu.elect.business.ergebnis.entity.Stimmabgabeart;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.entity.GruppePK;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.NlListe;
import de.ivu.elect.business.wahlvorschlag.entity.WahlvorschlagPK;
import de.ivu.elect.dokumente.ergebnis.NlErgebnisDatenCustomDataDocumentContext;
import de.ivu.elect.dokumente.ergebnis.adapter.Fraction;
import de.ivu.elect.dokumente.ergebnis.adapter.NlErgebnisDaten2ErgebnisDokumentAdapter;
import de.ivu.elect.dokumente.wahlvorschlag.GruppeDokument;

public class GruppeUnterverteilungenDokument extends BaseDocument {

  private static final long serialVersionUID = 1L;

  @DocumentComplexField("Gruppe")
  public final GruppeDokument gruppe;

  @DocumentSimpleField("AnzahlSitze")
  public final Long anzahlSitze;

  @DocumentSimpleField("AnzahlStimmen")
  public final Long anzahlStimmen;

  @DocumentSimpleField("AnzahlStimmenGewichtet")
  public final Long anzahlStimmenGewichtetMitVoteValues;

  @DocumentSimpleField("WahlteilerBeiUnterverteilungInGruppe") //groepskiesdeler
  public final String wahlteilerBeiUnterverteilungInGruppe;

  @DocumentRepeat(value = "UnterverteilungZeilen", elementClass = AbstractUnterverteilungZeile.class, itemName = "zeile")
  public final List<AbstractUnterverteilungZeile> zeilen;

  public GruppeUnterverteilungenDokument(NlErgebnisDatenCustomDataDocumentContext context, NlErgebnisDaten2ErgebnisDokumentAdapter adapter, PolitischeGruppe gruppe, GruppeDokument gruppeDokument, SortedMap<Integer, SortedMap<Wahldurchfuehrungsgebiet, Liste>> listenPerWahlkreisPerStelNummer) {
    super();

    NlErgebnisDaten nlErgebnisDaten = context.getNlErgebnisDaten();
    Stimmabgabeart stimmabgabeart = context.getStimmabgabeart();

    GruppePK gruppePK = gruppe.getPk();

    this.gruppe = gruppeDokument;

    this.anzahlSitze = adapter.getAnzahlSitze(gruppePK).orElse(0L);

    this.anzahlStimmen = nlErgebnisDaten.getSummeGruppeListenstimmen(gruppe, stimmabgabeart);
    this.anzahlStimmenGewichtetMitVoteValues = nlErgebnisDaten.getSummeGruppeListenstimmenGewichtetMitVoteValues(gruppe, stimmabgabeart);

    this.wahlteilerBeiUnterverteilungInGruppe = adapter.getWahlteilerBeiUnterverteilungInGruppe(gruppePK)
            .map(Fraction::toString)
            .orElse(null);

    if (this.wahlteilerBeiUnterverteilungInGruppe != null) {
      this.zeilen = erzeugeZeilen(context, adapter, gruppePK, listenPerWahlkreisPerStelNummer);
    } else {
      this.zeilen = emptyList();
    }
  }

  private static List<AbstractUnterverteilungZeile> erzeugeZeilen(NlErgebnisDatenCustomDataDocumentContext context, NlErgebnisDaten2ErgebnisDokumentAdapter adapter, GruppePK gruppePk, SortedMap<Integer, SortedMap<Wahldurchfuehrungsgebiet, Liste>> listenPerWahlkreisPerStelNummer) {
    List<AbstractUnterverteilungZeile> result = new ArrayList<>();
    List<AbstractUnterverteilungZeile> stelloseListenZeilen = new ArrayList<>();

    for (Entry<Integer, SortedMap<Wahldurchfuehrungsgebiet, Liste>> entry : listenPerWahlkreisPerStelNummer.entrySet()) {
      Integer stelNummer = entry.getKey();
      SortedMap<Wahldurchfuehrungsgebiet, Liste> listePerWahlkreis = entry.getValue();

      if (isListenEinesStels(stelNummer)) {
        result.addAll(createUnterverteilungZeilenFuerStel(context, adapter, gruppePk, stelNummer, listePerWahlkreis));
      } else {
        stelloseListenZeilen.addAll(createStelloseListenZeilen(context, adapter, gruppePk, listePerWahlkreis));
      }
    }

    // Diese Zeilen stehen in der Tabelle an letzer Stelle.
    result.addAll(stelloseListenZeilen);

    return result;
  }

  private static List<AbstractUnterverteilungZeile> createStelloseListenZeilen(NlErgebnisDatenCustomDataDocumentContext context, NlErgebnisDaten2ErgebnisDokumentAdapter adapter, GruppePK gruppePk, SortedMap<Wahldurchfuehrungsgebiet, Liste> listePerWahlkreis) {
    return listePerWahlkreis.entrySet().stream()
            .map(entry -> createWahlkreisUnterverteilungZeile(context, adapter, gruppePk, entry.getKey(), entry.getValue()))
            .collect(toList());
  }

  private static List<AbstractUnterverteilungZeile> createUnterverteilungZeilenFuerStel(NlErgebnisDatenCustomDataDocumentContext context, NlErgebnisDaten2ErgebnisDokumentAdapter adapter, GruppePK gruppePk, Integer stelNummer, SortedMap<Wahldurchfuehrungsgebiet, Liste> listePerWahlkreis) {
    List<AbstractUnterverteilungZeile> result = new ArrayList<>();

    NlErgebnisDaten nlErgebnisDaten = context.getNlErgebnisDaten();
    Stimmabgabeart stimmabgabeart = context.getStimmabgabeart();

    long anzahlStimmenFuerStel = 0;
    long anzahlStimmenGewichtetMitVoteValuesFuerStel = 0;

    for (Entry<Wahldurchfuehrungsgebiet, Liste> entry : listePerWahlkreis.entrySet()) {
      Wahldurchfuehrungsgebiet wahlkreis = entry.getKey();
      Liste liste = entry.getValue();

      long anzahlStimmenFuerListe = nlErgebnisDaten.getSummeListeListenstimmen(wahlkreis, stimmabgabeart, liste);
      long anzahlStimmenGewichtetMitVoteValuesFuerListe = nlErgebnisDaten.getSummeListeListenstimmenGewichtetMitVoteValues(wahlkreis, stimmabgabeart, liste);

      anzahlStimmenFuerStel += anzahlStimmenFuerListe;
      anzahlStimmenGewichtetMitVoteValuesFuerStel += anzahlStimmenGewichtetMitVoteValuesFuerListe;

      result.add(new WahlkreisUnterverteilungZeile(wahlkreis.getNummer(), wahlkreis.getName(), anzahlStimmenFuerListe, anzahlStimmenGewichtetMitVoteValuesFuerListe));
    }

    String stelKey = NlListe.createStelKey(gruppePk, stelNummer);

    result.add(createStelUnterverteilungZeile(context, adapter, gruppePk, stelKey, stelNummer, anzahlStimmenFuerStel, anzahlStimmenGewichtetMitVoteValuesFuerStel));
    result.add(new LeereUnterverteilungZeile());

    return result;
  }

  private static StelUnterverteilungZeile createStelUnterverteilungZeile(NlErgebnisDatenCustomDataDocumentContext context, NlErgebnisDaten2ErgebnisDokumentAdapter adapter, GruppePK gruppePk, String stelKey, Integer stelNummer,
                                                                  long anzahlStimmen, long anzahlStimmenGewichtetMitVoteValues) {
    return new StelUnterverteilungZeile(stelNummer, anzahlStimmen, anzahlStimmenGewichtetMitVoteValues,
            adapter.getAnzahlSitzeAusErsterZuteilungFuerStelInGruppe(stelKey, gruppePk),
            adapter.getRestNachErsterZuteilungFuerStelInGruppe(stelKey, gruppePk),
            adapter.getAnzahlSitzeAusNiemeyerVerteilungFuerStelInGruppe(stelKey, gruppePk),
            adapter.getGesamtzahlSitzeNachNiemeyerVerteilungFuerStelInGruppe(stelKey, gruppePk));
  }

  private static WahlkreisUnterverteilungZeile createWahlkreisUnterverteilungZeile(NlErgebnisDatenCustomDataDocumentContext context, NlErgebnisDaten2ErgebnisDokumentAdapter adapter, GruppePK gruppePk, Wahldurchfuehrungsgebiet wahlkreis, Liste liste) {
    NlErgebnisDaten nlErgebnisDaten = context.getNlErgebnisDaten();
    Stimmabgabeart stimmabgabeart = context.getStimmabgabeart();

    WahlvorschlagPK listePk = liste.getPk();

    return new WahlkreisUnterverteilungZeile(wahlkreis.getNummer(), wahlkreis.getName(), nlErgebnisDaten.getSummeListeListenstimmen(wahlkreis, stimmabgabeart, liste), nlErgebnisDaten.getSummeListeListenstimmenGewichtetMitVoteValues(wahlkreis, stimmabgabeart, liste),
            adapter.getAnzahlSitzeAusErsterZuteilungFuerListeInGruppe(listePk, gruppePk),
            adapter.getRestNachErsterZuteilungFuerListeInGruppe(listePk, gruppePk),
            adapter.getAnzahlSitzeAusNiemeyerVerteilungFuerListeInGruppe(listePk, gruppePk),
            adapter.getGesamtzahlSitzeNachNiemeyerVerteilungFuerListeInGruppe(listePk, gruppePk));
  }

  private static boolean isListenEinesStels(Integer stelnummer) {
    return stelnummer != P22ErgebnisDatenCustomDataDocument.LISTE_OHNE_STEL_NUMMER;
  }
}
