
package de.ivu.elect.business.wahlvorschlagsperson.entity;

import static java.util.Collections.unmodifiableCollection;
import static java.util.Collections.unmodifiableMap;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.UUID;
import java.util.stream.Collectors;

import javax.annotation.Nonnull;

import de.ivu.elect.business.gebietsbaum.boundary.AbstractGebietDAO;
import de.ivu.elect.business.gebietsbaum.control.GebietByNummerComparator;
import de.ivu.elect.business.gebietsbaum.entity.EntityMitGebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gruppe.control.GruppeByKurznameComparator;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.person.entity.Sperrmerkmal;
import de.ivu.elect.business.security.entity.EntityMitSperrmerkmal;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;
import de.ivu.elect.business.wahlvorschlagsperson.boundary.AbstractWahlvorschlagsPersonDAO;
import de.ivu.elect.business.wahlvorschlagsperson.boundary.WahlvorschlagsPersonDAO;
import de.ivu.elect.jee.entity.Entity;
import de.ivu.elect.jee.entity.EntityContainer;
import de.ivu.elect.jee.entity.EntityFachlichAbhaengigNode;
import de.ivu.elect.jee.entity.EntityLongPK;
import de.ivu.elect.jee.entity.tk.EntityTK;
import de.ivu.elect.jee.entity.tk.EntityUK;
import de.ivu.elect.jee.protokollierung.AttributWertVorher;
import de.ivu.elect.jee.protokollierung.VorherNachherWert;

public class WahlvorschlagsPersonPoolSearchResultEntry implements Entity<EntityLongPK, EntityUK>, Serializable, EntityMitSperrmerkmal, EntityMitGebiet {

  private static final long serialVersionUID = 1L;

  private static final Comparator<Gruppe> GRUPPE_COMPARATOR = GruppeByKurznameComparator.ASCENDING;

  private static final Comparator<Gebiet> GEBIET_COMPARATOR = GebietByNummerComparator.ASCENDING;

  private final UUID uuid;

  private WahlvorschlagsPerson template;

  private Collection<WahlvorschlagsPerson> wahlvorschlagsPersonenVerknuepft;

  private final Map<Gruppe, Map<WahlvorschlagsPersonTyp, Collection<Gebiet>>> gebietsinformationenPerWahlvorschlagsPersonTypPerGruppe;

  private WahlvorschlagsPersonTyp wahlvorschlagsPersonTypSelected;

  public WahlvorschlagsPersonPoolSearchResultEntry(WahlvorschlagsPerson template, Collection<WahlvorschlagsPerson> wahlvorschlagsPersonenVerknuepft) {
    super();

    this.uuid = UUID.randomUUID();

    this.template = template;
    this.wahlvorschlagsPersonenVerknuepft = unmodifiableCollection(wahlvorschlagsPersonenVerknuepft);

    this.gebietsinformationenPerWahlvorschlagsPersonTypPerGruppe = mapGebietsinformationenPerWahlvorschlagsPersonTypPerGruppe(template, wahlvorschlagsPersonenVerknuepft);
  }

  public UUID getUuid() {
    return uuid;
  }

  public WahlvorschlagsPersonTyp getWahlvorschlagsPersonTypSelected() {
    return wahlvorschlagsPersonTypSelected;
  }

  public void setWahlvorschlagsPersonTypSelected(WahlvorschlagsPersonTyp wahlvorschlagsPersonTypSelected) {
    this.wahlvorschlagsPersonTypSelected = wahlvorschlagsPersonTypSelected;
  }

  public WahlvorschlagsPerson getTemplate() {
    return template;
  }

  public Collection<WahlvorschlagsPerson> getWahlvorschlagsPersonenVerknuepft() {
    return wahlvorschlagsPersonenVerknuepft;
  }

  public Map<Gruppe, Map<WahlvorschlagsPersonTyp, Collection<Gebiet>>> getGebietsinformationenPerWahlvorschlagsPersonTypPerGruppe() {
    return gebietsinformationenPerWahlvorschlagsPersonTypPerGruppe;
  }

  @Nonnull
  @Override
  public Sperrmerkmal getSperrmerkmal() {
    return template.getSperrmerkmal();
  }

  @Override
  public Gebiet getGebiet() {
    return template.getGebiet();
  }

