
package de.ivu.elect.was.sitzberechnung.entity;

import static de.ivu.elect.jee.entity.EntityUtil.castTo;
import static java.util.Collections.emptyList;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.stimmzettel.entity.EntityAufStimmzettelDaten;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.sitzberechnung.Kandidatur;

public class ElectSitzberechnungListe extends AbstractElectSitzberechnungEntity<Liste> implements de.ivu.elect.sitzberechnung.Liste {

  private final EntityAufStimmzettelDaten entityAufStimmzettelDaten;

  private final de.ivu.elect.sitzberechnung.Gebiet gebiet;

  private final de.ivu.elect.sitzberechnung.Gruppe gruppe;

  private final de.ivu.elect.sitzberechnung.Listenverbindung listenverbindung;

  private final Collection<de.ivu.elect.sitzberechnung.Kandidatur> kandidaturen;

  private final Map<de.ivu.elect.sitzberechnung.Kandidatur, Long> positionPerKandidatur;

  private final Map<de.ivu.elect.sitzberechnung.Kandidatur, Collection<de.ivu.elect.sitzberechnung.Kandidatur>> ersatzkandidaturenPerKandidatur;

  public ElectSitzberechnungListe(Liste liste, EntityAufStimmzettelDaten entityAufStimmzettelDaten, de.ivu.elect.sitzberechnung.Gebiet gebiet, de.ivu.elect.sitzberechnung.Gruppe gruppe,
      de.ivu.elect.sitzberechnung.Listenverbindung listenverbindung) {
    super(liste);

    this.entityAufStimmzettelDaten = entityAufStimmzettelDaten;

    this.gebiet = gebiet;
    this.gruppe = gruppe;

    this.listenverbindung = listenverbindung;

    this.kandidaturen = new HashSet<>();

    this.positionPerKandidatur = new HashMap<>();

    this.ersatzkandidaturenPerKandidatur = new HashMap<>();
  }

  @Override
  public de.ivu.elect.sitzberechnung.Gebiet getGebiet() {
    return gebiet;
  }

  @Override
  public de.ivu.elect.sitzberechnung.Gruppe getGruppe() {
    return gruppe;
  }

  @Override
  public de.ivu.elect.sitzberechnung.Listenverbindung getListenverbindung() {
    return listenverbindung;
  }

  @Override
  public Collection<de.ivu.elect.sitzberechnung.Kandidatur> getListenkandidaturen() {
    return kandidaturen;
  }

  public void addKandidatur(de.ivu.elect.sitzberechnung.Kandidatur kandidatur) {
    kandidaturen.add(kandidatur);
  }

  @Override
  public long getPositionInListe(de.ivu.elect.sitzberechnung.Kandidatur kandidatur) {
    return positionPerKandidatur.computeIfAbsent(kandidatur, ignored -> {
      if (kandidatur instanceof ElectSitzberechnungErsatzkandidatur) {
        de.ivu.elect.business.kandidatur.entity.Kandidatur uebergeordneteKandidatur = ((ElectSitzberechnungErsatzkandidatur) kandidatur).getEntity().getUebergeordneteKandidatur();

        if (uebergeordneteKandidatur != null && uebergeordneteKandidatur.isInstanceOf(Listenkandidatur.class)) {
          return getPositionInListe(castTo(uebergeordneteKandidatur, Listenkandidatur.class));
        }
      }

      if (kandidatur instanceof ElectSitzberechnungListenkandidatur) {
        return getPositionInListe(((ElectSitzberechnungListenkandidatur) kandidatur).getEntity());
      }

      return kandidaturen.size() + 1L;
    });
  }

  private long getPositionInListe(Listenkandidatur listenkandidatur) {
    Long result = listenkandidatur.getStimmzettelPosition(entityAufStimmzettelDaten).getPosition();

    if (result != null) {
      return result;
    }

    return kandidaturen.size() + 1L;
  }

  @Override
  public Collection<Kandidatur> getErsatzkandidaturen(Kandidatur kandidatur) {
    if (kandidatur instanceof AbstractElectSitzberechnungKandidatur) {
      return ersatzkandidaturenPerKandidatur.computeIfAbsent(kandidatur, ignored -> ((AbstractElectSitzberechnungKandidatur) kandidatur).getErsatzkandidaturen());
    }

    return emptyList();
  }
}
