
package de.ivu.elect.core.exchange.stimmen.csv.dynamic;

import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_AUSZAEHLBEZIRK_GEMEINDE;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_AUSZAEHLBEZIRK_STIMMBEZIRKSNUMMER;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_BRIEFWAEHLER_MIT_WAHLSCHEIN_IN_URNENWAHLLOKAL;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_DIREKTSTIMMEN_GUELTIGE;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_DIREKTSTIMMEN_UNGUELTIGE;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_ERFASSUNGSGEBIETSART;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_ERFASSUNGSGEBIETS_NUMMER;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_ERFASSUNG_NICHT_ERFASST;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_GEMEINDE;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_INDEX_DIREKTSTIMMEN;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_INDEX_GUELTIGE_REFERENDUMSTIMMEN;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_INDEX_LISTENSTIMMEN;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_INDEX_NUR_STIMMEN;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_INDEX_UNGUELTIGE_REFERENDUMSTIMMEN;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_LISTENSTIMMEN_GUELTIGE;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_LISTENSTIMMEN_UNGUELTIGE;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_SEPARATOR_LISTENKANDIDATURSTIMMEN;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_SEPARATOR_REFEREUNDUMSTIMMEN;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_STIMMBEZIRKSART;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_STIMMBEZIRKSNUMMER;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_STIMMEN_GUELTIGE;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_STIMMEN_UNGUELTIGE;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_SUFFIX_LISTENSTIMMEN_PANASCHIERT;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_SUFFIX_LISTENSTIMMEN_UNVERAENDERT;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_SUFFIX_LISTENSTIMMEN_VERAENDERT;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_URNENWAEHLER_MIT_WAHLSCHEIN_IN_URNENWAHLLOKAL;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_WAEHLER_GESAMT;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_WAEHLER_MIT_WAHLSCHEIN;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_WAEHLER_MIT_WAHLSCHEIN_LEGACY;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_WAEHLER_OHNE_WAHLSCHEIN;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_WAHLBERECHTIGTE_GESAMT;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_WAHLBERECHTIGTE_MIT_WAHLSCHEIN;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_WAHLBERECHTIGTE_NICHT_IM_WVZ;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_WAHLBERECHTIGTE_OHNE_WAHLSCHEIN;
import static de.ivu.elect.core.exchange.stimmen.csv.dynamic.DefaultDynamicStimmenExportDTOFactory.HEADER_WAHLBRIEFE_ZURUECKGEWIESEN;
import static org.apache.commons.lang3.StringUtils.isBlank;

import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;

import javax.annotation.Nullable;

import org.apache.commons.lang3.math.NumberUtils;

import de.ivu.elect.business.gebietsbaum.boundary.WahlverwaltungGebietInformationResolver;
import de.ivu.elect.business.gebietsbaum.entity.GebietsartBezirksart;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.stimmeingabe.control.ListenwahlStimmeingabeKonfiguration;
import de.ivu.elect.business.wahl.entity.Wahldurchfuehrung;

public class DefaultDynamicStimmenImportDTOParser extends AbstractDynamicStimmenImportDTOParser {

  private final WahlverwaltungGebietInformationResolver wahlverwaltungGebietInformationResolver;

  public DefaultDynamicStimmenImportDTOParser(Wahldurchfuehrung wahldurchfuehrung, WahlverwaltungGebietInformationResolver wahlverwaltungGebietInformationResolver) {
    super(wahldurchfuehrung);

    this.wahlverwaltungGebietInformationResolver = wahlverwaltungGebietInformationResolver;
  }

  @Override
  public Optional<Boolean> isBriefwahlgebiet(DynamicStimmenSource source) {
    String value = source.getValue(HEADER_ERFASSUNGSGEBIETSART, HEADER_STIMMBEZIRKSART);

    if (GebietsartBezirksart.BRIEFWAHLBEZIRK.name().equalsIgnoreCase(value)) {
      return Optional.of(true);
    } else if (GebietsartBezirksart.STIMMBEZIRK.name().equalsIgnoreCase(value)) {
      return Optional.of(false);
    } else {
      return Optional.empty();
    }
  }

