
package de.ivu.elect.dokumente.gebiet;

import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

import java.util.function.Predicate;

import javax.annotation.Nullable;

import de.ivu.document.generator.template.annotations.DocumentComplexField;
import de.ivu.document.generator.template.annotations.DocumentMethod;
import de.ivu.document.generator.template.annotations.DocumentParam;
import de.ivu.document.generator.template.annotations.DocumentSimpleField;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.dokumente.AnschriftDokument;
import de.ivu.elect.dokumente.BaseElectContext;
import de.ivu.elect.dokumente.ParameterKey;

public class GebietDokument extends BasicGebietDokument {

  private static final long serialVersionUID = 1L;

  public static final GebietDokument EMPTY_OBJECT = new GebietDokument();

  private final Gebiet gebiet;

  @DocumentSimpleField("AnzahlErforderlicherUnterstützer")
  public final Long anzahlErforderlicherUnterstuetzer;

  @DocumentSimpleField("AnzahlStimmenProWaehler")
  public final Long anzahlStimmenProWaehler;

  @DocumentSimpleField("AnzahlSitze")
  public final Long anzahlSitze;

  @DocumentSimpleField("IstErfassungsgebiet")
  public final boolean erfassungsgebiet;

  @DocumentSimpleField("IstUrnenwahlgebiet")
  public final boolean urnenwahlgebiet;

  @DocumentSimpleField("IstBriefwahlgebiet")
  public final boolean briefwahlgebiet;

  @DocumentSimpleField("IstBriefwahlInUrneErfassen")
  public final boolean briefwahlInUrneErfassen;

  @DocumentSimpleField("IstSonderstimmbezirk")
  public final boolean sonderstimmbezirk;

  @DocumentSimpleField("IstAuswahlbezirk")
  public final boolean auswahlbezirk;

  @DocumentSimpleField("IstBeweglicherWahlvorstand")
  public final boolean beweglicherWahlvorstand;

  @DocumentSimpleField("IstUmfragebezirk")
  public final boolean umfragebezirk;

  @DocumentSimpleField("IstWahlbeteiligungsbezirk")
  public final boolean wahlbeteiligungsbezirk;

  @DocumentSimpleField("IstListenwahlgebiet")
  public final boolean listenwahlgebiet;

  @DocumentSimpleField("IstDirektwahlgebiet")
  public final boolean direktwahlgebiet;

  @DocumentSimpleField("IstWahlgebiet")
  public final boolean wahlgebiet;

  @DocumentComplexField("Anschrift")
  public final AnschriftDokument anschrift;

  protected GebietDokument() {
    super();

    this.gebiet = null;

    this.anzahlErforderlicherUnterstuetzer = null;
    this.erfassungsgebiet = false;
    this.urnenwahlgebiet = false;
    this.briefwahlgebiet = false;
    this.briefwahlInUrneErfassen = false;
    this.sonderstimmbezirk = false;
    this.auswahlbezirk = false;
    this.beweglicherWahlvorstand = false;
    this.umfragebezirk = false;
    this.wahlbeteiligungsbezirk = false;
    this.anzahlStimmenProWaehler = null;
    this.anzahlSitze = null;
    this.listenwahlgebiet = false;
    this.direktwahlgebiet = false;
    this.wahlgebiet = false;
    this.anschrift = AnschriftDokument.EMPTY_OBJECT;
  }

  public GebietDokument(BaseElectContext baseElectContext, Gebiet gebiet) {
    super(baseElectContext, gebiet);

    this.gebiet = gebiet;

    this.anzahlErforderlicherUnterstuetzer = gebiet.getAnzahlErforderlicherUnterstuetzer();
    this.erfassungsgebiet = gebiet.isErfassungsgebiet();
    this.urnenwahlgebiet = gebiet.isUrnenwahlgebiet();
    this.briefwahlgebiet = gebiet.isBriefwahlgebiet();
    this.briefwahlInUrneErfassen = Boolean.TRUE.equals(gebiet.getBriefwahlInUrneErfassen());
    this.sonderstimmbezirk = Boolean.TRUE.equals(gebiet.getSonderstimmbezirk());
    this.auswahlbezirk = Boolean.TRUE.equals(gebiet.getAuswahlbezirk());
    this.beweglicherWahlvorstand = Boolean.TRUE.equals(gebiet.getBeweglicherWahlvorstand());
    this.umfragebezirk = Boolean.TRUE.equals(gebiet.getUmfragebezirk());
    this.wahlbeteiligungsbezirk = Boolean.TRUE.equals(gebiet.getWahlbeteiligungsbezirk());
    this.anzahlStimmenProWaehler = baseElectContext.getAnzahlStimmenProWaehlerMaximal(gebiet); // ELECT-9115
    this.anzahlSitze = gebiet.getAnzahlSitzeImWahlgebiet(); // ELECT-10660
    this.listenwahlgebiet = gebiet.isListenwahlgebiet();
    this.direktwahlgebiet = gebiet.isDirektwahlgebiet();
    this.wahlgebiet = gebiet.isWahlgebiet();
    this.anschrift = new AnschriftDokument(baseElectContext, gebiet.getAnschrift());
  }

  public GebietDokument(BaseElectContext baseElectContext, String name, String nummer) {
    super(baseElectContext, name, nummer);

    this.gebiet = null;

    this.anzahlErforderlicherUnterstuetzer = null;
    this.erfassungsgebiet = false;
    this.urnenwahlgebiet = false;
    this.briefwahlgebiet = false;
    this.briefwahlInUrneErfassen = false;
    this.sonderstimmbezirk = false;
    this.auswahlbezirk = false;
    this.beweglicherWahlvorstand = false;
    this.umfragebezirk = false;
    this.wahlbeteiligungsbezirk = false;
    this.anzahlStimmenProWaehler = null;
    this.anzahlSitze = null;
    this.listenwahlgebiet = false;
    this.direktwahlgebiet = false;
    this.wahlgebiet = false;
    this.anschrift = AnschriftDokument.EMPTY_OBJECT;
  }

