
package de.ivu.elect.business.wahlrechtspruefung.entity;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import de.ivu.elect.business.kandidatur.entity.Direktkandidatur;
import de.ivu.elect.business.kandidatur.entity.Kandidatur;
import de.ivu.elect.business.kandidatur.entity.Listenkandidatur;
import de.ivu.elect.business.wahl.entity.Wahl;
import de.ivu.elect.business.wahlrechtspruefung.boundary.AbstractPruefkriteriumDAO;
import de.ivu.elect.business.wahlvorschlag.entity.Direktkandidat;
import de.ivu.elect.business.wahlvorschlag.entity.Liste;
import de.ivu.elect.business.wahlvorschlag.entity.Wahlvorschlag;

public enum PruefkriteriumBezug {

  ALLE_WAHLVORSCHLAEGE {

    @Override
    public List< ? extends Object> holeZuPruefendeEntitaeten(Wahl wahl, PruefkriteriumDaoContainer pruefkriteriumDaoContainer) {
      return pruefkriteriumDaoContainer.getWahlvorschlagDAO().findByWahl(wahl);
    }

    @Override
    public void setzeZuPruefendeEntitaet(Wahlrechtspruefung wahlrechtspruefung, Object o, PruefkriteriumDaoContainer pruefkriteriumDaoContainer) {
      Wahlvorschlag wahlvorschlag = (Wahlvorschlag) o;

      wahlrechtspruefung.setWahlvorschlag(wahlvorschlag);

      if (wahlvorschlag.isDirektkandidat()) {
        wahlrechtspruefung.setKandidatur(attach(wahlvorschlag.castTo(Direktkandidat.class).getEntity().getDirektkandidatur(), pruefkriteriumDaoContainer));
      }
    }

    private Direktkandidatur attach(Direktkandidatur direktkandidatur, PruefkriteriumDaoContainer pruefkriteriumDaoContainer) {
      if (direktkandidatur.isPersistent()) {
        return pruefkriteriumDaoContainer.getDirektkandidaturDAO().attach(direktkandidatur);
      }

      throw new IllegalStateException("Direktkandidatur muss persistent sein! [direktkandidatur = " + direktkandidatur + "]");
    }

    @Override
    public boolean isBezugFuer(Object o) {
      return o instanceof Wahlvorschlag;
    }
  },

  NUR_LISTEN {

    @Override
    public List< ? extends Object> holeZuPruefendeEntitaeten(Wahl wahl, PruefkriteriumDaoContainer pruefkriteriumDaoContainer) {
      return pruefkriteriumDaoContainer.getListeDAO().findByWahl(wahl);
    }

    @Override
    public void setzeZuPruefendeEntitaet(Wahlrechtspruefung wahlrechtspruefung, Object o, PruefkriteriumDaoContainer pruefkriteriumDaoContainer) {
      wahlrechtspruefung.setWahlvorschlag((Wahlvorschlag) o);
    }

    @Override
    public boolean isBezugFuer(Object o) {
      return o instanceof Liste;
    }
  },
  ALLE_KANDIDATUREN {

    @Override
    public List< ? extends Object> holeZuPruefendeEntitaeten(Wahl wahl, PruefkriteriumDaoContainer pruefkriteriumDaoContainer) {
      return pruefkriteriumDaoContainer.getKandidaturDAO().findByWahl(wahl)/* ELECTKOM-117 */.stream().filter(k -> k.getWahlvorschlag() != null).collect(Collectors.toList());
    }

    @Override
    public void setzeZuPruefendeEntitaet(Wahlrechtspruefung wahlrechtspruefung, Object o, PruefkriteriumDaoContainer pruefkriteriumDaoContainer) {
      Kandidatur kandidatur = (Kandidatur) o;

      wahlrechtspruefung.setWahlvorschlag(kandidatur.getWahlvorschlag());
      wahlrechtspruefung.setKandidatur(kandidatur);
    }

    @Override
    public boolean isBezugFuer(Object o) {
      return o instanceof Kandidatur;
    }
  },
  NUR_LISTENKANDIDATUREN {

    @Override
    public List< ? extends Object> holeZuPruefendeEntitaeten(Wahl wahl, PruefkriteriumDaoContainer pruefkriteriumDaoContainer) {
      return pruefkriteriumDaoContainer.getListenkandidaturDAO().findByWahl(wahl);
    }

    @Override
    public void setzeZuPruefendeEntitaet(Wahlrechtspruefung wahlrechtspruefung, Object o, PruefkriteriumDaoContainer pruefkriteriumDaoContainer) {
      Kandidatur kandidatur = (Kandidatur) o;

      wahlrechtspruefung.setWahlvorschlag(kandidatur.getWahlvorschlag());
      wahlrechtspruefung.setKandidatur(kandidatur);
    }

    @Override
    public boolean isBezugFuer(Object o) {
      return o instanceof Listenkandidatur;
    }
  },
  NUR_DIREKTKANDIDATUREN {

    @Override
    public List< ? extends Object> holeZuPruefendeEntitaeten(Wahl wahl, PruefkriteriumDaoContainer pruefkriteriumDaoContainer) {
      return pruefkriteriumDaoContainer.getDirektkandidaturDAO().findByWahl(wahl);
    }

    @Override
    public void setzeZuPruefendeEntitaet(Wahlrechtspruefung wahlrechtspruefung, Object o, PruefkriteriumDaoContainer pruefkriteriumDaoContainer) {
      Kandidatur kandidatur = (Kandidatur) o;

      wahlrechtspruefung.setWahlvorschlag(kandidatur.getWahlvorschlag());
      wahlrechtspruefung.setKandidatur(kandidatur);
    }

    @Override
    public boolean isBezugFuer(Object o) {
      return o instanceof Direktkandidatur;
    }
  };

  public abstract List< ? extends Object> holeZuPruefendeEntitaeten(Wahl wahl, PruefkriteriumDaoContainer pruefkriteriumDaoContainer);

  public abstract void setzeZuPruefendeEntitaet(Wahlrechtspruefung wahlrechtspruefung, Object o, PruefkriteriumDaoContainer pruefkriteriumDaoContainer);

  public abstract boolean isBezugFuer(Object o);

  public static List<Pruefkriterium> findePruefkriterienFuerWahlUndObject(Wahl wahl, Object o, AbstractPruefkriteriumDAO pruefkriteriumDAO) {
    List<Pruefkriterium> result = new ArrayList<>();

    boolean mindestensEinBezugGefunden = false;

    for (PruefkriteriumBezug pruefkriteriumBezug : values()) {
      if (pruefkriteriumBezug.isBezugFuer(o)) {
        mindestensEinBezugGefunden = true;
        result.addAll(findePruefkriterienFuerWahlUndObject(wahl, pruefkriteriumBezug, pruefkriteriumDAO));
      }
    }

    if (!mindestensEinBezugGefunden) {
      throw new IllegalArgumentException("Keine PruefkriteriumBezug für '" + o + "' gefunden!");
    }

    return result;
  }

  private static List<Pruefkriterium> findePruefkriterienFuerWahlUndObject(Wahl wahl, PruefkriteriumBezug pruefkriteriumBezug, AbstractPruefkriteriumDAO pruefkriteriumDAO) {
    return pruefkriteriumDAO.findByWahlAndBezugOrderByPosition(wahl, pruefkriteriumBezug);
  }
}
