
package de.ivu.elect.exchange.gebietsbaum.mapper;

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 java.util.Optional;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;

import de.ivu.elect.business.benutzerverwaltung.entity.Rolle;
import de.ivu.elect.business.benutzerverwaltung.entity.RolleArt;
import de.ivu.elect.business.gebietsbaum.boundary.GebietFactory;
import de.ivu.elect.business.gebietsbaum.entity.Gebiet;
import de.ivu.elect.business.gebietsbaum.entity.Gebietsart;
import de.ivu.elect.business.mandant.entity.Mandant;
import de.ivu.elect.exchange.anschrift.mapper.AnschriftMapper;
import de.ivu.elect.exchange.person.mapper.PersonMapper;
import de.ivu.elect.exchange.wahl.WahlKonfigurationMapper;
import de.ivu.elect.interfaces.core.GebietReferenz;
import de.ivu.elect.interfaces.core.Person;
import de.ivu.elect.interfaces.core.Wahl;
import de.ivu.elect.interfaces.core.konfiguration.WahlKonfiguration;

public class GebietMapper {

  @Inject
  protected transient GebietFactory gebietFactory;

  @Inject
  protected transient AnschriftMapper anschriftMapper;

  @Inject
  protected transient PersonMapper personMapper;

  @Inject
  protected transient WahlKonfigurationMapper wahlKonfigurationMapper;

  @Inject
  protected transient GebietMapperUtil mapperUtil;

  public GebietMapper() {
    super();
  }

  public GebietReferenz mapGebietReferenz(Gebiet intern) {
    return mapperUtil.createGebietReferenz(intern);
  }

  public Gebiet mapGebietReferenz(GebietReferenz extern, Map<GebietReferenz, Gebiet> gebieteNachReferenz) {
    return mapperUtil.createGebiet(extern, gebieteNachReferenz);
  }

  public Collection< ? extends de.ivu.elect.interfaces.core.Gebiet> mapGebiete(List<de.ivu.elect.business.gebietsbaum.entity.Gebiet> gebiete) {
    List<de.ivu.elect.interfaces.core.Gebiet> result = new ArrayList<>();

    for (de.ivu.elect.business.gebietsbaum.entity.Gebiet intern : gebiete) {
      result.add(mapGebiet(intern));
    }

    return result;
  }

  public de.ivu.elect.interfaces.core.Gebiet mapGebiet(Gebiet gebietIntern) {
    de.ivu.elect.interfaces.core.Gebiet gebietExtern = new de.ivu.elect.interfaces.core.Gebiet();
    gebietExtern.setGebietReferenz(mapperUtil.createGebietReferenz(gebietIntern.getNummer(), gebietIntern.getGebietsart().getName()));

    if (gebietIntern.getUebergeordnetesGebiet() != null) {
      Gebiet uebergeordnetesGebiet = gebietIntern.getUebergeordnetesGebiet();
      gebietExtern.setUebergeordnetesGebiet(mapperUtil.createGebietReferenz(uebergeordnetesGebiet.getNummer(), uebergeordnetesGebiet.getGebietsart().getName()));
    }

    gebietExtern.setName(gebietIntern.getName());
    gebietExtern.setKuerzel(gebietIntern.getKuerzel());
    gebietExtern.setNummerBezirk(gebietIntern.getNummerBezirk());

    gebietExtern.setExterneId(gebietIntern.getExterneIdSupplier().get() /* ELECT-8968 */);
    gebietExtern.setExterneIdSalt(gebietIntern.getExterneIdSalt());

    gebietExtern.setGebietsinformationCustomData(gebietIntern.getCustomDataXml());
    gebietExtern.setAnzahlStimmenProWaehler(gebietIntern.getAnzahlStimmenProWaehler());
    gebietExtern.setAnzahlSitze(gebietIntern.getAnzahlSitze());
    gebietExtern.setPosition(gebietIntern.getPosition());
    gebietExtern.setAuswahlbezirk(gebietIntern.getAuswahlbezirk());
    gebietExtern.setSonderstimmbezirk(gebietIntern.getSonderstimmbezirk());
    gebietExtern.setBeweglicherWahlvorstand(gebietIntern.getBeweglicherWahlvorstand());
    gebietExtern.setWahlbeteiligungsbezirk(gebietIntern.getWahlbeteiligungsbezirk());
    gebietExtern.setUmfragebezirk(gebietIntern.getUmfragebezirk());
    gebietExtern.setRolleBearbeiten(mapRolle(gebietIntern.getBearbeitenRolle()));
    gebietExtern.setRolleEinsehen(mapRolle(gebietIntern.getEinsehenRolle()));
    gebietExtern.setAnzahlWahlberechtigteVorlaeufig(gebietIntern.getAnzahlWahlberechtigteVorlaeufig());

    gebietExtern.setAnschrift(new de.ivu.elect.interfaces.core.Anschrift());
    anschriftMapper.extern(gebietIntern.getAnschrift(), gebietExtern.getAnschrift());
    gebietExtern.setAnsprechpartner(new Person());
    personMapper.extern(gebietIntern.getAnsprechpartner(), gebietExtern.getAnsprechpartner());

    if (gebietIntern.getVerantwortlicherMandant() != null) {
      gebietExtern.setVerantwortlicherMandant(gebietIntern.getVerantwortlicherMandant().getNummer());
    }

    return gebietExtern;
  }

