
package de.ivu.elect.sitzberechnung.verteilung.saintelague;

import de.ivu.elect.sitzberechnung.math.Fraction;
import de.ivu.elect.sitzberechnung.math.FractionConverter;
import de.ivu.elect.sitzberechnung.verteilung.AbstractTeilnehmerQuotient;
import de.ivu.elect.sitzberechnung.verteilung.dhondt.DHondtQuotient;

/**
 * Höchstzahl bei der Verteilung nach Sainte-Laguë für einen Teilnehmer berechnet aus dessen Anzahl Stimmen und einem GANZZAHLIGEN Teiler aus der Folge +2 -> 1 – 3 – 5 – 7 – (2n−1). Die ganzzahligen
 * Teiler sind äquivalent zu den Teilern aus der Folge +1 -> 0,5 – 1,5 – 2,5 – 3,5 – (n−0,5), bieten aber bspw. den Vorteil, dass sich ganzzahlige Teiler als (Nenner der Entität DHondtQuotient)
 * speichern lassen.
 */
public class SainteLagueHoechstzahl extends AbstractTeilnehmerQuotient<SainteLagueHoechstzahlTeilnehmerErgebnis> {

  private final int teilerIndexZeroBased;

  private long rang;

  private boolean rangSitzrelevant;

  private boolean rangImLosverfahren;

  /**
   * @param teilnehmerErgebnis
   * @param teilerIndexZeroBased
   */
  SainteLagueHoechstzahl(SainteLagueHoechstzahlTeilnehmerErgebnis teilnehmerErgebnis, int teilerIndexZeroBased) {
    super(teilnehmerErgebnis, teilnehmerErgebnis.getStimmen(), getTeilerAusFolgePlusZwei(teilerIndexZeroBased));

    this.teilerIndexZeroBased = teilerIndexZeroBased;
  }

  private int getTeilerIndexZeroBased() {
    return teilerIndexZeroBased;
  }

  long getRang() {
    return rang;
  }

  void setRang(long rang) {
    this.rang = rang;
  }

  boolean isRangSitzrelevant() {
    return rangSitzrelevant;
  }

  void setRangSitzrelevant(boolean rangSitzrelevant) {
    this.rangSitzrelevant = rangSitzrelevant;
  }

  boolean isRangImLosverfahren() {
    return rangImLosverfahren;
  }

  void setRangImLosverfahren(boolean rangImLosverfahren) {
    this.rangImLosverfahren = rangImLosverfahren;
  }

  /**
   * @return Fraction aus Anzahl Stimmen und einem Teiler aus der Folge +1 -> 0,5 – 1,5 – 2,5 – 3,5 – (n−0,5)...
   */
  public Fraction toFractionMitTeilerAusFolgePlusEins(FractionConverter fractionConverter) {
    return toFractionMitTeilerAusFolge(fractionConverter, getTeilerAusFolgePlusEins());
  }

  /**
   * @return Fraction des Teilers aus der Folge +1 -> 0,5 – 1,5 – 2,5 – 3,5 – (n−0,5)...
   */
  public Fraction getTeilerAusFolgePlusEins() {
    return Fraction.getFraction(getTeilerAusFolgePlusEins(getTeilerIndexZeroBased()));
  }

  public static double getTeilerAusFolgePlusEins(int teilerIndexZeroBased) {
    return teilerIndexZeroBased + 0.5D;
  }

  /**
   * @return Fraction aus Anzahl Stimmen und einem Teiler aus der Folge +2 -> 1 – 3 – 5 – 7 – (2n−1)...
   */
  public Fraction toFractionMitTeilerAusFolgePlusZwei(FractionConverter fractionConverter) {
    return toFractionMitTeilerAusFolge(fractionConverter, getTeilerAusFolgePlusZwei());
  }

  /**
   * @return Fraction des Teilers aus der Folge +2 -> 1 – 3 – 5 – 7 – (2n−1)...
   */
  public Fraction getTeilerAusFolgePlusZwei() {
    return Fraction.getFraction(getTeilerAusFolgePlusZwei(getTeilerIndexZeroBased()));
  }

  public static long getTeilerAusFolgePlusZwei(int teilerIndexZeroBased) {
    return 2L * teilerIndexZeroBased + 1L;
  }

  private Fraction toFractionMitTeilerAusFolge(FractionConverter fractionConverter, Fraction teilerAusFolge) {
    return fractionConverter.asFractionConverted(getZaehler(), teilerAusFolge);
  }

  /**
   * @return DHondtQuotient aus Anzahl Stimmen und einem GANZZAHLIGEN Teiler aus der Folge +2 -> 1 – 3 – 5 – 7 – (2n−1)...
   */
  public DHondtQuotient toDHondtQuotient() {
    boolean isSitzgewinn = isRangSitzrelevant() && (!isRangImLosverfahren() || getTeilnehmerErgebnis().getRestsitzeAusLosverfahren() > 0);

    DHondtQuotient dHondtQuotient = new DHondtQuotient(getTeilnehmerErgebnis(), getZaehler(), getNenner(), isSitzgewinn, isRangImLosverfahren());
    dHondtQuotient.setRang(getRang());

    return dHondtQuotient;
  }
}
