
package de.ivu.elect.was.exchange.stimmeingabe;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.xml.bind.JAXBException;

import de.ivu.elect.business.datei.entity.FileType;
import de.ivu.elect.business.exchange.control.FileFormat;
import de.ivu.elect.business.exchange.control.FileKonfiguration;
import de.ivu.elect.business.exchange.control.MappingException;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabeImportContext;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingabestufe;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingang;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingangsart;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingangsfehlerstatus;
import de.ivu.elect.business.stimmeingabe.entity.Ergebniseingangsherkunft;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabe;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabefehler;
import de.ivu.elect.business.stimmeingabe.entity.Stimmeingabefehlerart;
import de.ivu.elect.business.stimmeingabe.entity.ZwischensummeEingabe;
import de.ivu.elect.business.wahl.boundary.WahldurchfuehrungFactory;
import de.ivu.elect.business.wahl.boundary.WahlterminFactory;
import de.ivu.elect.business.wahl.entity.Wahlart;
import de.ivu.elect.business.wahl.entity.Wahldurchfuehrung;
import de.ivu.elect.exchange.XmlImportMapper;
import de.ivu.elect.interfaces.was.stimmeingabe.ErfassteStimmen;
import de.ivu.elect.interfaces.was.stimmeingabe.Stimmeneingabefehler;
import de.ivu.elect.jee.exchange.Violations;
import de.ivu.elect.jee.exchange.XmlMarshaller;
import de.ivu.elect.jee.exchange.XmlSchemaLoader;
import de.ivu.elect.was.exchange.GebietMapper;
import de.ivu.elect.was.exchange.wahl.AbstractWahlExternInternMapper;

public abstract class AbstractWasStimmeingabeXMLImportMapper extends AbstractStimmeingabeImportMapper implements XmlImportMapper {

  private static final long serialVersionUID = 1L;

  private static final String SCHEMA_RESOURCE_NAME = "elect-interfaces-was-stimmeingabe.xsd";

  @Inject
  private transient XmlMarshaller<ErfassteStimmen> xmlMarshaller;

  @Inject
  private transient XmlSchemaLoader schemaLoader;

  @Inject
  private transient WahlterminFactory wahlterminFactory;

  @Inject
  private transient WahldurchfuehrungFactory wahldurchfuehrungFactory;

  @Inject
  private transient GebietMapper gebietMapper;

  @Inject
  private transient StimmeingabePlausiMapper stimmeingabePlausiMapper;

  public AbstractWasStimmeingabeXMLImportMapper() {
    super();
  }

  @Override
  protected boolean isAcceptWahl(@Nonnull Wahldurchfuehrung wahldurchfuehrung) {
    return wahldurchfuehrung != null;
  }

  @Override
  public FileFormat getFileFormat() {
    return new FileFormat(WasStimmeingabeXMLZIPExportMapper.FORMAT_NAME, getFileType());
  }

  protected abstract FileType getFileType();

  @Override
  public List<Ergebniseingang> deserialize(InputStream is, StimmeingabeImportContext context, FileKonfiguration fileKonfiguration) throws MappingException {
    try {
      return mappe(xmlMarshaller.deserialize(ErfassteStimmen.class, is));
    } catch (JAXBException e) {
      throw new MappingException(e.getMessage(), e);
    }
  }

  public List<ErfassteStimmen> extractErfassteStimmen(InputStream is, StimmeingabeImportContext context, FileKonfiguration fileKonfiguration) throws MappingException {
    try {
      return Collections.singletonList(xmlMarshaller.deserialize(ErfassteStimmen.class, is));
    } catch (JAXBException e) {
      throw new MappingException(e.getMessage(), e);
    }
  }

  public List<Ergebniseingang> mappe(ErfassteStimmen erfassteStimmen) {
    Wahldurchfuehrung wahldurchfuehrung = wahldurchfuehrungFactory.create();
    wahldurchfuehrung.setWahlart(new Wahlart());
    wahldurchfuehrung.setWahltermin(wahlterminFactory.create());
    wahldurchfuehrung.setName(erfassteStimmen.getWahlname());
    wahldurchfuehrung.getWahlart().setName(erfassteStimmen.getWahlart());
    wahldurchfuehrung.getWahltermin().setWahldatum(erfassteStimmen.getWahldatum());

    if (erfassteStimmen.getWahlterminNummer() != null) {
      wahldurchfuehrung.getWahltermin().setNummer(erfassteStimmen.getWahlterminNummer());
    }

    WasStimmeingabeXMLImportMapperMaps maps = new WasStimmeingabeXMLImportMapperMaps(gebietMapper, gruppeImGebietFactory, gruppeFactory, listenkandidaturFactory, wahldurchfuehrung);

    return erfassteStimmen.getErgebniseingaenge().stream().map(ergebniseingang -> mappe(ergebniseingang, maps))
        .collect(Collectors.toCollection(() -> new ArrayList<>(erfassteStimmen.getErgebniseingaenge().size())));
  }