  @Override
  public String getErfassungsgebietNummer(DynamicStimmenSource source) {
    return source.getValue(HEADER_ERFASSUNGSGEBIETS_NUMMER, HEADER_STIMMBEZIRKSNUMMER);
  }

  @Override
  public String getErfassungsgebietName(DynamicStimmenSource source) {
    return getErfassungsgebietNummer(source);//source.getValue(HEADER_STIMMBEZIRKSNUMMER);
  }

  @Override
  public String getAnzahlWahlberechtigteGesamt(DynamicStimmenSource source) {
    return source.getValue(HEADER_WAHLBERECHTIGTE_GESAMT);
  }

  @Override
  public String getAnzahlWahlberechtigteMitWahlschein(DynamicStimmenSource source) {
    return source.getValue(HEADER_WAHLBERECHTIGTE_MIT_WAHLSCHEIN);
  }

  @Override
  public String getAnzahlWahlberechtigteOhneWahlschein(DynamicStimmenSource source) {
    return source.getValue(HEADER_WAHLBERECHTIGTE_OHNE_WAHLSCHEIN);
  }

  @Override
  public String getAnzahlWahlberechtigteNichtImWaehlerverzeichnis(DynamicStimmenSource source) {
    return source.getValue(HEADER_WAHLBERECHTIGTE_NICHT_IM_WVZ);
  }

  @Override
  public String getAnzahlWaehlerOhneWahlschein(DynamicStimmenSource source) {
    String result = source.getValue(HEADER_WAEHLER_OHNE_WAHLSCHEIN);

    if (isBlank(result)) {
      int waehlerOhneWahlschein = NumberUtils.toInt(source.getValue(HEADER_WAEHLER_GESAMT), 0)
          - NumberUtils.toInt(source.getValue(HEADER_WAEHLER_MIT_WAHLSCHEIN, HEADER_WAEHLER_MIT_WAHLSCHEIN_LEGACY), 0);

      return String.valueOf(waehlerOhneWahlschein);
    }

    return result;
  }

  @Override
  public String getAnzahlWaehlerMitWahlschein(DynamicStimmenSource source) {
    return source.getValue(HEADER_WAEHLER_MIT_WAHLSCHEIN, HEADER_WAEHLER_MIT_WAHLSCHEIN_LEGACY);
  }

  @Override
  public String getUrnenwaehlerMitWahlscheinInUrnenwahllokal(DynamicStimmenSource source) {
    return source.getValue(HEADER_URNENWAEHLER_MIT_WAHLSCHEIN_IN_URNENWAHLLOKAL);
  }

  @Override
  public String getBriefwaehlerMitWahlscheinInUrnenwahllokal(DynamicStimmenSource source) {
    return source.getValue(HEADER_BRIEFWAEHLER_MIT_WAHLSCHEIN_IN_URNENWAHLLOKAL);
  }

  @Override
  public String getAnzahlWaehlerGesamt(DynamicStimmenSource source) {
    return source.getValue(HEADER_WAEHLER_GESAMT);
  }

  @Override
  public String getWahlbriefeZurueckgewiesen(DynamicStimmenSource source) {
    return source.getValue(HEADER_WAHLBRIEFE_ZURUECKGEWIESEN);
  }

  @Override
  public String getAnzahlDirektstimmenUngueltig(DynamicStimmenSource source) {
    if (source.isWahlNurDirektstimmen()) {
      return source.getValue(HEADER_STIMMEN_UNGUELTIGE);
    }

    return source.getValue(HEADER_DIREKTSTIMMEN_UNGUELTIGE);
  }

  @Override
  public String getAnzahlDirektstimmenGueltig(DynamicStimmenSource source) {
    if (source.isWahlNurDirektstimmen()) {
      return source.getValue(HEADER_STIMMEN_GUELTIGE);
    }

    return source.getValue(HEADER_DIREKTSTIMMEN_GUELTIGE);
  }

