
package de.ivu.elect.business.sitzberechnung.control.wahlneutral;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import de.ivu.elect.business.ergebniskonflikt.entity.ErgebnisKonflikt;
import de.ivu.elect.business.ergebniskonflikt.entity.KandidaturenReihenfolgeErgebnisKonfliktCustomData;
import de.ivu.elect.business.kandidatur.entity.Direktkandidatur;
import de.ivu.elect.business.person.entity.PersonendatenPK;
import de.ivu.elect.business.sitzberechnung.entity.Sitzberechnungseingabe;
import de.ivu.elect.jee.entity.customdata.CustomData;

public class DirektkandidaturNachAnzahlStimmenUndKonfliktloesungLosentscheidComparator
    implements
      Comparator<Direktkandidatur>,
      Serializable {

  private static final long serialVersionUID = 1L;

  private transient final Sitzberechnungseingabe< ? > sitzberechnungseingabe;
  private final List<ErgebnisKonflikt> konflikte;

  public <L extends LosentscheidTyp> DirektkandidaturNachAnzahlStimmenUndKonfliktloesungLosentscheidComparator(
      Sitzberechnungseingabe< ? > sitzberechnungseingabe, List<ErgebnisKonflikt> konflikte, L losentscheidTyp) {
    this(sitzberechnungseingabe, filterByLosentscheidTyp(konflikte, losentscheidTyp));
  }

  public DirektkandidaturNachAnzahlStimmenUndKonfliktloesungLosentscheidComparator(
      Sitzberechnungseingabe< ? > sitzberechnungseingabe, List<ErgebnisKonflikt> konflikte) {
    this.sitzberechnungseingabe = sitzberechnungseingabe;
    this.konflikte = konflikte;
  }

  /**
   * Filtert aus <code>konflikte</code> diejenigen heraus, die einen
   * {@link AbstractLosentscheidTypKandidaturReihenfolgeErgebnisKonfliktCustomData} haben und dessen
   * {@link LosentscheidTyp} mit <code>losentscheidTyp</code> übereinstimmt.
   * 
   * @param konflikte
   *          die ungefilterte Liste
   * @param losentscheidTyp
   *          das Filterkriterium
   * @return die gefilterte Liste
   */
  private static <L extends LosentscheidTyp> List<ErgebnisKonflikt> filterByLosentscheidTyp(
      List<ErgebnisKonflikt> konflikte, L losentscheidTyp) {
    List<ErgebnisKonflikt> result = new ArrayList<>();

    for (ErgebnisKonflikt konflikt : konflikte) {
      CustomData customData = konflikt.getCustomData();
      if (customData instanceof AbstractLosentscheidTypKandidaturReihenfolgeErgebnisKonfliktCustomData) {
        @SuppressWarnings("unchecked")
        AbstractLosentscheidTypKandidaturReihenfolgeErgebnisKonfliktCustomData<L> cd = (AbstractLosentscheidTypKandidaturReihenfolgeErgebnisKonfliktCustomData<L>) customData;
        if (losentscheidTyp.equals(cd.getLosentscheidTyp())) {
          result.add(konflikt);
        }
      }
    }

    return result;
  }

  @Override
  public int compare(Direktkandidatur o1, Direktkandidatur o2) {
    long result = getAnzahlStimmen(o2) - getAnzahlStimmen(o1);
    if (result == 0) {
      result = vergleicheInKonflikten(o1, o2);
    }

    if (result == 0) {
      result = vergleichePk(o1, o2);
    }

    return (int) result;
  }

  private long vergleichePk(Direktkandidatur o1, Direktkandidatur o2) {
    return o1.getPk().compareTo(o2.getPk());
  }

  private long getAnzahlStimmen(Direktkandidatur direktkandidatur) {
    return sitzberechnungseingabe
        .getAnzahlStimmenFuerDirektkandidaturKumuliertBisGebietAufDemKandidaturAntritt(direktkandidatur);
  }

  private int vergleicheInKonflikten(Direktkandidatur o1, Direktkandidatur o2) {
    KandidaturenReihenfolgeErgebnisKonfliktCustomData customdata = sucheKonfliktCustomData(o1, o2);
    if (customdata == null) {
      return 0;
    }
    List<PersonendatenPK> gelosteReihenfolge = customdata.getGelosteKandidaturenPKs();
    return gelosteReihenfolge.indexOf(o1.getPk()) - gelosteReihenfolge.indexOf(o2.getPk());
  }

  private KandidaturenReihenfolgeErgebnisKonfliktCustomData sucheKonfliktCustomData(Direktkandidatur o1,
      Direktkandidatur o2) {
    for (ErgebnisKonflikt ergebnisKonflikt : konflikte) {
      KandidaturenReihenfolgeErgebnisKonfliktCustomData customdata = ergebnisKonflikt
          .getCustomData(AbstractLosentscheidTypKandidaturReihenfolgeErgebnisKonfliktCustomData.class);
      List<PersonendatenPK> pksZurAuswahl = customdata.getBeteiligteKandidaturenPKs();
      if (pksZurAuswahl.contains(o1.getPk()) && pksZurAuswahl.contains(o2.getPk())) {
        return customdata;
      }
    }

    return null;
  }

}
