
package de.ivu.elect.presentation.wahl.form;

import javax.enterprise.inject.Specializes;
import javax.inject.Inject;

import de.ivu.elect.business.gebietsbaum.boundary.GebietExtendedDAO;
import de.ivu.elect.business.gebietsbaum.control.GebietByBreadthFirstComparator;
import de.ivu.elect.business.gebietsbaum.entity.AbstractGebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.jee.presentation.dynamicform.AbstractDateProperty;
import de.ivu.elect.jee.presentation.dynamicform.FormBuilder;
import de.ivu.elect.jee.presentation.dynamicform.Property;
import de.ivu.elect.presentation.gebietsbaum.GebietNummerFormatter;
import de.ivu.elect.presentation.gebietsbaum.form.GebietViewFormBuilder;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static java.util.Collections.emptyList;

@Specializes
public class CommonWahlViewFormBuilder extends WahlViewFormBuilder {

  private static final long serialVersionUID = 1L;

  @Inject
  private transient WahlOderWahlterminSecurityViewFormBuilder wahlOderWahlterminSecurityViewFormBuilder;

  @Inject
  private transient GebietExtendedDAO gebietExtendedDAO;

  @Inject
  private transient GebietNummerFormatter gebietNummerFormatter;

  @Inject
  private transient GebietViewFormBuilder gebietViewFormBuilder;

  public CommonWahlViewFormBuilder() {
    super();
  }

  @Override
  protected void addProperties(FormBuilder formBuilder, Wahl wahl, Property wahlartProperty, AbstractDateProperty wahlterminWahldatumProperty, boolean readonly) {
    super.addProperties(formBuilder, wahl, wahlartProperty, wahlterminWahldatumProperty, readonly);

    addStatusProperty(formBuilder, wahl);
    addQuorumProperty(formBuilder, wahl, wahlartProperty, readonly);

    wahlOderWahlterminSecurityViewFormBuilder.addSecurityProperties(formBuilder, wahl, readonly);
  }

  private void addStatusProperty(FormBuilder formBuilder, Wahl wahl) {
    formBuilder.addProperty(wahl.getProperties().getStatusProperty()).withReadonly(true); // Immer Read-Only, über Aktionen in der Wahlsuche steuerbar
  }

  private void addQuorumProperty(FormBuilder formBuilder, Wahl wahl, Property wahlartProperty, boolean readonly) {
    formBuilder.addProperty(new AutoUpdateQuorumProperty(wahl)).withObservedProperty(wahlartProperty).withReadonly(readonly);
  }

  protected void addSitzeStimmenProperties(FormBuilder formBuilder, Wahl wahl, boolean readonly) {
    if (wahl.isPersistent() && wahl.isWahlMitAnzahlSitze() || wahl.isWahlMitAnzahlStimmenProWaehlerPerWahlgebiet()) { // ELECT-9377
      List<Gebiet> gebieteMitSitzen = findeGebieteMitSitzen(wahl);
      for (Gebiet gebiet : gebieteMitSitzen) {
        wahl.getEditGebiete().add(gebiet);

        formBuilder.addSeparator("wahl.edit.gebiet.sitze", gebietNummerFormatter.getDarstellung(gebiet));
        gebietViewFormBuilder.addSitzeStimmenProperties(formBuilder, gebiet, false, readonly);
      }
    }
  }

  private List<Gebiet> findeGebieteMitSitzen(Wahl wahl) {
    List<Gebiet> gebiete = gebietExtendedDAO.findByWahl(wahl);

    if (gebiete.isEmpty()) {
      return emptyList();
    }

    // ELECT-9441 alle wahlgebiete (ohne wurzelgebiet)
    Set<Gebiet> wahlgebiete = gebiete.stream().filter(AbstractGebiet::isWahlgebiet).collect(Collectors.toSet());

    // jeweils oberstes Wahlgebiet betrachten
    List<Gebiet> obersteWahlgebiete = wahlgebiete.stream().filter(gebiet -> !isUebergeordnetesGebietInWahlgebiete(gebiet, wahlgebiete)).sorted(GebietByBreadthFirstComparator.INSTANCE).collect(Collectors.toList());

    // Wurzelgebiet fallback
    if (obersteWahlgebiete.isEmpty()) {
      obersteWahlgebiete = gebiete.stream().filter(AbstractGebiet::isWurzelgebiet/*ELECTKOM-1019*/).collect(Collectors.toList());
    }
    obersteWahlgebiete.sort(GebietByBreadthFirstComparator.INSTANCE);
    return obersteWahlgebiete;
  }

  private boolean isUebergeordnetesGebietInWahlgebiete(Gebiet gebiet, Set<Gebiet> wahlgebiete) {
    Gebiet uebergeordnetesGebiet = gebiet.getUebergeordnetesGebiet();

    if (uebergeordnetesGebiet == null) {
      return false;
    }

    if (wahlgebiete.contains(uebergeordnetesGebiet)) {
      return true;
    }

    return isUebergeordnetesGebietInWahlgebiete(uebergeordnetesGebiet, wahlgebiete);
  }
}