  @Override
  public String getAnzahlListenstimmenUngueltig(DynamicStimmenSource source) {
    if (source.isWahlNurListenstimmen()) {
      return source.getValue(HEADER_STIMMEN_UNGUELTIGE);
    }

    return source.getValue(HEADER_LISTENSTIMMEN_UNGUELTIGE);
  }

  @Override
  public String getAnzahlListenstimmenGueltig(DynamicStimmenSource source) {
    if (source.isWahlNurListenstimmen()) {
      return source.getValue(HEADER_STIMMEN_GUELTIGE);

    }
    return source.getValue(HEADER_LISTENSTIMMEN_GUELTIGE);
  }

  @Override
  public long getAnzahlParteien(DynamicStimmenSource source) {
    long result = 0;

    if (source.isWahlNurDirektstimmen() || source.isWahlNurListenstimmen()) {
      return findMax(result, source, HEADER_INDEX_NUR_STIMMEN);
    }

    if (source.getWahldurchfuehrung().isWahlMitDirektkandidatengebieten()) {
      result = findMax(result, source, HEADER_INDEX_DIREKTSTIMMEN);
    }

    if (source.getWahldurchfuehrung().isWahlMitListengebieten()) {
      result = findMax(result, source, HEADER_INDEX_LISTENSTIMMEN);
    }

    if (source.getWahldurchfuehrung().isReferendum()) {
      result = findMax(0, source, HEADER_INDEX_GUELTIGE_REFERENDUMSTIMMEN);
    }

    return result;
  }

  @Override
  public String getAnzahlDirektstimmen(int gruppenPositionEinsBasiert, DynamicStimmenSource source) {
    if (source.isWahlNurDirektstimmen()) {
      return source.getValue(HEADER_INDEX_NUR_STIMMEN + gruppenPositionEinsBasiert);
    }

    return source.getValue(HEADER_INDEX_DIREKTSTIMMEN + gruppenPositionEinsBasiert);
  }

  @Override
  public String getAnzahlListenstimmen(int gruppenPositionEinsBasiert, DynamicStimmenSource source) {
    return source.getValue(getHeaderIndexListenstimmen(source) + gruppenPositionEinsBasiert);
  }

  private String getHeaderIndexListenstimmen(DynamicStimmenSource source) {
    String headerIndexListenstimmen = HEADER_INDEX_LISTENSTIMMEN;
    if (source.isWahlNurListenstimmen()) {
      headerIndexListenstimmen = HEADER_INDEX_NUR_STIMMEN;
    }
    return headerIndexListenstimmen;
  }

  @Override
  public boolean isErfasst(DynamicStimmenSource source) {
    return true; // alle Gebiete in diesem Format sind erfasst, sonst würden sie nicht exportiert werden
  }

  @Override
  public boolean isAusbleibend(DynamicStimmenSource source) {
    return "j".equalsIgnoreCase(source.getValue(HEADER_ERFASSUNG_NICHT_ERFASST)); // ELECT-12508
  }

  @Nullable
  @Override
  public String getErfasstInErfassungsgebietNummer(DynamicStimmenSource source) {
    return source.getValue(HEADER_AUSZAEHLBEZIRK_STIMMBEZIRKSNUMMER);
  }

  @Override
  public String getGemeindeNummer(DynamicStimmenSource source) {
    return source.getValue(HEADER_GEMEINDE);
  }

  @Nullable
  @Override
  public String getErfasstInGemeindeNummer(DynamicStimmenSource source) {
    return source.getValue(HEADER_AUSZAEHLBEZIRK_GEMEINDE);
  }

  @Nullable
  @Override
  public String getErfasstInErfassungsgebietName(DynamicStimmenSource source) {
    return null;
  }

  @Nullable
  @Override
  public String getErfasstInErfassungsGebietsArt(DynamicStimmenSource source) {
    return null;
  }

  @Override
  public Optional<Boolean> isErfasstInBriefwahlgebiet(DynamicStimmenSource source) {
    return Optional.of(false); // Gebiete werden nicht in Briefwahlgebieten "fremd erfasst" TODO, siehe de.ivu.elect.core.exchange.stimmen.csv.voteit.VoteItKomStimmenImportDTOParser.isErfasstInBriefwahlgebiet
  }

