
package de.ivu.elect.was.sitzberechnung.control;

import static de.ivu.elect.jee.entity.EntityUtil.castTo;
import static java.util.stream.Collectors.toSet;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import de.ivu.elect.business.kandidatur.entity.Direktkandidatur;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.sitzberechnung.entity.Sitzberechnungseingabe;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.was.sitzberechnung.entity.AbstractElectSitzberechnungKandidatur;
import de.ivu.elect.was.sitzberechnung.entity.ElectSitzberechnungEntities;
import de.ivu.elect.was.sitzberechnung.entity.ElectSitzberechnungGebiet;
import de.ivu.elect.was.sitzberechnung.entity.ElectSitzberechnungListe;
import de.ivu.elect.was.sitzberechnung.entity.ElectSitzberechnungStaatenGruppe;

public class ElectSitzberechnungseingabeMapper extends AbstractElectSitzberechnungMapper {

  public ElectSitzberechnungseingabeMapper() {
    super();
  }

  public de.ivu.elect.sitzberechnung.Sitzberechnungseingabe mapExtern(ElectSitzberechnungContext sitzberechnungContext, ElectSitzberechnungEntities sitzberechnungEntities,
      ElectErgebnisKonfliktMapper ergebnisKonfliktMapper, ElectAnzahlSitzeMapper anzahlSitzeMapper, ElectAnzahlStimmenMapper anzahlStimmenMapper, Sitzberechnungseingabe< ? > sitzberechnungseingabe) {
    ElectSitzberechnungLokalisierung sitzberechnungLokalisierung = new ElectSitzberechnungLokalisierung(sitzberechnungContext, sitzberechnungEntities);

    Wahl wahl = sitzberechnungContext.getWahl();

    Optional<BigDecimal> quorumProzenthuerdeDezimal = Optional.empty();

    if (wahl != null) {
      quorumProzenthuerdeDezimal = Optional.ofNullable(wahl.getQuorumProzenthuerdeDezimal());
    }

    ElectSitzberechnungGebiet wahlgebietExtern = sitzberechnungEntities.getWahlgebiet();

    Map<ElectSitzberechnungGebiet, Long> anzahlSitzePerGebietExtern = new HashMap<>();
    anzahlSitzePerGebietExtern.put(wahlgebietExtern, anzahlSitzeMapper.getAnzahlSitze(wahlgebietExtern.getEntity()));

    Set<ElectSitzberechnungGebiet> wahlvorschlaggebieteExtern = sitzberechnungEntities.getGebiete().stream()
        .filter(gebietExtern -> gebietExtern.isDirektwahlgebiet() || gebietExtern.isListenwahlgebiet()).collect(toSet());

    for (ElectSitzberechnungGebiet wahlvorschlaggebietExtern : wahlvorschlaggebieteExtern) {
      // ELECT-15111, bspw. KOM-HB, wo bei der LTW (Bürgerschaftswahl) das Bundesland das Wahlgebiet ist, aber die Sitze separat in den zwei Wahlbereichen Bremen/Bremerhaven verteilt werden...
      // ELECT-16013, bspw. KOM-SH, wo bei der Kommunalwahl in den Wahlkreisen ein Teil aller Sitze im Wahlgebiet an unmittelbare Vertreter verteilt werden...
      anzahlSitzePerGebietExtern.putIfAbsent(wahlvorschlaggebietExtern, anzahlSitzeMapper.getAnzahlSitze(wahlvorschlaggebietExtern.getEntity()));
    }

    Map<ElectSitzberechnungListe, Long> anzahlStimmenPerListeExtern = collectAnzahlStimmenPerListeExtern(anzahlStimmenMapper, sitzberechnungEntities.getListen());
    Map<AbstractElectSitzberechnungKandidatur< ? >, Long> anzahlStimmenPerKandidaturExtern = collectAnzahlStimmenPerKandidaturExtern(anzahlStimmenMapper, sitzberechnungEntities.getKandidaturen());

    Map<de.ivu.elect.sitzberechnung.Minderheitengruppe, Long> anzahlSitzePerMinderheitengruppe = collectAnzahlSitzePerMinderheitengruppeExtern(sitzberechnungContext, sitzberechnungEntities,
        anzahlSitzeMapper);

    Collection<de.ivu.elect.sitzberechnung.konflikt.ErgebnisKonflikt> ergebnisKonflikteExtern = ergebnisKonfliktMapper.mapExtern(sitzberechnungContext, sitzberechnungEntities,
        sitzberechnungseingabe.getKonflikte());

    return new ElectSitzberechnungseingabe(sitzberechnungLokalisierung, sitzberechnungEntities.getWahlgebiet(), quorumProzenthuerdeDezimal, anzahlSitzePerGebietExtern,
        sitzberechnungEntities.getGebiete(), anzahlStimmenPerListeExtern, anzahlStimmenPerKandidaturExtern, anzahlSitzePerMinderheitengruppe, ergebnisKonflikteExtern);
  }

  private Map<ElectSitzberechnungListe, Long> collectAnzahlStimmenPerListeExtern(ElectAnzahlStimmenMapper anzahlStimmenMapper, Collection<ElectSitzberechnungListe> listenExtern) {
    Map<ElectSitzberechnungListe, Long> result = new HashMap<>();

    for (ElectSitzberechnungListe listeExtern : listenExtern) {
      Liste liste = listeExtern.getEntity();

      result.put(listeExtern, anzahlStimmenMapper.getListenstimmen(liste));
    }

    return result;
  }

  private Map<AbstractElectSitzberechnungKandidatur< ? >, Long> collectAnzahlStimmenPerKandidaturExtern(ElectAnzahlStimmenMapper anzahlStimmenMapper,
      Collection<AbstractElectSitzberechnungKandidatur< ? >> kandidaturenExtern) {
    Map<AbstractElectSitzberechnungKandidatur< ? >, Long> result = new HashMap<>();

    for (AbstractElectSitzberechnungKandidatur< ? > kandidaturExtern : kandidaturenExtern) {
      Kandidatur kandidatur = kandidaturExtern.getEntity();
      long anzahlStimmen = 0L;

      if (kandidatur.isListenkandidatur()) {
        anzahlStimmen = anzahlStimmenMapper.getListenstimmen(castTo(kandidatur, Listenkandidatur.class));
      } else if (kandidatur.isDirektkandidatur()) {
        anzahlStimmen = anzahlStimmenMapper.getDirektstimmen(castTo(kandidatur, Direktkandidatur.class));
      }

      result.put(kandidaturExtern, anzahlStimmen);
    }

    return result;
  }

  private Map<de.ivu.elect.sitzberechnung.Minderheitengruppe, Long> collectAnzahlSitzePerMinderheitengruppeExtern(ElectSitzberechnungContext sitzberechnungContext,
      ElectSitzberechnungEntities sitzberechnungEntities, ElectAnzahlSitzeMapper anzahlSitzeMapper) {
    Map<de.ivu.elect.sitzberechnung.Minderheitengruppe, Long> result = new HashMap<>();

    for (ElectSitzberechnungStaatenGruppe staatenGruppeExtern : sitzberechnungEntities.getStaatenGruppen()) {
      if (staatenGruppeExtern.isMinderheitengruppe()) {
        result.put(staatenGruppeExtern, anzahlSitzeMapper.getAnzahlSitze(staatenGruppeExtern.getEntity()));
      }
    }

    return result;
  }
}
