
package de.ivu.elect.was.exchange.ergebnis;

import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.isNumeric;
import static org.apache.commons.lang3.StringUtils.leftPad;
import static org.apache.commons.lang3.StringUtils.length;
import static org.apache.commons.lang3.StringUtils.overlay;
import static org.apache.commons.lang3.StringUtils.right;
import static org.apache.commons.lang3.StringUtils.trimToEmpty;

import java.io.Serializable;
import java.util.Optional;

import de.ivu.elect.business.gebietsbaum.boundary.WahlverwaltungGebietDescriptor;
import de.ivu.elect.business.gebietsbaum.boundary.WahlverwaltungGebietInformation;
import de.ivu.elect.business.gebietsbaum.boundary.WahlverwaltungGemeindeInformation;
import de.ivu.elect.business.gebietsbaum.entity.ByStimmbezirkArt;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;

/**
 * Bspw. AGS8 München (gesamt) = 09 1 62 000...
 * <ul>
 * <li>1-2 = Kennung Bundesland</li>
 * <li>3 = Kennung Regierungsbezirk</li>
 * <li>4-5 = Kennung Landkreis oder kreisfreie Stadt</li>
 * <li>6-8 = Kennung Gemeinde (000 bei kreisfreier Stadt)</li>
 * </ul>
 * ... die Stelle 3-5 werden bei München als Nummer der Gemeinde hinterlegt.
 */
public class ErgebnisExportGebietFormatter implements Serializable {

  private static final long serialVersionUID = 1L;

  public ErgebnisExportGebietFormatter() {
    super();
  }

  /**
   * Bspw. Gebietsreferenz München, Wahlkreis 221, Stadtbezirk 25, Briefwahlbezirk 2588 = 221 162025 2588 1...
   * <ul>
   * <li>1-3 = Kennung Stimmzettelgebiet (bspw. Stimmkreis bei LTW, Wahlkreis bei BTW)</li>
   * <li>4-9 = Regionalangaben (4 = Kennung Regierungsbezirk, 5-6 = Kennung Landkreis oder kreisfreie Stadt, 7-9 = Kennung Gemeinde bzw. Stadtbezirk, siehe auch AGS8-Stellen 3-8)</li>
   * <li>10-13 = Kennung Erfassungsgebiet (Stimmbezirk, Briefwahlbezirk)</li>
   * <li>14 = Kennung Art des Erfassungsgebietes (Werte = {0 = Allgemeiner Stimmbezirk, 1 = Briefwahlvorstand, 2 = Allgemeiner Stimmbezirk mit beweglichem Wahlvorstand, 3 = Auswahlbezirk, 4 =
   * Auswahlbezirk mit beweglichem Wahlvorstand, 5 = Sonderstimmbezirk, 6 = Sonderstimmbezirk mit beweglichem Wahlvorstand})</li>
   * </ul>
   *
   * @see https://support.elect-it.com/ticket/secure/attachment/228721/Anlage_3_LWNSDE_Datensatzbeschreibung.pdf
   * @see https://support.elect-it.com/ticket/secure/attachment/230813/Anlage_6_LTW-Verschl%C3%BCsselung%20der%20Ordnungsbegriffe.pdf
   *
   * @param format
   *          %1$s = Kennung Stimmzettelgebiet (3), %2$s = Regionalangaben (6), %3$s = Kennung Erfassungsgebiet (4), %4$s = Kennung Art des Erfassungsgebietes (1)
   */
  public String formatGebietsreferenz(String format, WahlverwaltungGebietInformation stimmzettelgebietInformation, WahlverwaltungGebietInformation gebietUebergeordnetInformation,
      WahlverwaltungGebietDescriptor erfassungsgebietDescriptor) {
    return String.format(format, //
        formatGebietsreferenzKennungStimmzettelgebiet(stimmzettelgebietInformation), //
        formatGebietsreferenzRegionalangaben(gebietUebergeordnetInformation, erfassungsgebietDescriptor), //
        formatGebietsreferenzKennungErfassungsgebiet(erfassungsgebietDescriptor), //
        formatGebietsreferenzKennungErfassungsgebietArt(erfassungsgebietDescriptor) //
    );
  }

  public String formatGebietsreferenzKennungStimmzettelgebiet(WahlverwaltungGebietInformation stimmzettelgebietInformation) {
    return formatGebietsreferenzKennungStimmzettelgebiet(stimmzettelgebietInformation, 3);
  }

  public String formatGebietsreferenzKennungStimmzettelgebiet(WahlverwaltungGebietInformation stimmzettelgebietInformation, int size) {
    return formatRightmostCharactersLeftPadded(stimmzettelgebietInformation.getNummer(), size);
  }