  @Override
  public List<String> getCsvColumnHeaders(Wahldurchfuehrungsgebiet wurzelgebiet, Supplier<DynamicStimmenExchangeErgebnisDaten> ergebnisDaten,
      ListenwahlStimmeingabeKonfiguration listenwahlStimmeingabeKonfiguration) {
    return new DefaultDynamicStimmenExportDTOFactory(wurzelgebiet, ergebnisDaten.get(), listenwahlStimmeingabeKonfiguration, wahlverwaltungGebietInformationResolver, true).createHeader().get(0);
  }

  @Override
  public long getAnzahlListenkandidaturen(int gruppenposition, DynamicStimmenSource source) {
    if (source.isWahlMitListenkandidaturStimmen()) {
      return findMax(0L, source, getHeaderPrefixListenkandidatur(gruppenposition, source));
    }
    return 0L;
  }

  private String getHeaderPrefixListenkandidatur(int gruppenposition, DynamicStimmenSource source) {
    return getHeaderIndexListenstimmen(source) + gruppenposition + HEADER_SEPARATOR_LISTENKANDIDATURSTIMMEN;
  }

  @Override
  public String getAnzahlListenkandidaturstimmen(int gruppenPositionEinsBasiert, int listenkandidaturPositionEinsBasiert, DynamicStimmenSource source) {
    return source.getValue(getHeaderPrefixListenkandidatur(gruppenPositionEinsBasiert, source) + listenkandidaturPositionEinsBasiert);
  }

  @Override
  public String getAnzahlListenstimmenUnveraendert(int gruppenPositionEinsBasiert, DynamicStimmenSource source) {
    return source.getValue(getHeaderIndexListenstimmen(source) + gruppenPositionEinsBasiert + HEADER_SUFFIX_LISTENSTIMMEN_UNVERAENDERT);
  }

  @Override
  public String getAnzahlListenstimmenVeraendert(int gruppenPositionEinsBasiert, DynamicStimmenSource source) {
    return source.getValue(getHeaderIndexListenstimmen(source) + gruppenPositionEinsBasiert + HEADER_SUFFIX_LISTENSTIMMEN_VERAENDERT);
  }

  @Override
  public String getAnzahlListenstimmenPanaschiert(int gruppenPositionEinsBasiert, DynamicStimmenSource source) {
    return source.getValue(getHeaderIndexListenstimmen(source) + gruppenPositionEinsBasiert + HEADER_SUFFIX_LISTENSTIMMEN_PANASCHIERT);
  }

  @Override
  public String getAnzahlGueltigeVorlagenstimmen(int gruppenPositionEinsBasiert, DynamicStimmenSource source) {
    return source.getValue(HEADER_INDEX_GUELTIGE_REFERENDUMSTIMMEN + gruppenPositionEinsBasiert);
  }

  @Override
  public String getAnzahlUngueltigeVorlagenstimmen(int gruppenPositionEinsBasiert, DynamicStimmenSource source) {
    return source.getValue(HEADER_INDEX_UNGUELTIGE_REFERENDUMSTIMMEN + gruppenPositionEinsBasiert);
  }

  @Override
  public String getAnzahlReferendumOptionStimmen(int gruppenPosition, long referendumOptionPosition, DynamicStimmenSource source) {
    return source.getValue(getHeaderPrefixReferendumOption(gruppenPosition) + referendumOptionPosition);
  }

  @Override
  public Long getAnzahlReferendumOptionen(DynamicStimmenSource source, int gruppenPosition) {
    return findMax(0, source, getHeaderPrefixReferendumOption(gruppenPosition));
  }

  private String getHeaderPrefixReferendumOption(int gruppenposition) {
    return HEADER_INDEX_GUELTIGE_REFERENDUMSTIMMEN + gruppenposition + HEADER_SEPARATOR_REFEREUNDUMSTIMMEN;
  }

  @Override
  public String getErfassungsGebietsArt(DynamicStimmenSource source) {
    return source.getValue(HEADER_ERFASSUNGSGEBIETSART, HEADER_STIMMBEZIRKSART);
  }
}