  public Map<GebietReferenz, Gebiet> mapGebiete(Wahl wahlExtern) {
    List<de.ivu.elect.interfaces.core.konfiguration.Gebietsart> gebietsartenExtern = Optional //
        .ofNullable(wahlExtern.getWahlKonfiguration()) //
        .map(WahlKonfiguration::getGebietsarten) //
        .orElse(Collections.emptyList());

    return mapGebiete(wahlExtern.getGebiete(), gebietsartenExtern);
  }

  public Map<GebietReferenz, Gebiet> mapGebiete(List<de.ivu.elect.interfaces.core.Gebiet> gebieteExtern, List<de.ivu.elect.interfaces.core.konfiguration.Gebietsart> gebietsartenExtern) {
    Map<GebietReferenz, Gebiet> interneGebieteNachReferenz = new HashMap<>();
    // erst alle Mappen
    Map<String, Gebietsart> gebietsartenNachName = new HashMap<>();

    for (de.ivu.elect.interfaces.core.Gebiet gebietExtern : gebieteExtern) {
      GebietReferenz gebietReferenz = gebietExtern.getGebietReferenz();
      Gebiet gebietIntern = createGebiet(gebietReferenz, gebietsartenExtern, gebietsartenNachName);
      interneGebieteNachReferenz.put(gebietReferenz, gebietIntern);
    }

    for (de.ivu.elect.interfaces.core.Gebiet gebietExtern : gebieteExtern) {
      Gebiet gebietIntern = interneGebieteNachReferenz.get(gebietExtern.getGebietReferenz());
      mapGebiet(gebietExtern, gebietIntern, interneGebieteNachReferenz);
    }

    // dann Baum aufbauen
    for (de.ivu.elect.interfaces.core.Gebiet gebietExtern : gebieteExtern) {
      if (gebietExtern.getUebergeordnetesGebiet() != null) {
        Gebiet uebergeordnetesGebietIntern = interneGebieteNachReferenz.get(gebietExtern.getUebergeordnetesGebiet());

        Gebiet gebietIntern = interneGebieteNachReferenz.get(gebietExtern.getGebietReferenz());
        gebietIntern.setUebergeordnetesGebiet(uebergeordnetesGebietIntern);
      }
    }

    return interneGebieteNachReferenz;
  }

  @Nonnull
  private Gebiet createGebiet(GebietReferenz gebietReferenz, List<de.ivu.elect.interfaces.core.konfiguration.Gebietsart> gebietsartenExtern, Map<String, Gebietsart> gebietsartenNachName ) {
    Gebietsart gebietsartIntern = findOrCreateGebietsartIntern(gebietReferenz, gebietsartenExtern, gebietsartenNachName);

    Gebiet gebietIntern = createEmptyGebietObject(gebietsartIntern);

    gebietIntern.setGebietsart(gebietsartIntern);
    gebietIntern.setNummer(gebietReferenz.getNummer());
    return gebietIntern;
  }

