
package de.ivu.elect.business.gebietsbaum.boundary;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

import de.ivu.elect.business.exchange.control.AbstractContextMitCustomParameter;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsgruppierung;
import de.ivu.elect.business.gebietsbaum.entity.Verwaltungsart;
import de.ivu.elect.business.wahl.control.Wahldaten;
import de.ivu.elect.business.wahl.control.WahldatenLoader;
import de.ivu.elect.business.wahl.entity.AbstractWahlParameterLoader;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlParameterWerte;


import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.groupingBy;

public class WahlverwaltungGebietInformationResolverContext extends AbstractContextMitCustomParameter {

  private final Wahl wahl;

  private final AbstractWahlParameterLoader wahlParameterLoader;

  private final Supplier<Collection<? extends Gebiet>> gebieteLoader;

  private final Supplier<Collection<Gebietsgruppierung>> gebietsgruppierungenLoader;

  private Collection<Gebietsgruppierung> gebietsgruppierungen;

  private Map<Verwaltungsart, List<Gebietsgruppierung>> gebietsgruppierungenPerArt;

  public WahlverwaltungGebietInformationResolverContext(Wahldaten<? extends Wahl, ? extends Gebiet> wahldaten) {
    this(wahldaten.getWahl(), wahldaten.getWahlParameterLoader(), wahldaten::getGebiete, wahldaten::getGebietsgruppierungen);
  }

  public WahlverwaltungGebietInformationResolverContext(Wahl wahl, AbstractWahlParameterLoader wahlParameterLoader, AbstractGebietDAO gebietDAO, AbstractGebietsgruppierungDAO gebietsgruppierungDAO) {
    this(wahl, wahlParameterLoader, // Verwendung von WahldatenLoader, da dieser bereits cached!
        new WahldatenLoader<>(() -> gebietDAO.findByWahl(wahl), Collections::emptyList, Collections::emptyList, Collections::emptyList, Collections::emptyList,
            Collections::emptyList,
            Collections::emptyList, Collections::emptyList, ergebnisart -> Collections.emptyList(), ergebnisart -> Collections.emptyList(),
            ergebnisart -> Collections.emptyList(),
            Collections::emptyList,
            () -> gebietsgruppierungDAO.findByWahl(wahl), Collections::emptyList, Collections::emptyList, Collections::emptyList, Collections::emptyList,
            Collections::emptyList,
            Collections::emptyList, Collections::emptyList, Collections::emptyList, Collections::emptyList, Collections::emptyList));
  }

  private WahlverwaltungGebietInformationResolverContext(Wahl wahl, AbstractWahlParameterLoader wahlParameterLoader, WahldatenLoader<? extends Gebiet> wahldatenLoader) {
    this(wahl, wahlParameterLoader, wahldatenLoader::getGebiete, wahldatenLoader::getGebietsgruppierungen);
  }

  private WahlverwaltungGebietInformationResolverContext(Wahl wahl, AbstractWahlParameterLoader wahlParameterLoader, Supplier<Collection<? extends Gebiet>> gebieteLoader,
                                                         Supplier<Collection<Gebietsgruppierung>> gebietsgruppierungenLoader) {
    super();

    this.wahl = wahl;

    this.wahlParameterLoader = wahlParameterLoader;

    this.gebieteLoader = gebieteLoader;

    this.gebietsgruppierungenLoader = gebietsgruppierungenLoader;

    this.gebietsgruppierungen = null;
    this.gebietsgruppierungenPerArt = null;
  }

  public Wahl getWahl() {
    return wahl;
  }

  public String extractWahlParameter(WahlParameterWerte template) {
    return wahlParameterLoader.resolveStringValue(getWahl(), template, "");
  }

  private transient Collection<? extends Gebiet> gebiete = null;

  public Collection<? extends Gebiet> collectGebiete() {
    if (gebiete == null) {
      gebiete = gebieteLoader.get();
    }
    return gebiete;
  }

  public Collection<Gebietsgruppierung> collectGebietsgruppierungen(Verwaltungsart gebietsgruppierungArt) {
    if (gebietsgruppierungenPerArt == null) {
      gebietsgruppierungenPerArt = collectGebietsgruppierungen().stream().collect(groupingBy(Gebietsgruppierung::getVerwaltungsart));
    }

    return gebietsgruppierungenPerArt.getOrDefault(gebietsgruppierungArt, emptyList());
  }

  public Collection<Gebietsgruppierung> collectGebietsgruppierungen() {
    if (gebietsgruppierungen == null) {
      gebietsgruppierungen = gebietsgruppierungenLoader.get();
    }

    return gebietsgruppierungen;
  }
}
