
package de.ivu.elect.business.ergebnis.control;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import de.ivu.elect.business.ergebnis.entity.Sitze;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gruppe.entity.Gruppe;
import de.ivu.elect.business.gruppe.entity.PolitischeGruppe;
import de.ivu.elect.business.wahl.control.Loader;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;


import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.reducing;

/**
 *  on 27.06.2016.
 */
public class SitzePool implements Serializable {

  private final Loader<Sitze> sitzeLoader;
  private final Loader<Liste> listenLoader;

  public SitzePool(Loader<Sitze> sitzeLoader, Loader<Liste> listenLoader) {
    this.sitzeLoader = sitzeLoader;
    this.listenLoader = listenLoader;
  }

  private List<Sitze> getSitze() {
    return sitzeLoader.load();
  }

  private List<Liste> getListen() {
    return listenLoader.load();
  }

  private Map<Gebiet, List<Sitze>> sitzeFuerGebiet;

  public List<Sitze> getSitzeFuerGebiet(Gebiet gebiet) {
    initSitzeFuerGebiet();
    List<Sitze> sitzes = sitzeFuerGebiet.get(gebiet);
    if (sitzes == null) {
      return new ArrayList<>();
    }
    return sitzes;

  }

  private void initSitzeFuerGebiet() {
    if (sitzeFuerGebiet == null) {
      sitzeFuerGebiet = new HashMap<>();
      for (Sitze sitz : getSitze()) {
        Gebiet gebiet = sitz.getGebiet();
        List<Sitze> sitzeImGebiet = sitzeFuerGebiet.computeIfAbsent(gebiet, k -> new ArrayList<>());
        sitzeImGebiet.add(sitz);
      }
    }
  }

  private Map<Gebiet, List<Sitze>> sitzeFuerGebietUndDarunter;

  public List<Sitze> getSitzeFuerGebietUndDarunter(Gebiet gebiet) {
    initSitzeFuerGebietUndDarunter();
    List<Sitze> sitzes = sitzeFuerGebietUndDarunter.get(gebiet);
    if (sitzes == null) {
      return new ArrayList<>();
    }
    return sitzes;
  }

  public Map<Gruppe, Long> getAnzahlSitzeFuerGebietUndDarunter(Gebiet gebiet) {
    // ELECTDD-287
    return getSitzeFuerGebietUndDarunter(gebiet).stream()
        .collect(groupingBy(Sitze::getGruppe, reducing(0L, Sitze::getAnzahl, Long::sum)));
  }

  private void initSitzeFuerGebietUndDarunter() {
    if (sitzeFuerGebietUndDarunter == null) {
      sitzeFuerGebietUndDarunter = new HashMap<>();
      for (Sitze sitz : getSitze()) {
        Gebiet gebiet = sitz.getGebiet();

        while (gebiet != null) {
          List<Sitze> sitzeImGebietUndDarunter = sitzeFuerGebietUndDarunter.computeIfAbsent(gebiet, k -> new ArrayList<>());
          sitzeImGebietUndDarunter.add(sitz);
          gebiet = gebiet.getUebergeordnetesGebiet();
        }
      }
    }
  }

  private Map<PolitischeGruppe, Collection<Sitze>> sitzeFuerPolitischeGruppe;

  public Collection<Sitze> getSitzeFuerPolitischeGruppe(PolitischeGruppe politischeGruppe) {
    initSitzeFuerPolitischeGruppe();
    Collection<Sitze> sitzes = sitzeFuerPolitischeGruppe.get(politischeGruppe);
    if (sitzes == null) {
      return Collections.emptyList();
    }
    return sitzes;
  }

  private void initSitzeFuerPolitischeGruppe() {
    if (sitzeFuerPolitischeGruppe == null) {
      sitzeFuerPolitischeGruppe = new HashMap<>();
      for (Sitze sitzeLokal : getSitze()) {
        if (sitzeLokal.getGruppe().isPolitischeGruppe()) {
          PolitischeGruppe politischeGruppe = sitzeLokal.getGruppe().castTo(PolitischeGruppe.class).getEntity();
          Collection<Sitze> sitzeListe = sitzeFuerPolitischeGruppe.computeIfAbsent(politischeGruppe, k -> new ArrayList<>());
          sitzeListe.add(sitzeLokal);
        }
      }
    }
  }

  private Map<Liste, Sitze> sitzeFuerListe;

  @Deprecated // Methode ist Quatsch
  public Sitze getSitzeFuerListe(Liste liste) {
    initSitzeFuerListe();
    return sitzeFuerListe.get(liste);
  }

  private void initSitzeFuerListe() {
    if (sitzeFuerListe == null) {
      sitzeFuerListe = new HashMap<>();
      for (Sitze sitze : getSitze()) {
        for (Liste liste : getListen()) {
          if (liste.getGruppe().equals(sitze.getGruppe()) && liste.getGebiet().equals(sitze.getGebiet())) {
            if (sitzeFuerListe.put(liste, sitze) != null) {
              throw new IllegalStateException("Liste: " + liste + " hat mehrere vergebene Sitze!");
            }
            break;
          }
        }
      }
    }
  }

}