  protected Gebietsart findOrCreateGebietsartIntern(GebietReferenz gebietReferenz, List<de.ivu.elect.interfaces.core.konfiguration.Gebietsart> gebietsartenExtern,
      Map<String, Gebietsart> gebietsartenNachName) {
    Gebietsart gebietsartIntern = gebietsartenNachName.get(gebietReferenz.getGebietsart());

    if (gebietsartIntern != null) {
      return gebietsartIntern;
    }

    gebietsartIntern = gebietsartenExtern.stream().filter(gebietsart -> gebietsart != null && gebietsart.getName() != null && gebietsart.getName().equals(gebietReferenz.getGebietsart())).findFirst()
        .map(gebietsart -> wahlKonfigurationMapper.mappe(gebietsart)).orElse(null);

    if (gebietsartIntern == null) {
      gebietsartIntern = new Gebietsart();
      gebietsartIntern.setName(gebietReferenz.getGebietsart());
    }

    gebietsartenNachName.put(gebietReferenz.getGebietsart(), gebietsartIntern);
    return gebietsartIntern;
  }

  protected void mapGebiet(de.ivu.elect.interfaces.core.Gebiet gebietExtern, Gebiet gebietIntern, Map<GebietReferenz, Gebiet> interneGebieteNachReferenz) {
    gebietIntern.setName(gebietExtern.getName());
    gebietIntern.setKuerzel(gebietExtern.getKuerzel());
    gebietIntern.setNummerBezirk(gebietExtern.getNummerBezirk());
    gebietIntern.setExterneId(gebietExtern.getExterneId());
    gebietIntern.setExterneIdSalt(gebietExtern.getExterneIdSalt());
    gebietIntern.setCustomDataXml(gebietExtern.getGebietsinformationCustomData());

    gebietIntern.setAnzahlStimmenProWaehler(gebietExtern.getAnzahlStimmenProWaehler());
    gebietIntern.setAnzahlSitze(gebietExtern.getAnzahlSitze());
    gebietIntern.setPosition(gebietExtern.getPosition());
    gebietIntern.setAnzahlErforderlicherUnterstuetzer(gebietExtern.getAnzahlErforderlicherUnterstuetzer());
    gebietIntern.setAuswahlbezirk(gebietExtern.getAuswahlbezirk());
    gebietIntern.setSonderstimmbezirk(gebietExtern.getSonderstimmbezirk());
    gebietIntern.setBeweglicherWahlvorstand(gebietExtern.getBeweglicherWahlvorstand());

    gebietIntern.setWahlbeteiligungsbezirk(gebietExtern.getWahlbeteiligungsbezirk());
    gebietIntern.setUmfragebezirk(gebietExtern.getUmfragebezirk());
    gebietIntern.setBearbeitenRolle(mapRolle(gebietExtern.getRolleBearbeiten()));
    gebietIntern.setEinsehenRolle(mapRolle(gebietExtern.getRolleEinsehen()));
    gebietIntern.setAnzahlWahlberechtigteVorlaeufig(gebietExtern.getAnzahlWahlberechtigteVorlaeufig());

    if (gebietExtern.getAnschrift() != null) {
      anschriftMapper.intern(gebietExtern.getAnschrift(), gebietIntern.getAnschrift());
    }

    if (gebietExtern.getAnsprechpartner() != null) {
      personMapper.intern(gebietExtern.getAnsprechpartner(), gebietIntern.getAnsprechpartner());
    }

    if (gebietExtern.getVerantwortlicherMandant() != null) {
      Mandant verantwortlicherMandant = new Mandant();
      verantwortlicherMandant.setNummer(gebietExtern.getVerantwortlicherMandant());
      gebietIntern.setVerantwortlicherMandant(verantwortlicherMandant);
    }

  }

  protected Gebiet createEmptyGebietObject(Gebietsart gebietsartIntern) {
    return gebietFactory.create();
  }

  @Nullable
  private String mapRolle(@Nullable Rolle rolle) {
    if (rolle == null) {
      return null;
    }

    return rolle.getName();
  }

  @Nullable
  private Rolle mapRolle(String rolleExtern) {
    if (rolleExtern == null) {
      return null;
    }
    Rolle rolle = new Rolle();
    rolle.setName(rolleExtern);
    rolle.setRolleArt(RolleArt.GEBIET);
    return rolle;
  }

  public GebietMapperUtil getMapperUtil() {
    return mapperUtil;
  }
}