  private Ergebniseingang mappe(de.ivu.elect.interfaces.was.stimmeingabe.Ergebniseingang ergebniseingang, WasStimmeingabeXMLImportMapperMaps maps) {
    Ergebniseingang result = new Ergebniseingang();
    result.setEingangssequenznummer(ergebniseingang.getEingangssequenznummer());
    result.setErfasser(ergebniseingang.getErfasser());
    result.setErgebniseingangsart(mappe(ergebniseingang.getErgebniseingangsart()));
    result.setErgebniseingabestufe(mappe(ergebniseingang.getErgebniseingabestufe()));
    result.setErgebniseingangsfehlerstatus(mappe(ergebniseingang.getErgebniseingangsfehlerstatus()));
    result.setErgebniseingangsherkunft(mappe(ergebniseingang.getErgebniseingangsherkunft()));
    result.setWahldurchfuehrungsgebiet(maps.getGebietFuerReferenz(ergebniseingang.getGebiet()));
    result.setZeitstempel(ergebniseingang.getZeitstempel());

    for (de.ivu.elect.interfaces.was.stimmeingabe.Stimmeingabe stimmeingabe : ergebniseingang.getStimmeingaben()) {
      result.addStimmeingabeByErgebniseingang(mappe(stimmeingabe, maps));
    }

    for (de.ivu.elect.interfaces.was.stimmeingabe.ZwischensummeEingabe zwischensummeEingabe : ergebniseingang.getZwischensummeEingaben()) {
      result.addStimmeingabeByErgebniseingang(mappe(zwischensummeEingabe, maps));
    }

    return result;
  }

  private ZwischensummeEingabe mappe(de.ivu.elect.interfaces.was.stimmeingabe.ZwischensummeEingabe zwischensummeEingabe, WasStimmeingabeXMLImportMapperMaps maps) {
    ZwischensummeEingabe result = new ZwischensummeEingabe();
    mappeStimmeingabe(result, zwischensummeEingabe, maps);

    result.setSummenIndex(zwischensummeEingabe.getSummenIndex());
    result.setSummenName(zwischensummeEingabe.getSummenName());

    return result;

  }

  private Stimmeingabe mappe(de.ivu.elect.interfaces.was.stimmeingabe.Stimmeingabe stimmeingabe, WasStimmeingabeXMLImportMapperMaps maps) {
    Stimmeingabe result = new Stimmeingabe();
    mappeStimmeingabe(result, stimmeingabe, maps);

    return result;
  }

  private void mappeStimmeingabe(Stimmeingabe result, de.ivu.elect.interfaces.was.stimmeingabe.Stimmeingabe stimmeingabe, WasStimmeingabeXMLImportMapperMaps maps) {
    result.setAnzahlEingabe(stimmeingabe.getAnzahlEingabe());
    result.setStimmenart(AbstractWahlExternInternMapper.mappe(stimmeingabe.getStimmenart())); // TODO "irgendwann" mal schöner machen un Referenz auf  WahlExternInternMapper entfernen?
    result.setGruppeImGebiet(maps.getGruppeImGebietFuerReferenz(stimmeingabe.getGruppeImGebietReferenz()));
    result.setListenkandidatur(maps.getListenkandidaturFuerReferenz(stimmeingabe.getListenkandidatur()));
    result.setReferendumOption(maps.getReferendumOptionFuerReferenz(stimmeingabe.getReferendumOption()));

    for (Stimmeneingabefehler stimmeneingabefehler : stimmeingabe.getStimmeneingabefehlerListe()) {
      result.addStimmeingabefehlerByStimmeingabe(mappe(stimmeneingabefehler));
    }
  }

  private Stimmeingabefehler mappe(Stimmeneingabefehler stimmeneingabefehler) {
    Stimmeingabefehler result = new Stimmeingabefehler();
    result.setBeschreibung(stimmeneingabefehler.getBeschreibung());
    result.setFehlerart(mappe(stimmeneingabefehler.getFehlerart()));

    if (stimmeneingabefehler.getStimmeingabePlausiTyp() != null) {
      result.setStimmeingabePlausiTyp(stimmeingabePlausiMapper.mappe(stimmeneingabefehler.getStimmeingabePlausiTyp()));
    }

    result.setErwarteteAnzahlEingabe(stimmeneingabefehler.getErwarteteAnzahlEingabe());

    return result;
  }

  protected Stimmeingabefehlerart mappe(de.ivu.elect.interfaces.was.stimmeingabe.Stimmeingabefehlerart fehlerart) {
    return Stimmeingabefehlerart.valueOf(fehlerart.name());
  }

  public Ergebniseingangsherkunft mappe(de.ivu.elect.interfaces.was.stimmeingabe.Ergebniseingangsherkunft ergebniseingangsherkunft) {
    return Ergebniseingangsherkunft.valueOf(ergebniseingangsherkunft.name());
  }

  protected Ergebniseingabestufe mappe(de.ivu.elect.interfaces.was.stimmeingabe.Ergebniseingabestufe ergebniseingabestufe) {
    if (ergebniseingabestufe == null) {
      return Ergebniseingabestufe.ENDSTUFE;
    }

    return Ergebniseingabestufe.valueOf(ergebniseingabestufe.name());
  }

  protected Ergebniseingangsfehlerstatus mappe(de.ivu.elect.interfaces.was.stimmeingabe.Ergebniseingangsfehlerstatus ergebniseingangsfehlerstatus) {
    return Ergebniseingangsfehlerstatus.valueOf(ergebniseingangsfehlerstatus.name());
  }

  public Ergebniseingangsart mappe(de.ivu.elect.interfaces.was.stimmeingabe.Ergebniseingangsart ergebniseingangsart) {
    return Ergebniseingangsart.valueOf(ergebniseingangsart.name());
  }

  @Override
  public Violations validateInputFormat(InputStream is, StimmeingabeImportContext context, FileKonfiguration fileKonfiguration) {
    return validateXml(is, xmlMarshaller, schemaLoader, SCHEMA_RESOURCE_NAME, ErfassteStimmen.class);
  }
}
