
package de.ivu.elect.was.exchange.ergebnis.zusammenstellung;

import static java.util.stream.Collectors.toSet;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;

import de.ivu.elect.business.ergebnis.entity.Stimmabgabeart;
import de.ivu.elect.business.gebietsbaum.boundary.WahlverwaltungGebietInformation;
import de.ivu.elect.business.gebietsbaum.boundary.WahlverwaltungGemeindeInformation;

public class ErgebnisZusammenstellungTabelleFilter {

  private final ErgebnisZusammenstellungTabelle tabelle;

  public ErgebnisZusammenstellungTabelleFilter(ErgebnisZusammenstellungTabelle tabelle) {
    super();

    this.tabelle = tabelle;
  }

  public ErgebnisZusammenstellungTabelle get() {
    return tabelle;
  }

  public ErgebnisZusammenstellungTabelleFilter filterByNurSummenzeile(String nummer, String name) {
    Collection<ErgebnisZusammenstellungZeile> zeilen = tabelle.getZeilen();

    Stimmabgabeart stimmabgabeart = Stimmabgabeart.GESAMT;
    Set<Stimmabgabeart> stimmabgabearten = zeilen.stream().map(ErgebnisZusammenstellungZeile::getStimmabgabeart).collect(toSet());

    if (stimmabgabearten.size() == 1) {
      stimmabgabeart = stimmabgabearten.iterator().next();
    }

    Optional<ErgebnisZusammenstellungZeile> summenzeile = ErgebnisZusammenstellungZeile.sum(ErgebnisZusammenstellungZeileDatengebiet.create(tabelle, nummer, name), stimmabgabeart, zeilen);

    return new ErgebnisZusammenstellungTabelleFilter(new ErgebnisZusammenstellungTabelle(tabelle, summenzeile.map(Collections::singletonList).orElse(new ArrayList<>())));
  }

  public ErgebnisZusammenstellungTabelleFilter filterByStimmabgabeart(Stimmabgabeart stimmabgabeart) {
    return new ErgebnisZusammenstellungTabelleFilter(filterZeilen(zeile -> {
      if (Objects.equals(zeile.getStimmabgabeart(), stimmabgabeart)) {
        return Optional.of(zeile);
      }

      return Optional.empty();
    }));
  }

  public ErgebnisZusammenstellungTabelleFilter filterByGruppe(ErgebnisZusammenstellungGruppe gruppe) {
    return new ErgebnisZusammenstellungTabelleFilter(filterZeilen(zeile -> {
      List<ErgebnisZusammenstellungListe> listen = new ArrayList<>();

      for (ErgebnisZusammenstellungListe liste : zeile.getListen()) {
        if (Objects.equals(liste.getGruppe(), gruppe)) {
          listen.add(liste);
        }
      }

      List<ErgebnisZusammenstellungDirektkandidatur> direktkandidaturen = new ArrayList<>();

      for (ErgebnisZusammenstellungDirektkandidatur direktkandidatur : zeile.getDirektkandidaturen()) {
        if (Objects.equals(direktkandidatur.getGruppe(), gruppe)) {
          direktkandidaturen.add(direktkandidatur);
        }
      }

      if (!direktkandidaturen.isEmpty() || !listen.isEmpty()) {
        return Optional.of(new ErgebnisZusammenstellungZeile(zeile, listen, direktkandidaturen));
      }

      return Optional.empty();
    }));
  }

  public ErgebnisZusammenstellungTabelleFilter filterByKreis(WahlverwaltungGebietInformation kreis) {
    return filterByGebiet(kreis, ErgebnisZusammenstellungZeile::getKreis);
  }

  public ErgebnisZusammenstellungTabelleFilter filterByGemeindeverband(WahlverwaltungGemeindeInformation gemeindeverband) {
    return filterByGebiet(gemeindeverband, ErgebnisZusammenstellungZeile::getGemeindeverband);
  }

  public ErgebnisZusammenstellungTabelleFilter filterByGemeinde(WahlverwaltungGemeindeInformation gemeinde) {
    return filterByGebiet(gemeinde, ErgebnisZusammenstellungZeile::getGemeinde);
  }

  private <T extends WahlverwaltungGebietInformation> ErgebnisZusammenstellungTabelleFilter filterByGebiet(T gebiet, Function<ErgebnisZusammenstellungZeile, T> zeileGebietFct) {
    return new ErgebnisZusammenstellungTabelleFilter(filterZeilen(zeile -> {
      if (Objects.equals(zeileGebietFct.apply(zeile), gebiet)) {
        return Optional.of(zeile);
      }

      return Optional.empty();
    }));
  }

  private ErgebnisZusammenstellungTabelle filterZeilen(Function<ErgebnisZusammenstellungZeile, Optional<ErgebnisZusammenstellungZeile>> zeileFilter) {
    List<ErgebnisZusammenstellungZeile> zeilen = new ArrayList<>();

    for (ErgebnisZusammenstellungZeile zeile : tabelle.getZeilen()) {
      zeileFilter.apply(zeile).ifPresent(zeilen::add);
    }

    return new ErgebnisZusammenstellungTabelle(tabelle, zeilen);
  }
}