  public void attach(AbstractWahlvorschlagsPersonDAO wahlvorschlagsPersonDAO, AbstractGebietDAO gebietDAO){
    // ELECT-10995
    this.template = wahlvorschlagsPersonDAO.attach(template);
    this.wahlvorschlagsPersonenVerknuepft = wahlvorschlagsPersonDAO.attachAll(wahlvorschlagsPersonenVerknuepft);
    for (Map<WahlvorschlagsPersonTyp, Collection<Gebiet>> value : gebietsinformationenPerWahlvorschlagsPersonTypPerGruppe.values()) {
      for (Collection<Gebiet> gebiete : value.values()) {
        List<Gebiet> attachedGebiete = gebiete.stream().map(gebietDAO::attach).collect(Collectors.toCollection(() -> new ArrayList<>(gebiete.size())));
        gebiete.clear();
        gebiete.addAll(attachedGebiete);
      }
    }
  }

  private static Map<Gruppe, Map<WahlvorschlagsPersonTyp, Collection<Gebiet>>> mapGebietsinformationenPerWahlvorschlagsPersonTypPerGruppe(WahlvorschlagsPerson wahlvorschlagsPerson,
      Collection<WahlvorschlagsPerson> wahlvorschlagsPersonenVerknuepft) {
    Map<Gruppe, Map<WahlvorschlagsPersonTyp, Collection<Gebiet>>> entryPerGruppe = new TreeMap<>(GRUPPE_COMPARATOR);

    for (WahlvorschlagsPerson wahlvorschlagsPersonVerknuepft : wahlvorschlagsPersonenVerknuepft) {
      WahlvorschlagsPersonTyp wahlvorschlagsPersonTyp = wahlvorschlagsPersonVerknuepft.getWahlvorschlagsPersonTyp();

      // NOTE, dass die Gebietsinformationen (und nicht nur die Gebiete) an dieser Stelle 'vorgeladen' werden MÜSSEN :-(,
      // um bspw. die Gebietsnummern im Suchergebnis anzuzeigen.
      Gruppe gruppe = wahlvorschlagsPersonVerknuepft.getGruppe();
      Gebiet gebiet = wahlvorschlagsPersonVerknuepft.getGebiet();

      if (!entryPerGruppe.containsKey(gruppe)) {
        entryPerGruppe.put(gruppe, new HashMap<>());
      }

      Map<WahlvorschlagsPersonTyp, Collection<Gebiet>> entryPerWahlvorschlagsPersonTyp = entryPerGruppe.get(gruppe);

      if (!entryPerWahlvorschlagsPersonTyp.containsKey(wahlvorschlagsPersonTyp)) {
        entryPerWahlvorschlagsPersonTyp.put(wahlvorschlagsPersonTyp, new TreeSet<>(GEBIET_COMPARATOR));
      }

      entryPerWahlvorschlagsPersonTyp.get(wahlvorschlagsPersonTyp).add(gebiet);
    }

    return unmodifiableMap(entryPerGruppe);
  }

  @Override
  public Long getId() {
    // ELECT-5245
    return null;
  }

  @Override
  public boolean isAenderungenProtokollieren() {
    return false;
  }

  @Override
  public Class< ? > getEntityClass() {
    throw new UnsupportedOperationException();
  }

  @Override
  public String getEntityTechnicalName() {
    throw new UnsupportedOperationException();
  }

  @Override
  public EntityLongPK getPk() {
    throw new UnsupportedOperationException();
  }

  @Override
  public EntityUK getUk() {
    throw new UnsupportedOperationException();
  }

  @Override
  public EntityTK getTk() {
    throw new UnsupportedOperationException();
  }

  @Override
  public Map<String, VorherNachherWert> getProtokollierteAttributeMitWerten() {
    throw new UnsupportedOperationException();
  }

  @Override
  public void setProtokollierteAttributeMitWerten(Map<String, AttributWertVorher> werteVorher) {
    throw new UnsupportedOperationException();
  }

  @Override
  public void resetProtokollierteAttribute() {
    throw new UnsupportedOperationException();
  }

  @Override
  public List<EntityFachlichAbhaengigNode> getFachlichAbhaengigeEntitaeten() {
    throw new UnsupportedOperationException();
  }

  @Override
  public boolean isInstanceOf(Class< ? > clazz) {
    return clazz.isInstance(this);
  }

  @Override
  @SuppressWarnings({"unchecked", "rawtypes"})
  public EntityContainer< ? > castTo(Class clazz) {
    return new EntityContainer<>(this);
  }

  @Override
  public String getBaseClassName() {
    return getClass().getName();
  }

  @Override
  public void copyEntityAttributesToOtherEntity(Entity other) {

  }
}
