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

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import de.ivu.elect.business.ergebnis.control.ErgebnisDaten;
import de.ivu.elect.business.ergebnis.control.ErgebnisDatenReferendumOptionen;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.hochrechnung.entity.ReferendumFrage;
import de.ivu.elect.business.internet.control.GebietUndStimmabgabeart;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahlvorschlag.entity.ReferendumOption;

public class GebietDatenVonAktuelleErgebnisdaten extends GebietDatenVonErgebnisdaten {

  GebietDatenVonAktuelleErgebnisdaten(Wahldurchfuehrungsgebiet gebiet, ErgebnisDaten ergebnisDatenAktuell) {
    super(gebiet, ergebnisDatenAktuell);
  }

  @Override
  Function<GebietUndStimmabgabeart, Long> getWahlberechtigteOhneWahlscheinSupplier(ErgebnisDaten ergebnisDatenAktuelleWahl) {
    return ergebnisDatenAktuelleWahl::getWahlberechtigteOhneWahlscheinFuerErfassungsgebietUnterhalbVonAnzahl;
  }

  @Override
  Function<GebietUndStimmabgabeart, Long> getWahlberechtigteMitWahlscheinSupplier(ErgebnisDaten ergebnisDatenAktuelleWahl) {
    return ergebnisDatenAktuelleWahl::getWahlberechtigteMitWahlscheinFuerErfassungsgebietUnterhalbVonAnzahl;
  }

  @Override
  Function<GebietUndStimmabgabeart, Long> getWahlberechtigteNichtImWaehlerverzeichnisSupplier(ErgebnisDaten ergebnisDatenAktuelleWahl) {
    return ergebnisDatenAktuelleWahl::getWahlberechtigteNichtImWaehlerverzeichnisFuerErfassungsgebietUnterhalbVonAnzahl;
  }

  @Override
  Function<GebietUndStimmabgabeart, Long> getWahlberechtigteGesamtSupplier(ErgebnisDaten ergebnisDatenAktuelleWahl) {
    return ergebnisDatenAktuelleWahl::getWahlberechtigteGesamtFuerErfassungsgebietUnterhalbVonAnzahl;
  }

  @Override
  Function<Wahldurchfuehrungsgebiet, Long> getWaehlerOhneWahlscheinSupplier(ErgebnisDaten ergebnisDatenAktuelleWahl) {
    return ergebnisDatenAktuelleWahl::getWaehlerOhneWahlschein;
  }

  @Override
  Function<Wahldurchfuehrungsgebiet, Long> getWaehlerMitWahlscheinSupplier(ErgebnisDaten ergebnisDatenAktuelleWahl) {
    return ergebnisDatenAktuelleWahl::getWaehlerMitWahlschein;
  }

  @Override
  Function<Wahldurchfuehrungsgebiet, Long> getWaehlerGesamtSupplier(ErgebnisDaten ergebnisDatenAktuelleWahl) {
    return ergebnisDatenAktuelleWahl::getWaehlerGesamt;
  }

  @Override
  Function<GebietUndStimmabgabeart, Long> getErststimmenGueltigeSupplier(ErgebnisDaten ergebnisDatenAktuelleWahl) {
    return ergebnisDatenAktuelleWahl::getErststimmenAktuellGueltige;
  }

  @Override
  Function<GebietUndStimmabgabeart, Long> getZweitstimmenGueltigeSupplier(ErgebnisDaten ergebnisDatenAktuelleWahl) {
    return ergebnisDatenAktuelleWahl::getZweitstimmenAktuellGueltige;
  }

  @Override
  Function<GebietUndStimmabgabeart, Long> getErststimmenUngueltigeSupplier(ErgebnisDaten ergebnisDatenAktuelleWahl) {
    return ergebnisDatenAktuelleWahl::getErststimmenAktuellUngueltige;
  }

  @Override
  Function<GebietUndStimmabgabeart, Long> getZweitstimmenUngueltigeSupplier(ErgebnisDaten ergebnisDatenAktuelleWahl) {
    return ergebnisDatenAktuelleWahl::getZweitstimmenAktuellUngueltige;
  }

  @Override
  Map<PolitischeGruppe, Long> getErstStimmenFuerGebiet(ErgebnisDaten ergebnisDatenAktuell, GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    if (ergebnisDatenAktuell == null || gebietUndStimmabgabeart == null) {
      return new HashMap<>();
    }
    return ergebnisDatenAktuell.getErststimmenAktuell(gebietUndStimmabgabeart);
  }

  @Override
  Map<PolitischeGruppe, Long> getZweitStimmenFuerGebiet(ErgebnisDaten ergebnisDatenAktuell, GebietUndStimmabgabeart gebietUndStimmabgabeart) {
    if (ergebnisDatenAktuell == null || gebietUndStimmabgabeart == null) {
      return new HashMap<>();
    }
    return ergebnisDatenAktuell.getZweitstimmenAktuell(gebietUndStimmabgabeart);
  }

