
package de.ivu.elect.presentation.ergebnis.dokumente.gesamtergebnis;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

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.document.generator.template.annotations.DocumentSubTemplate;
import de.ivu.elect.business.ergebnis.control.ErgebnisDaten;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.internet.control.GebietUndStimmabgabeart;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.person.control.EntityMitPersonendatenByNamenComparator;
import de.ivu.elect.business.wahlvorschlag.control.ListenverbindungNachPositionListenComparator;
import de.ivu.elect.business.wahlvorschlag.control.WahlvorschlagByStimmzettelPositionComparator;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.Listenverbindung;
import de.ivu.elect.dokumente.CoreElectContext;
import de.ivu.elect.dokumente.kandidatur.KandidaturCoreDaten;

// Cf. ListeStimmenDocument, GebietMitDirektkandidatenDocument, ListeMitStimmergebnissenDocument!!!
public class GebietMitListenDocument extends GebietMitStimmenUndWahlausschussDocument {

  private static final long serialVersionUID = 1L;

  @DocumentRepeat(value = "Listen", elementClass = ListeMitStimmergebnissenDocument.class, itemName = "liste")
  public final List<ListeMitStimmergebnissenDocument> listenMitStimmergebnis;

  @DocumentRepeat(value = "Listenverbindungen", elementClass = WasListenverbindungDokument.class, itemName = "listenverbindung")
  public final List<WasListenverbindungDokument> listenverbindungen;

  @DocumentSimpleField("ListenverbindungenVorhanden")
  public final boolean listenverbindungenVorhanden;

  // ELECT-3581 Listenkandidaten sortiert nach Alphabet sind für DD im Fall der Migrationsbeiratswahl interessant...
  @DocumentRepeat(value = "ListenkandidaturenAlphabetisch", elementClass = KandidaturMitStimmergebnissenDocument.class, itemName = "listenkandidatur")
  public final List<KandidaturMitStimmergebnissenDocument> listenkandidaturenAlphabetisch;

  @DocumentSubTemplate
  @DocumentRepeat(value = "Lose", elementClass = ErgebnisKonfliktDocument.class, itemName = "los")
  public final List<ErgebnisKonfliktDocument> lose; // Lose damit es hinter Listen kommt...

  @DocumentComplexField("ErgebnisGewähltMarkiert")
  public final ErgebnisGewaehltMarkiertDocument ergebnisGewaehltMarkiert; // ELECT-9429

  /**
   * @deprecated
   * @see GebietMitStimmenDocument#anzahlWahlvorschlagUnveraendertStimmzettel
   */
  @Deprecated
  @DocumentSimpleField("AnzahlStimmzettelNurEinzelnerWahlvorschlagUnveraendert")
  public final Long anzahlStimmzettelWahlvorschlagUnveraendert;

  /**
   * @deprecated
   * @see GebietMitStimmenDocument#anzahlWahlvorschlagVeraendertStimmzettel
   */
  @Deprecated
  @DocumentSimpleField("AnzahlStimmzettelNurEinzelnerWahlvorschlagVeraendert")
  public final Long anzahlStimmzettelWahlvorschlagVeraendert;

  /**
   * @deprecated
   * @see GebietMitStimmenDocument#anzahlWahlvorschlagPanaschiertStimmzettel
   */
  @Deprecated
  @DocumentSimpleField("AnzahlStimmzettelNurEinzelnerWahlvorschlagPanaschiert")
  public final Long anzahlStimmzettelWahlvorschlagPanaschiert;

  /**
   * @deprecated
   * @see GebietMitStimmenDocument#anzahlWahlvorschlagGesamtStimmzettel
   */
  @Deprecated
  @DocumentSimpleField("AnzahlStimmzettelNurEinzelnerWahlvorschlagGesamt")
  public final Long anzahlStimmzettelWahlvorschlagGesamt;

  /**
   * @deprecated
   * @see GebietMitStimmenDocument#anzahlGueltigeListenstimmen
   */
  @Deprecated
  @DocumentSimpleField("AnzahlStimmenGueltig")
  public Long anzahlStimmen;

  /**
   * @deprecated
   * @see GebietMitStimmenDocument#anzahlGueltigeListenstimmenNurListe
   */
  @Deprecated
  @DocumentSimpleField("AnzahlStimmenGueltigNurListe")
  public Long anzahlStimmenNurListe;

  /**
   * @deprecated
   * @see GebietMitStimmenDocument#anzahlGueltigeListenstimmenNurListenkandidaturen
   */
  @Deprecated
  @DocumentSimpleField("AnzahlStimmenGueltigNurListenkandidaturen")
  public Long anzahlStimmenNurListenkandidaturen;

