package de.ivu.elect.business.kandidatur.control;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.enterprise.inject.Specializes;

import de.ivu.elect.business.ergebnis.control.StimmenPool;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.kandidatur.entity.Direktkandidatur;
import de.ivu.elect.business.kandidatur.entity.KandidaturKumulierteStimmen;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.stimmzettel.entity.EntityAufStimmzettelDaten;

@Specializes
public class NlKandidaturKumulierteStimmenBuilder extends KandidaturKumulierteStimmenBuilder {

  @Override
  public List<KandidaturKumulierteStimmen> erzeugeKandidaturKumulierteStimmenFuerGruppeAufGebiet(Gebiet gebiet, List<Listenkandidatur> listenkandidaturenDerGruppe, List<Direktkandidatur> direktkandidaturenDerGruppe, EntityAufStimmzettelDaten entityAufStimmzettelDaten, StimmenPool stimmenDerGruppe) {
    List<KandidaturKumulierteStimmen> kandidaturKumulierteStimmen = super
        .erzeugeKandidaturKumulierteStimmenFuerGruppeAufGebiet(gebiet, listenkandidaturenDerGruppe, direktkandidaturenDerGruppe, entityAufStimmzettelDaten,
            stimmenDerGruppe);

    if (gebiet.isListenwahlgebiet() || kandidaturKumulierteStimmen.isEmpty()) {
      kandidaturKumulierteStimmen.sort(super.getDefaultComparator()); // ELECT-11343
      return kandidaturKumulierteStimmen;
    }

    Map<Listenkandidatur, Collection<KandidaturKumulierteStimmen>> gruppiertNachKandidatur = new LinkedHashMap<>(); // Reihenfolge behalten??

    for (KandidaturKumulierteStimmen kandidaturKumulierteStimman : kandidaturKumulierteStimmen) {
      findOrCreateGruppe(kandidaturKumulierteStimman.getListenkandidatur(), gruppiertNachKandidatur).add(kandidaturKumulierteStimman);
    }

    List<KandidaturKumulierteStimmen> result = new ArrayList<>(gruppiertNachKandidatur.size());

    // Zusammenfassen ELECT-11331
    for (Map.Entry<Listenkandidatur, Collection<KandidaturKumulierteStimmen>> e : gruppiertNachKandidatur.entrySet()) {
      Listenkandidatur listenkandidatur = e.getKey();
      Collection<KandidaturKumulierteStimmen> gruppe = e.getValue();

      Long anzahlListenstimmen = null;
      Long anzahlListenstimmenAnteilAusEinzelstimmvergabe = null;
      Long anzahlListenstimmenAnteilAusWahlvorschlagstimmvergabe = null;
      Long anzahlListenstimmenNichtErgebnisrelevant = null;

      for (KandidaturKumulierteStimmen kumulierteStimmen : gruppe) {
        anzahlListenstimmen = add(anzahlListenstimmen, kumulierteStimmen.getAnzahlListenstimmen());
        anzahlListenstimmenAnteilAusEinzelstimmvergabe = add(anzahlListenstimmenAnteilAusEinzelstimmvergabe, kumulierteStimmen.getAnzahlListenstimmenAnteilAusEinzelstimmvergabe());
        anzahlListenstimmenAnteilAusWahlvorschlagstimmvergabe = add(anzahlListenstimmenAnteilAusWahlvorschlagstimmvergabe,
            kumulierteStimmen.getAnzahlListenstimmenAnteilAusWahlvorschlagstimmvergabe());
        anzahlListenstimmenNichtErgebnisrelevant = add(anzahlListenstimmenNichtErgebnisrelevant,
            kumulierteStimmen.getAnzahlListenstimmenNichtErgebnisrelevant());
      }

      result.add(new KandidaturKumulierteStimmen(entityAufStimmzettelDaten, gebiet, null, null,
          listenkandidatur, anzahlListenstimmen, anzahlListenstimmenAnteilAusEinzelstimmvergabe, anzahlListenstimmenAnteilAusWahlvorschlagstimmvergabe,
          anzahlListenstimmenNichtErgebnisrelevant));

    }

    // ELECT-11343 Sortierung
    result.sort(this::sortResult);

    return result;
  }

  @Override
  public Comparator<? super KandidaturKumulierteStimmen> getDefaultComparator() {
    return (u,v)->0; // nicht sortieren // ELECT-11343
  }

  private int sortResult(KandidaturKumulierteStimmen k1, KandidaturKumulierteStimmen k2) {

    Long stimmen1 = k1.getAnzahlListenstimmen();
    Long stimmen2 = k2.getAnzahlListenstimmen();

    if (stimmen1 == null && stimmen2 == null) {
      return 0;
    }
    if (stimmen1 == null) {
      return 1;
    }
    if (stimmen2 == null) {
      return -1;
    }

    return stimmen2.compareTo(stimmen1); // absteigend sortieren
  }

  private Long add(Long vorher, Long summe) {
    if (summe == null) {
      return vorher;
    }
    if (vorher == null) {
      return summe;
    }
    return vorher + summe;

  }

  private Collection<KandidaturKumulierteStimmen> findOrCreateGruppe(Listenkandidatur listenkandidatur, Map<Listenkandidatur, Collection<KandidaturKumulierteStimmen>> gruppiertNachKandidatur) {
    for (Map.Entry<Listenkandidatur, Collection<KandidaturKumulierteStimmen>> entry : gruppiertNachKandidatur.entrySet()) {
      if (entry.getKey().getIdentischVerknuepfteKandidaturen().contains(listenkandidatur)) {
        return entry.getValue();
      }
    }

    Collection<KandidaturKumulierteStimmen> result = new HashSet<>();
    gruppiertNachKandidatur.put(listenkandidatur, result);
    return result;
  }
}