  @Override
  Function<PolitischeGruppe, Map<Listenkandidatur, Long>> getListenKandidaturStimmenGetterFuerGebiet(ErgebnisDaten ergebnisDatenAktuell, Wahldurchfuehrungsgebiet gebiet) {
    return politischeGruppe -> getStimmenFuerListenkandidaturenMitEchtenNullen(ergebnisDatenAktuell, gebiet, politischeGruppe);
  }

  private Map<Listenkandidatur, Long> getStimmenFuerListenkandidaturenMitEchtenNullen(ErgebnisDaten ergebnisDaten, Wahldurchfuehrungsgebiet gebiet, PolitischeGruppe politischeGruppe) {
    Map<Listenkandidatur, Long> result = ergebnisDaten.getErgebnisDatenListenkandidaturen().collectStimmenPerListenkandidatur(politischeGruppe, gebiet);
    if (isStimmenVorhanden(result)) {
      return result;
    }
    // When we are here, this means that the numbers are either 0 or null overall
    // we need to make sure that they are null and not 0
    Map<Listenkandidatur, Long> final_result = new HashMap<>();
    result.forEach(((listenkandidatur, possibleZero) -> final_result.put(listenkandidatur, null)));
    return final_result;
  }

  private <K> boolean isStimmenVorhanden(Map<K, Long> stimmen) {
    return stimmen.values().stream().anyMatch(x -> x != null && x > 0);
  }

  private boolean isLetzterEingangFuerGebietGueltig(Wahldurchfuehrungsgebiet gebiet, ErgebnisDaten ergebnisDaten) {
    return ergebnisDaten.isAusgezaehlt(gebiet) && !ergebnisDaten.getLetztenGueltigenEingangFuerGebiet(gebiet).getErgebniseingangsherkunft().isFiktiv();
  }

  @Override
  Map<Gruppe, ReferendumFrage<Long>> extractReferendumFragenVonErgebnisDatenFuerGebiet(ErgebnisDaten ergebnisDatenAktuell, Wahldurchfuehrungsgebiet gebiet) {
    Map<Gruppe, ReferendumFrage<Long>> result = new LinkedHashMap<>();

    if (!isReferendumOptionStimmeingabenErfassen(gebiet)) {
      return result;
    }

    List<Gruppe> referendumVorlagenAufGebiet = ergebnisDatenAktuell.getReferendumVorlagenSortiertNachPositionAufGebiet(gebiet);

    ErgebnisDatenReferendumOptionen ergebnisDatenReferendumOptionen = ergebnisDatenAktuell.getErgebnisDatenReferendumOptionen();

    referendumVorlagenAufGebiet.forEach(referendumVorlage -> {
      ReferendumFrage<Long> referendumFrage = getReferendumFrage(gebiet, referendumVorlage, ergebnisDatenReferendumOptionen);
      if (referendumFrage != null) {
        result.put(referendumVorlage, referendumFrage);
      }
    });

    return result;
  }

  private boolean isReferendumOptionStimmeingabenErfassen(Wahldurchfuehrungsgebiet gebiet) {
    Wahl wahl = gebiet.getWahl();
    return wahl != null && wahl.isReferendum();
  }

  private ReferendumFrage<Long> getReferendumFrage(Wahldurchfuehrungsgebiet gebiet, Gruppe referendumVorlage, ErgebnisDatenReferendumOptionen ergebnisDatenReferendumOptionen) {
    if (referendumVorlage == null || ergebnisDatenReferendumOptionen == null) {
      return null;
    }

    List<ReferendumOption> referendumOptionen = ergebnisDatenReferendumOptionen
        .getReferendumOptionenFuerReferendumVorlage(referendumVorlage);
    Map<ReferendumOption, Long> stimmenPerReferendumOption = new HashMap<>();
    referendumOptionen.forEach(referendumOption -> {
      if (referendumOption != null) {
        Long stimmenFuerReferendumOption = ergebnisDatenReferendumOptionen.getStimmenFuerReferendumOption(referendumOption, gebiet);
        stimmenPerReferendumOption.put(referendumOption, stimmenFuerReferendumOption);
      }
    });
    Long stimmenFuerReferendumOptionenGueltig = ergebnisDatenReferendumOptionen.getStimmenFuerReferendumOptionenGueltig(referendumVorlage, gebiet);
    Long stimmenFuerReferendumOptionenUngueltig = ergebnisDatenReferendumOptionen.getStimmenFuerReferendumOptionenUngueltig(referendumVorlage, gebiet);
    return new ReferendumFrage<>(stimmenFuerReferendumOptionenGueltig, stimmenFuerReferendumOptionenUngueltig,
        referendumVorlage, stimmenPerReferendumOption);
  }
}