  public boolean isEmptyObject() {
    return this == EMPTY_OBJECT;
  }

  public boolean istUntergeordnetZuGebietMitKuerzel() {
    return false;
  }

  @DocumentMethod
  public boolean istUntergeordnetZuGebietMitExterneId(@DocumentParam("\"externeId\"") String externeId) {
    return istUntergeordnetZuGebietMitExterneIdUebergeordneteEbenenMaximal(Integer.MAX_VALUE, externeId);
  }

  @DocumentMethod
  public boolean istUntergeordnetZuGebietMitExterneIdUebergeordneteEbenenMaximal(@DocumentParam("\"uebergeordneteEbenenMaximal\"") int uebergeordneteEbenenMaximal,
      @DocumentParam("\"externeId\"") String externeId) {
    return isNotBlank(externeId) && istUntergeordnetZuGebietAufEbeneMaximalMit(uebergeordneteEbenenMaximal, uebergeordnetesGebiet -> equalsIgnoreCase(uebergeordnetesGebiet.getExterneId(), externeId));
  }

  @DocumentMethod
  public boolean istUntergeordnetZuGebietMitName(@DocumentParam("\"name\"") String name) {
    return istUntergeordnetZuGebietMitNameUebergeordneteEbenenMaximal(Integer.MAX_VALUE, name);
  }

  @DocumentMethod
  public boolean istUntergeordnetZuGebietMitNameUebergeordneteEbenenMaximal(@DocumentParam("\"uebergeordneteEbenenMaximal\"") int uebergeordneteEbenenMaximal, @DocumentParam("\"name\"") String name) {
    return istUntergeordnetZuGebietAufEbeneMaximalMit(uebergeordneteEbenenMaximal, uebergeordnetesGebiet -> equalsIgnoreCase(uebergeordnetesGebiet.getName(), name));
  }

  @DocumentMethod
  public boolean istUntergeordnetZuGebietMitNummer(@DocumentParam("\"nummer\"") String nummer) {
    return istUntergeordnetZuGebietMitNummerUebergeordneteEbenenMaximal(Integer.MAX_VALUE, nummer);
  }

  @DocumentMethod
  public boolean istUntergeordnetZuGebietMitNummerUebergeordneteEbenenMaximal(@DocumentParam("\"uebergeordneteEbenenMaximal\"") int uebergeordneteEbenenMaximal,
      @DocumentParam("\"nummer\"") String nummer) {
    return isNotBlank(nummer) && istUntergeordnetZuGebietAufEbeneMaximalMit(uebergeordneteEbenenMaximal, uebergeordnetesGebiet -> equalsIgnoreCase(uebergeordnetesGebiet.getNummer(), nummer));
  }

  @DocumentMethod
  public boolean istUntergeordnetZuGebietMitKuerzel(@DocumentParam("\"kuerzel\"") String kuerzel) {
    return istUntergeordnetZuGebietMitKuerzelUebergeordneteEbenenMaximal(Integer.MAX_VALUE, kuerzel);
  }

  @DocumentMethod
  public boolean istUntergeordnetZuGebietMitKuerzelUebergeordneteEbenenMaximal(@DocumentParam("\"uebergeordneteEbenenMaximal\"") int uebergeordneteEbenenMaximal,
      @DocumentParam("\"kuerzel\"") String kuerzel) {
    return isNotBlank(kuerzel) && istUntergeordnetZuGebietAufEbeneMaximalMit(uebergeordneteEbenenMaximal, uebergeordnetesGebiet -> equalsIgnoreCase(uebergeordnetesGebiet.getKuerzel(), kuerzel));
  }

  private boolean istUntergeordnetZuGebietAufEbeneMaximalMit(int uebergeordneteEbenenMaximal, Predicate<Gebiet> uebergeordnetesGebietPredicate) {
    int uebergeordneteEbenen = 0;
    Gebiet uebergeordnetesGebiet = gebiet;

    while (uebergeordnetesGebiet != null && uebergeordnetesGebiet.getUebergeordnetesGebiet() != null && uebergeordneteEbenen < uebergeordneteEbenenMaximal) {
      uebergeordneteEbenen++;
      uebergeordnetesGebiet = uebergeordnetesGebiet.getUebergeordnetesGebiet();

      if (uebergeordnetesGebietPredicate.test(uebergeordnetesGebiet)) {
        return true;
      }
    }

    return false;
  }

  public static GebietDokument lookupOrCreate(BaseElectContext baseElectContext, Gebiet gebiet) {
    GebietDokument result = baseElectContext.lookupCachedDokument(GebietDokument.class, new ParameterKey(gebiet));

    if (result == null) {
      result = new GebietDokument(baseElectContext, gebiet);

      baseElectContext.cacheDokument(GebietDokument.class, new ParameterKey(gebiet), result);
    }

    return result;
  }

  public Gebiet getGebiet() {
    return gebiet;
  }

  public boolean equalsGebiet(Gebiet gebiet) {
    return this.gebiet == gebiet || this.gebiet != null && this.gebiet.equals(gebiet);
  }

  // ELECTKOM-517
  protected Long sum(@Nullable Long one, @Nullable Long another) {
    if (one == null || another == null) {
      if (one == null) {
        return another;
      } else {
        return one;
      }
    }

    return one + another;
  }

  protected Long fixNullToZero(Long value) {
    if (value == null) {
      return 0L;
    }

    return value;
  }
}