  /**
   * @deprecated
   * @see ListeMitStimmergebnissenDocument#ergebnisGewaehltMarkiert#anzahlStimmenNurListenkandidaturen
   */
  @Deprecated
  @DocumentSimpleField("AnzahlListenkandidaturStimmenGesamt")
  public final Long anzahlListenkandidaturStimmenGesamt;

  public GebietMitListenDocument(CoreElectContext coreElectContext, ErgebnisDaten ergebnisDaten, GebietUndStimmabgabeart gebietUndStimmabgabeart, GebietMitStimmenDocument wahlgebietUebergeordnet,
      Map<Liste, ListeMitStimmergebnissenDocument> ergebnisDokumentPerListe, Map<Listenverbindung, WasListenverbindungDokument> ergebnisDokumentPerListenverbindung,
      KandidaturCoreDaten kandidaturCoreDaten, Map<Kandidatur, KandidaturMitStimmergebnissenDocument> ergebnisDokumentPerKandidatur) {
    super(coreElectContext, ergebnisDaten, gebietUndStimmabgabeart, wahlgebietUebergeordnet);

    this.listenMitStimmergebnis = bestimmeListenMitStimmergebnis(coreElectContext, ergebnisDaten, gebietUndStimmabgabeart, ergebnisDokumentPerListe, kandidaturCoreDaten,
        ergebnisDokumentPerKandidatur);

    this.listenverbindungen = bestimmeListenverbindungen(coreElectContext, ergebnisDaten, gebietUndStimmabgabeart, ergebnisDokumentPerListe, ergebnisDokumentPerListenverbindung);
    this.listenverbindungenVorhanden = !listenverbindungen.isEmpty();

    List<Listenkandidatur> listenkandidaturen = getListenkandidaturen(ergebnisDokumentPerListe);

    this.listenkandidaturenAlphabetisch = bestimmeListenkandidaturenAlphabetisch(listenkandidaturen, ergebnisDokumentPerKandidatur);

    this.lose = erstelleKonflikte(coreElectContext, ergebnisDaten, gebietUndStimmabgabeart, ergebnisDokumentPerListe, ergebnisDokumentPerListenverbindung, ergebnisDokumentPerKandidatur);

    this.ergebnisGewaehltMarkiert = new ErgebnisGewaehltMarkiertDocument(coreElectContext, ergebnisDaten, listenkandidaturen);

    Long anzahlStimmzettelWahlvorschlagUnveraendert = 0L;
    Long anzahlStimmzettelWahlvorschlagVeraendert = 0L;
    Long anzahlStimmzettelWahlvorschlagPanaschiert = 0L;
    Long anzahlStimmzettelWahlvorschlagGesamt = 0L;

    Long anzahlStimmen = 0L;
    Long anzahlStimmenNurListe = 0L;
    Long anzahlStimmenNurListenkandidaturen = 0L;

    for (ListeMitStimmergebnissenDocument listeMitStimmergebnissenDocument : this.listenMitStimmergebnis) {
      anzahlStimmzettelWahlvorschlagUnveraendert += fixNullToZero(listeMitStimmergebnissenDocument.anzahlStimmzettelWahlvorschlagUnveraendert);
      anzahlStimmzettelWahlvorschlagVeraendert += fixNullToZero(listeMitStimmergebnissenDocument.anzahlStimmzettelWahlvorschlagVeraendert);
      anzahlStimmzettelWahlvorschlagPanaschiert += fixNullToZero(listeMitStimmergebnissenDocument.anzahlStimmzettelWahlvorschlagPanaschiert);
      anzahlStimmzettelWahlvorschlagGesamt += fixNullToZero(listeMitStimmergebnissenDocument.anzahlStimmzettelWahlvorschlagGesamt);

      anzahlStimmen += fixNullToZero(listeMitStimmergebnissenDocument.anzahlGueltigerStimmen);
      anzahlStimmenNurListe += fixNullToZero(listeMitStimmergebnissenDocument.anzahlGueltigerStimmenNurListe);
      anzahlStimmenNurListenkandidaturen += fixNullToZero(listeMitStimmergebnissenDocument.anzahlGueltigerStimmenNurListenkandidaturen);
    }

    this.anzahlStimmzettelWahlvorschlagUnveraendert = anzahlStimmzettelWahlvorschlagUnveraendert;
    this.anzahlStimmzettelWahlvorschlagVeraendert = anzahlStimmzettelWahlvorschlagVeraendert;
    this.anzahlStimmzettelWahlvorschlagPanaschiert = anzahlStimmzettelWahlvorschlagPanaschiert;
    this.anzahlStimmzettelWahlvorschlagGesamt = anzahlStimmzettelWahlvorschlagGesamt;

    this.anzahlStimmen = anzahlStimmen;
    this.anzahlStimmenNurListe = anzahlStimmenNurListe;
    this.anzahlStimmenNurListenkandidaturen = anzahlStimmenNurListenkandidaturen;
    this.anzahlListenkandidaturStimmenGesamt = this.anzahlStimmenNurListenkandidaturen;
  }

