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

import static java.util.Arrays.asList;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.inject.Inject;

import de.ivu.elect.business.person.entity.PersonStatus;
import de.ivu.elect.business.person.entity.QPersonendaten;
import de.ivu.elect.business.personenpruefung.control.PersonenKonfliktByPersonendatenNamenComparator;
import de.ivu.elect.business.personenpruefung.entity.PersonenKonflikt;
import de.ivu.elect.business.personenpruefung.entity.PersonenKonfliktArt;
import de.ivu.elect.business.personenpruefung.entity.PersonenKonfliktStatus;
import de.ivu.elect.business.personenpruefung.entity.QPersonenKonflikt;
import de.ivu.elect.jee.entity.EagerLoadingCollector;
import de.ivu.elect.jee.entity.QueryDsl;

public abstract class AbstractPersonenKonfliktSearch<P extends AbstractPersonenKonfliktSearchParameters> implements Serializable {

  private static final long serialVersionUID = 1L;

  protected static final Collection<PersonenKonfliktStatus> PERSONEN_KONFLIKT_STATUS_DEFAULTS = asList(PersonenKonfliktStatus.values());

  protected static final Collection<PersonenKonfliktArt> PERSONEN_KONFLIKT_ART_DEFAULTS = asList(PersonenKonfliktArt.values());

  private static final Comparator<PersonenKonflikt> DEFAULT_COMPARATOR = new PersonenKonfliktByPersonendatenNamenComparator();

  @Inject
  protected transient PersonenKonfliktExtendedDAO personenKonfliktDao;

  public AbstractPersonenKonfliktSearch() {
    super();
  }

  public List<PersonenKonflikt> getSearchResult(P searchParameters, EagerLoadingCollector<PersonenKonflikt> eagerLoadingCollector) {
    QPersonendaten personendaten = QPersonendaten.personendaten;
    QPersonenKonflikt personenKonflikt = QPersonenKonflikt.personenKonflikt;

    QueryDsl queryDsl = createQueryDsl(personendaten, personenKonflikt, searchParameters);

    List<PersonenKonflikt> result = new ArrayList<>(queryDsl.list(personenKonflikt));
    sortSearchResult(result, searchParameters);

    return eagerLoadingCollector.eagerLoad(result);
  }

  protected QueryDsl createQueryDsl(QPersonendaten personendaten, QPersonenKonflikt personenKonflikt, P searchParameters) {
    QueryDsl queryDsl = personenKonfliktDao.getQueryDsl();
    queryDsl.from(personenKonflikt, personendaten);

    /*
     * NOTE, dass der 'Join' mit dem EntityPath Personendaten explizit erfolgt und das dieser verwendet werden kann, um das Suchergebnis ggf. weiter einzuschränken, bspw. um einen weiteren 'Join'
     * anhand Personendaten.id mit dem EntityPath WahlvorschlagsPerson durchzuführen.
     */
    queryDsl.getQuery().where(personenKonflikt.personendaten.eq(personendaten));

    queryDsl.inIfNotEmpty(personenKonflikt.personenKonfliktStatus, getPersonenKonfliktStatusOrDefaults(searchParameters));
    queryDsl.inIfNotEmpty(personenKonflikt.personenKonfliktArt, getPersonenKonfliktArtOrDefaults(searchParameters));

    queryDsl.likeNormalizedIfNotBlank(personendaten.familiennameNormalisiert, searchParameters.getFamilienname());
    queryDsl.likeInAnyNormalizedIfNotBlank(personendaten.rufnameNormalisiert, personendaten.vornamenNormalisiert, searchParameters.getVornameSeparator(), searchParameters.getVorname());

    queryDsl.getQuery().where(personendaten.personStatus.ne(PersonStatus.UNGEPRUEFT));

    return queryDsl;
  }

  protected Collection<PersonenKonfliktStatus> getPersonenKonfliktStatusOrDefaults(P searchParameters) {
    if (searchParameters.hasPersonenKonfliktStatus()) {
      return asList(searchParameters.getPersonenKonfliktStatus());
    }

    return PERSONEN_KONFLIKT_STATUS_DEFAULTS;
  }

  protected Collection<PersonenKonfliktArt> getPersonenKonfliktArtOrDefaults(P searchParameters) {
    if (searchParameters.hasPersonenKonfliktArt()) {
      return asList(searchParameters.getPersonenKonfliktArt());
    }

    return PERSONEN_KONFLIKT_ART_DEFAULTS;
  }

  protected void sortSearchResult(List<PersonenKonflikt> result, P searchParameters) {
    Collections.sort(result, getSearchResultComparator(searchParameters));
  }

  protected Comparator<PersonenKonflikt> getSearchResultComparator(P searchParameters) {
    return DEFAULT_COMPARATOR;
  }
}