  public String formatGebietsreferenzRegionalangaben(WahlverwaltungGebietDescriptor erfassungsgebietDescriptor) {
    return formatGebietsreferenzRegionalangaben(erfassungsgebietDescriptor.getGemeinde(), erfassungsgebietDescriptor);
  }

  public String formatGebietsreferenzRegionalangaben(WahlverwaltungGebietInformation gebietUebergeordnetInformation, WahlverwaltungGebietDescriptor erfassungsgebietDescriptor) {
    WahlverwaltungGemeindeInformation gemeindeInformation = erfassungsgebietDescriptor.getGemeinde();
    Optional<WahlverwaltungGebietInformation> stadtbezirkInformationIfPresent = Optional.empty();

    if (!gemeindeInformation.equals(gebietUebergeordnetInformation)) {
      stadtbezirkInformationIfPresent = Optional.of(gebietUebergeordnetInformation);
    }

    return formatGebietsreferenzRegionalangaben(gemeindeInformation, stadtbezirkInformationIfPresent);
  }

  public String formatGebietsreferenzRegionalangaben(WahlverwaltungGemeindeInformation gemeindeInformation, Optional<WahlverwaltungGebietInformation> stadtbezirkInformationIfPresent) {
    String result = gemeindeInformation.formatNummerAgsRegionalangaben();

    if (stadtbezirkInformationIfPresent.isPresent()) {
      // NOTE, dass MUC momentan zweistellige Stadtbezirksnummern verwendet, aber die tatsächlich am Gebiet hinterlegte Nummer Wahlart-abhängig
      // auch andere Angaben enthält! Also statt nur Nummer 25, ist die am Gebiet hinterlegte Nummer bspw. 10825...
      String stadtbezirkNummer = right(trimToEmpty(stadtbezirkInformationIfPresent.get().getNummer()), 2);
      String stadtbezirkKennung = formatRightmostCharactersLeftPadded(stadtbezirkNummer, 3);

      result = overlay(result, stadtbezirkKennung, 3, result.length());
    }

    return result;
  }

  public String formatGebietsreferenzKennungErfassungsgebiet(WahlverwaltungGebietDescriptor erfassungsgebietDescriptor) {
    return formatGebietsreferenzKennungErfassungsgebiet(erfassungsgebietDescriptor.getGebiet());
  }

  public String formatGebietsreferenzKennungErfassungsgebiet(WahlverwaltungGebietDescriptor erfassungsgebietDescriptor, int size) {
    return formatGebietsreferenzKennungErfassungsgebiet(erfassungsgebietDescriptor.getGebiet(), size);
  }

  public String formatGebietsreferenzKennungErfassungsgebiet(Gebiet erfassungsgebiet) {
    return formatGebietsreferenzKennungErfassungsgebiet(erfassungsgebiet, 4);
  }

  public String formatGebietsreferenzKennungErfassungsgebiet(Gebiet erfassungsgebiet, int size) {
    return formatRightmostCharactersLeftPadded(erfassungsgebiet.getNummerBezirkFallsGesetzt(), size);
  }

  protected String formatGebietsreferenzKennungErfassungsgebietGeneric(Gebiet erfassungsgebiet, int size) {
    String nummerBezirk = trimToEmpty(erfassungsgebiet.getNummerBezirk());

    if (isNotBlank(nummerBezirk)) {
      return formatLeftPadded(nummerBezirk, size);
    }

    return formatRightmostCharactersLeftPadded(erfassungsgebiet.getNummer(), size);
  }

  public String formatGebietsreferenzKennungErfassungsgebietArt(WahlverwaltungGebietDescriptor erfassungsgebietDescriptor) {
    return formatGebietsreferenzKennungErfassungsgebietArt(erfassungsgebietDescriptor.getGebiet());
  }

  public String formatGebietsreferenzKennungErfassungsgebietArt(Gebiet erfassungsgebiet) {
    return formatGebietsreferenzKennungErfassungsgebietArt(erfassungsgebiet, 1);
  }

  public String formatGebietsreferenzKennungErfassungsgebietArt(Gebiet erfassungsgebiet, int size) {
    String erfassungsgebietArt = "";

    ByStimmbezirkArt value = ByStimmbezirkArt.getStimmbezirkArt(erfassungsgebiet);

    if (value != null) {
      erfassungsgebietArt = value.getKennzeichen();
    }

    return formatRightmostCharactersLeftPadded(erfassungsgebietArt, size);
  }

  private String formatRightmostCharactersLeftPadded(String value, int size) {
    return formatLeftPadded(right(trimToEmpty(value), size), size);
  }

  private String formatLeftPadded(String value, int size) {
    return leftPad(value, Math.max(size, length(value)), calculatePadCharacter(value));
  }

  private String calculatePadCharacter(String value) {
    if (isNumeric(trimToEmpty(value))) {
      return "0";
    }

    return " ";
  }
}
