
package de.ivu.elect.presentation.ergebnis.dokumente.gesamtergebnis.dhondt;

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

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.function.BiPredicate;

import de.ivu.document.generator.template.BaseDocument;
import de.ivu.document.generator.template.annotations.DocumentQueue;
import de.ivu.document.generator.template.annotations.DocumentRepeat;
import de.ivu.elect.business.ergebnis.entity.DHondtQuotient;

public class AbstractDHondtQuotientTabelleDocument extends BaseDocument {

  private static final long serialVersionUID = 1L;

  @DocumentQueue(value = "Header", elementClass = String.class)
  public final LinkedList<String> header;

  @DocumentQueue(value = "Sitze", elementClass = Long.class)
  public final LinkedList<Long> sitze;

  @DocumentRepeat(value = "Zeilen", elementClass = DHondtQuotientListenInGruppeDocument.class, itemName = "zeile")
  public final List<DHondtTableRow> zeilen;

  public AbstractDHondtQuotientTabelleDocument(List<String> header, List<Long> sitze, List<DHondtTableRow> zeilen) {
    super();

    this.header = new LinkedList<>(header);

    this.sitze = new LinkedList<>(sitze);

    this.zeilen = zeilen;
  }

  protected static <Q extends DHondtQuotient, E> List<DHondtTableRow> erzeugeZeilen(Collection<Q> dHondtQuotienten, List<E> spaltenEntities, BiPredicate<Q, E> spaltenEntityPredicate) {
    List<DHondtTableRow> result = new ArrayList<>();

    for (Long nenner : collectNennerDistinct(dHondtQuotienten)) {
      List<DHondtQuotient> dHondtQuotientenZeile = new ArrayList<>(spaltenEntities.size());

      for (E spaltenEntity : spaltenEntities) {
        for (Q dHondtQuotient : dHondtQuotienten) {
          if (dHondtQuotient.getNenner().equals(nenner) && spaltenEntityPredicate.test(dHondtQuotient, spaltenEntity)) {
            dHondtQuotientenZeile.add(dHondtQuotient);
          }
        }
      }

      DHondtTableRow dHondtTableRow = new DHondtTableRow(nenner, dHondtQuotientenZeile);
      result.add(dHondtTableRow);

      if (dHondtTableRow.rangGewonnen.isEmpty() || dHondtTableRow.rangGewonnen.stream().allMatch(value -> Objects.equals(value, DHondtTableRow.RANG_GEWONNEN_PLACEHOLDER))) {
        break; // ELECT-8842, kein Rang mehr vergeben in der Zeile -> weitere Zeilen irrelevant...
      }
    }

    return result;
  }

  private static List<Long> collectNennerDistinct(Collection< ? extends DHondtQuotient> dHondtQuotienten) {
    return dHondtQuotienten.stream() //
        .map(DHondtQuotient::getNenner) //
        .sorted() //
        .distinct().collect(toList());
  }
}