  public static List<KandidaturMitStimmergebnissenDocument> bestimmeListenkandidaturenAlphabetisch(List<Listenkandidatur> alleListenkandidaturen,
      Map<Kandidatur, KandidaturMitStimmergebnissenDocument> ergebnisDokumentPerKandidatur) {

    alleListenkandidaturen.sort(new EntityMitPersonendatenByNamenComparator<Listenkandidatur>());

    return alleListenkandidaturen.stream().map(ergebnisDokumentPerKandidatur::get).collect(Collectors.toList());
  }

  private List<Listenkandidatur> getListenkandidaturen(Map<Liste, ListeMitStimmergebnissenDocument> ergebnisDokumentPerListe) {
    List<Listenkandidatur> alleListenkandidaturen = new ArrayList<>();

    for (Liste liste : ergebnisDokumentPerListe.keySet()) {
      alleListenkandidaturen.addAll(liste.getListenkandidaturen());
    }

    return alleListenkandidaturen;
  }

  private List<WasListenverbindungDokument> bestimmeListenverbindungen(CoreElectContext coreElectContext, ErgebnisDaten ergebnisDaten, GebietUndStimmabgabeart gebietUndStimmabgabeart,
      Map<Liste, ListeMitStimmergebnissenDocument> ergebnisDokumentPerListe, Map<Listenverbindung, WasListenverbindungDokument> ergebnisDokumentPerListenverbindung) {
    ArrayList<Listenverbindung> listenverbindungenSortiert = new ArrayList<>(ergebnisDaten.getListenverbindungenFuerGebiet(gebietUndStimmabgabeart.getWahldurchfuehrungsgebiet()));
    Collections.sort(listenverbindungenSortiert, new ListenverbindungNachPositionListenComparator());
    List<WasListenverbindungDokument> result = new ArrayList<>(listenverbindungenSortiert.size());

    long laufendeNummer = 0L;

    for (Listenverbindung listenverbindung : listenverbindungenSortiert) {
      laufendeNummer++;

      List<ListeMitStimmergebnissenDocument> listenDokumente = new ArrayList<>();

      for (Liste liste : listenverbindung.getListen()) {
        if (ergebnisDokumentPerListe.containsKey(liste)) {
          listenDokumente.add(ergebnisDokumentPerListe.get(liste));
        }
      }

      WasListenverbindungDokument dokument = new WasListenverbindungDokument(coreElectContext, listenverbindung, laufendeNummer, this, listenDokumente);

      ergebnisDokumentPerListenverbindung.put(listenverbindung, dokument);

      result.add(dokument);
    }

    return result;
  }

  private List<ListeMitStimmergebnissenDocument> bestimmeListenMitStimmergebnis(CoreElectContext coreElectContext, ErgebnisDaten ergebnisDaten, GebietUndStimmabgabeart gebietUndStimmabgabeart,
      Map<Liste, ListeMitStimmergebnissenDocument> ergebnisDokumentPerListe, KandidaturCoreDaten kandidaturCoreDaten,
      Map<Kandidatur, KandidaturMitStimmergebnissenDocument> ergebnisDokumentPerKandidatur) {
    List<ListeMitStimmergebnissenDocument> result = new ArrayList<>();

    Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = gebietUndStimmabgabeart.getWahldurchfuehrungsgebiet();

    long laufendeNummer = 0;

    List<Liste> listenSorted = new ArrayList<>(ergebnisDaten.getListenInGebietshierarchie(wahldurchfuehrungsgebiet));
    Collections.sort(listenSorted, new WahlvorschlagByStimmzettelPositionComparator(wahldurchfuehrungsgebiet, ergebnisDaten.getEntityAufStimmzettelDaten()));

    for (Liste liste : listenSorted) {
      laufendeNummer++;

      ListeMitStimmergebnissenDocument dokument = new ListeMitStimmergebnissenDocument(coreElectContext, ergebnisDaten, gebietUndStimmabgabeart, liste, laufendeNummer, kandidaturCoreDaten,
          ergebnisDokumentPerKandidatur);

      ergebnisDokumentPerListe.put(liste, dokument);

      result.add(dokument);
    }

    return result;
  }
}
