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

import static de.ivu.elect.jee.entity.EntityUtil.castTo;

import java.util.*;
import java.util.Map.Entry;

import de.ivu.elect.business.ergebnis.control.ErgebnisDaten;
import de.ivu.elect.business.ergebnis.control.ErgebnisExportContext;
import de.ivu.elect.business.ergebnis.control.ErgebnisExportObject;
import de.ivu.elect.business.ergebnis.control.StimmenPool;
import de.ivu.elect.business.ergebnis.entity.Stimmen;
import de.ivu.elect.business.ergebnis.entity.Stimmenart;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Wahldurchfuehrungsgebiet;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppe.entity.SystemGruppe;

public abstract class AbstractEineStimmenCsvExportMapper extends AbstractStimmenCsvExportMapper<EineStimmenCsvDTO> {

  public AbstractEineStimmenCsvExportMapper() {
    super();
  }

  @Override
  protected Class<EineStimmenCsvDTO> getStimmenCsvDTOClass() {
    return EineStimmenCsvDTO.class;
  }

  protected abstract Stimmenart getStimmenart();

  @Override
  protected List<EineStimmenCsvDTO> mappeEntityInternToExtern(ErgebnisExportObject exportObject, ErgebnisExportContext context) {
    List<EineStimmenCsvDTO> result = new ArrayList<>();

    ErgebnisDaten ergebnisDaten = exportObject.getErgebnisDaten();

    StimmenPool stimmenPool = ergebnisDaten.getStimmen();
    List<Stimmen> stimmen = stimmenPool.getSystemgruppenStimmen();

    List<Gruppe> gruppenSortiertNachPositionAufWurzelgebiet = ergebnisDaten.getGruppenSortiertNachPositionAufWurzelgebiet();
    for (Gruppe gruppe : gruppenSortiertNachPositionAufWurzelgebiet) {
      // ELECTMUC-752
      //
      // Gültige und ungültige Stimmen wurden doppelt gezählt da daten.getGruppen()
      // nicht nur politische Gruppen zurückgibt.
      //

      if (gruppe.isPolitischeGruppe()) {
        stimmen.addAll(stimmenPool.getDirektstimmen(gruppe));
        stimmen.addAll(stimmenPool.getListenstimmenDerGruppe(gruppe));
      }
    }

    Map<Gebiet, Map<Gruppe, List<Stimmen>>> stimmenByGebietUndGruppe = aggregateStimmenByGebietUndGruppe(stimmen);

    for (Gebiet gebiet : getSortedGebiete(stimmenByGebietUndGruppe)) {
      Map<Gruppe, List<Stimmen>> entry = stimmenByGebietUndGruppe.get(gebiet);

      Wahldurchfuehrungsgebiet wahldurchfuehrungsgebiet = castTo(gebiet, Wahldurchfuehrungsgebiet.class);

      EineStimmenCsvDTO dto = new EineStimmenCsvDTO();
      mapGebietAndKopfzahlen(dto, ergebnisDaten, wahldurchfuehrungsgebiet);

      dto.setPolitischeGruppenStimmen(new ArrayList<EineGruppeStimmenDTO>());

      for (Entry<Gruppe, List<Stimmen>> entryPerGruppe : sortiereEntrySetNachPositionAufWurzelgebiet(gruppenSortiertNachPositionAufWurzelgebiet, entry.entrySet())) {
        Gruppe gruppe = entryPerGruppe.getKey();
        List<Stimmen> stimmenDerGruppeAufDemGebiet = entryPerGruppe.getValue();

        if (systemGruppeEigenschaften.isGruppeEqualsSystemGruppe(gruppe, SystemGruppe.GUELTIGE)) {
          addGueltigeStimmenToCsvDTO(dto, stimmenDerGruppeAufDemGebiet);
        } else if (systemGruppeEigenschaften.isGruppeEqualsSystemGruppe(gruppe, SystemGruppe.UNGUELTIGE)) {
          addUngueltigeStimmenToCsvDTO(dto, stimmenDerGruppeAufDemGebiet);
        } else if (gruppe.isPolitischeGruppe()) { // ELECT-8668
          addPolitischeGruppeStimmenToCsvDTO(dto, gruppe, stimmenDerGruppeAufDemGebiet);
        }
      }

      result.add(dto);
    }

    return result;
  }

  private List<Entry<Gruppe, List<Stimmen>>> sortiereEntrySetNachPositionAufWurzelgebiet(List<Gruppe> gruppenSortiertNachPositionAufWurzelgebiet, Set<Entry<Gruppe, List<Stimmen>>> entrySet) {
    List<Entry<Gruppe, List<Stimmen>>> entriesSortiert = new ArrayList<>(entrySet);
    entriesSortiert.sort(Comparator.comparingInt(entry -> gruppenSortiertNachPositionAufWurzelgebiet.indexOf(entry.getKey())));
    return entriesSortiert;
  }

  private void addGueltigeStimmenToCsvDTO(EineStimmenCsvDTO dto, List<Stimmen> stimmenDerGruppeAufDemGebiet) {
    dto.setAnzahlStimmenGueltig(summiereAnzahlFuerStimmart(stimmenDerGruppeAufDemGebiet, getStimmenart()));
  }

  private void addUngueltigeStimmenToCsvDTO(EineStimmenCsvDTO dto, List<Stimmen> stimmenDerGruppeAufDemGebiet) {
    dto.setAnzahlStimmenUngueltig(summiereAnzahlFuerStimmart(stimmenDerGruppeAufDemGebiet, getStimmenart()));
  }

  private void addPolitischeGruppeStimmenToCsvDTO(EineStimmenCsvDTO dto, Gruppe gruppe, List<Stimmen> stimmenDerGruppeAufDemGebiet) {
    EineGruppeStimmenDTO politischeGruppeStimmen = new EineGruppeStimmenDTO();
    politischeGruppeStimmen.setGruppeSchluessel(gruppe.getSchluessel());
    politischeGruppeStimmen.setAnzahlStimmen(summiereAnzahlFuerStimmart(stimmenDerGruppeAufDemGebiet, getStimmenart()));
    dto.getPolitischeGruppenStimmen().add(politischeGruppeStimmen);
  }
}
