
package de.ivu.elect.dokumente.ergebnis;

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

import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.ToLongFunction;

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.dokumente.wahlvorschlag.GruppeDokument;

public class NlGewaehlteProGruppeDocument extends BaseDocument {

  private static final long serialVersionUID = 1L;

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

  @DocumentSimpleField("AnzahlSitzeDerGruppe")
  public final Long anzahlSitzeDerGruppe;

  @DocumentSimpleField("AnzahlStimmenDerGruppe")
  public final Long anzahlStimmenDerGruppe;

  @DocumentSimpleField("AnzahlStimmenDerGruppeGewichtet")
  public final Long anzahlStimmenDerGruppeGewichtetMitVoteValues;

  @DocumentRepeat(value = "GewaehlteMitVorzugsstimmenSortiertNachStimmenzahl", elementClass = NlKandidatStimmenDocument.class, itemName = "gewaehlt")
  public final List<NlKandidatStimmenDocument> gewaehlteMitVorzugsstimmenSortiertNachStimmenzahl;

  @DocumentRepeat(value = "GewaehlteMitVorzugsstimmenSortiertNachStimmenzahlGewichtetMitVoteValues", elementClass = NlKandidatStimmenDocument.class, itemName = "gewaehlt")
  public final List<NlKandidatStimmenDocument> gewaehlteMitVorzugsstimmenSortiertNachStimmenzahlGewichtetMitVoteValues;

  @DocumentRepeat(value = "GewaehlteNichtMitVorzugsstimmenSortiertNachRang", elementClass = NlKandidatStimmenDocument.class, itemName = "gewaehlt")
  public final List<NlKandidatStimmenDocument> gewaehlteNichtMitVorzugsstimmenSortiertNachRang;

  @DocumentRepeat(value = "KandidatenVonListenMitGeaenderterReihenfolge", elementClass = AbstractNlKandidatenStimmenProListeDocument.class, itemName = "liste")
  public final List<AbstractNlKandidatenStimmenProListeDocument> listenMitGeaenderterReihenfolge;

  public NlGewaehlteProGruppeDocument(Long anzahlSitzeDerGruppe, List<AbstractNlKandidatenStimmenProListeDocument> kandidatenStimmenDokumente) {
    super();

    if (!kandidatenStimmenDokumente.isEmpty()) {
      this.gruppe = kandidatenStimmenDokumente.get(0).gruppe;
    } else {
      this.gruppe = GruppeDokument.EMPTY_OBJECT;
    }

    this.anzahlSitzeDerGruppe = anzahlSitzeDerGruppe;

    this.anzahlStimmenDerGruppe = kandidatenStimmenDokumente.stream().mapToLong(dokument -> dokument.anzahlStimmenDerListe == null ? 0L : dokument.anzahlStimmenDerListe).sum();
    this.anzahlStimmenDerGruppeGewichtetMitVoteValues = kandidatenStimmenDokumente.stream().mapToLong(dokument -> dokument.anzahlStimmenDerListeGewichtetMitVoteValues == null ? 0L : dokument.anzahlStimmenDerListeGewichtetMitVoteValues).sum();

    List<NlKandidatStimmenDocument> kandidaturen = kandidatenStimmenDokumente.stream().flatMap(ks -> ks.kandidatenStimmenzahl.stream()).collect(toList());
    List<NlKandidatStimmenDocument> kandidaturenUeberVorzugGewaehlt = kandidaturen.stream().filter(ks -> ks.isUeberVorzugGewaehlt).collect(toList());

    Map<NlKandidatStimmenDocument, Long> gewaehlteMitVorzugsstimmen = new HashMap<>();
    Map<NlKandidatStimmenDocument, Long> gewaehlteMitVorzugsstimmenGewichtetMitVoteValues = new HashMap<>();

    for (NlKandidatStimmenDocument nlKandidatStimmenDocument : kandidaturenUeberVorzugGewaehlt) {
      gewaehlteMitVorzugsstimmen.put(nlKandidatStimmenDocument, getStimmenDesKandidaten(kandidaturen, nlKandidatStimmenDocument, document -> document.anzahlStimmen));
      gewaehlteMitVorzugsstimmenGewichtetMitVoteValues.put(nlKandidatStimmenDocument, getStimmenDesKandidaten(kandidaturen, nlKandidatStimmenDocument, document -> document.anzahlStimmenGewichtetMitVoteValues));
    }

    this.gewaehlteMitVorzugsstimmenSortiertNachStimmenzahl = sortDokumenteNachStimmenzahl(gewaehlteMitVorzugsstimmen); // ELECT-10767
    this.gewaehlteMitVorzugsstimmenSortiertNachStimmenzahlGewichtetMitVoteValues = sortDokumenteNachStimmenzahl(gewaehlteMitVorzugsstimmenGewichtetMitVoteValues); // ELECT-13591

    this.gewaehlteNichtMitVorzugsstimmenSortiertNachRang = kandidatenStimmenDokumente.stream().flatMap(ks -> ks.kandidatenStimmenzahl.stream().filter(k -> !k.isUeberVorzugGewaehlt)).sorted(Comparator.comparingLong(k -> k.rang)).collect(toList());

    this.listenMitGeaenderterReihenfolge = kandidatenStimmenDokumente.stream().filter(k -> k.isMitGeaenderterReihenfolge).collect(toList());
  }

  private static List<NlKandidatStimmenDocument> sortDokumenteNachStimmenzahl(Map<NlKandidatStimmenDocument, Long> gewaehlteMitVorzugsstimmen) {
    //ELECT-11312
    return gewaehlteMitVorzugsstimmen.keySet().stream().sorted((u, v) -> Long.compare(gewaehlteMitVorzugsstimmen.get(v), gewaehlteMitVorzugsstimmen.get(u))).collect(toList());
  }

  private static Long getStimmenDesKandidaten(List<NlKandidatStimmenDocument> kandidaturen, NlKandidatStimmenDocument nlKandidatStimmenDocument,
      ToLongFunction< ? super NlKandidatStimmenDocument> anzahlStimmenFct) {
    return kandidaturen.stream() //
        .filter(k -> k.getListenkandidatur().equals(nlKandidatStimmenDocument.getListenkandidatur()) || k.getListenkandidatur().getIdentischVerknuepfteKandidaturen().stream().anyMatch(ik -> nlKandidatStimmenDocument.getListenkandidatur().equals(ik))) //
        .mapToLong(anzahlStimmenFct).sum();
  }
}
