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

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

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;

import javax.inject.Inject;

import de.ivu.elect.business.stimmeingabe.boundary.plausi.StimmeingabePlausiDefinitionsResolver;
import de.ivu.elect.business.stimmeingabe.control.StimmeingabeKopfzahlKonfiguration;
import de.ivu.elect.business.stimmeingabe.control.plausi.StimmeingabePlausiDefinitions;
import de.ivu.elect.business.stimmeingabe.control.plausi.StimmeingabePlausiDefinitionsContext;
import de.ivu.elect.business.stimmeingabe.entity.StimmeingabePlausiKonfiguration;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahl.entity.WahlParameterExtendedLoader;
import de.ivu.elect.jee.presentation.dynamicform.BooleanFilterKriterium;
import de.ivu.elect.presentation.stimmeingabe.StimmeingabePlausiKonfigurationComparator;

public class StimmeingabePlausiKonfigurationSearch extends AbstractStimmeingabePlausiKonfigurationSearch {

  private static final Comparator<StimmeingabePlausiKonfiguration> DEFAULT_COMPARATOR = new StimmeingabePlausiKonfigurationComparator();

  @Inject
  private transient WahlParameterExtendedLoader wahlParameterExtendedLoader;

  @Inject
  private transient StimmeingabePlausiDefinitionsResolver stimmeingabePlausiDefinitionsResolver;

  @Inject
  private transient StimmeingabeKopfzahlKonfiguration stimmeingabeKopfzahlKonfiguration;

  public StimmeingabePlausiKonfigurationSearch() {
    super();
  }

  @Override
  protected List<StimmeingabePlausiKonfiguration> querySearchResult(StimmeingabePlausiKonfigurationSearchParameters searchParameters) {
    List<StimmeingabePlausiKonfiguration> result = new ArrayList<>(findStimmeingabePlausiDefinitions(searchParameters) //
        .map(stimmeingabePlausiDefinitions -> collectStimmeingabePlausiDefinitions(stimmeingabePlausiDefinitions, searchParameters)) //
        .orElse(emptyList()));

    result.sort(DEFAULT_COMPARATOR);

    return result;
  }

  private List<StimmeingabePlausiKonfiguration> collectStimmeingabePlausiDefinitions(StimmeingabePlausiDefinitions stimmeingabePlausiDefinitions,
      StimmeingabePlausiKonfigurationSearchParameters searchParameters) {
    List<StimmeingabePlausiKonfiguration> result = new ArrayList<>();

    Wahl wahl = searchParameters.getSelectedWahl();
    StimmeingabePlausiDefinitionsContext context = new StimmeingabePlausiDefinitionsContext(dao, wahlParameterExtendedLoader);

    if (searchParameters.hasErgebniseingangsart() && searchParameters.hasGebietsart()) {
      result.addAll(stimmeingabePlausiDefinitions.collectStimmeingabePlausiKonfigurationRelevant(context, stimmeingabeKopfzahlKonfiguration, wahl, searchParameters.getErgebniseingangsart(),
          searchParameters.getGebietsart()));
    } else {
      result.addAll(stimmeingabePlausiDefinitions.collectStimmeingabePlausiKonfigurationRelevant(context, stimmeingabeKopfzahlKonfiguration, wahl).stream() //
          .filter(byErgebniseingangsart(searchParameters).and(byGebietsart(searchParameters))) //
          .collect(toList()));
    }

    return result.stream() //
        .filter( //
            byFehlerart(searchParameters) //
                .and(byStatus(searchParameters)) //
                .and(byStimmeingabePlausiKonfigurationDefaultOverridden(searchParameters)) //
        ).collect(toList());
  }

  private Predicate<StimmeingabePlausiKonfiguration> byErgebniseingangsart(StimmeingabePlausiKonfigurationSearchParameters searchParameters) {
    return stimmeingabePlausiKonfiguration -> !searchParameters.hasErgebniseingangsart() || searchParameters.getErgebniseingangsart().equals(stimmeingabePlausiKonfiguration.getErgebniseingangsart());
  }

  private Predicate<StimmeingabePlausiKonfiguration> byGebietsart(StimmeingabePlausiKonfigurationSearchParameters searchParameters) {
    return stimmeingabePlausiKonfiguration -> !searchParameters.hasGebietsart() || searchParameters.getGebietsart().equals(stimmeingabePlausiKonfiguration.getGebietsart());
  }

  private Predicate<StimmeingabePlausiKonfiguration> byFehlerart(StimmeingabePlausiKonfigurationSearchParameters searchParameters) {
    return stimmeingabePlausiKonfiguration -> !searchParameters.hasFehlerart() || searchParameters.getFehlerart().equals(stimmeingabePlausiKonfiguration.getFehlerart());
  }

  private Predicate<StimmeingabePlausiKonfiguration> byStatus(StimmeingabePlausiKonfigurationSearchParameters searchParameters) {
    return stimmeingabePlausiKonfiguration -> !searchParameters.hasStatus() || searchParameters.getStatus().equals(stimmeingabePlausiKonfiguration.getStatus());
  }

  private Predicate<StimmeingabePlausiKonfiguration> byStimmeingabePlausiKonfigurationDefaultOverridden(StimmeingabePlausiKonfigurationSearchParameters searchParameters) {
    return stimmeingabePlausiKonfiguration -> {
      boolean stimmeingabePlausiKonfigurationDefaultOverridden = stimmeingabePlausiKonfiguration.isStimmeingabePlausiKonfigurationDefaultOverridden();
      BooleanFilterKriterium stimmeingabePlausiKonfigurationDefaultOverriddenFilterKriterium = searchParameters.getStimmeingabePlausiKonfigurationDefaultOverriddenFilterKriterium();

      return stimmeingabePlausiKonfigurationDefaultOverriddenFilterKriterium == null
          || BooleanFilterKriterium.TRUE.equals(stimmeingabePlausiKonfigurationDefaultOverriddenFilterKriterium) && stimmeingabePlausiKonfigurationDefaultOverridden
          || BooleanFilterKriterium.FALSE.equals(stimmeingabePlausiKonfigurationDefaultOverriddenFilterKriterium) && !stimmeingabePlausiKonfigurationDefaultOverridden;
    };
  }

  public Optional<StimmeingabePlausiDefinitions> findStimmeingabePlausiDefinitions(StimmeingabePlausiKonfigurationSearchParameters searchParameters) {
    return getStimmeingabePlausiDefinitionsResolver().findStimmeingabePlausiDefinitions(searchParameters.getSelectedWahl());
  }

  private StimmeingabePlausiDefinitionsResolver getStimmeingabePlausiDefinitionsResolver() {
    return stimmeingabePlausiDefinitionsResolver;
  }
}
