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

import java.io.File;
import java.math.BigDecimal;
import java.util.Date;

import javax.annotation.Nullable;
import javax.persistence.NoResultException;

import de.ivu.elect.business.konfiguration.entity.AbstractCustomParameterLoader;
import de.ivu.elect.business.konfiguration.entity.CustomParameter;
import de.ivu.elect.business.wahl.boundary.AbstractWahlParameterDAO;
import de.ivu.elect.jee.exception.ElectCommonException;


import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNumeric;

public abstract class AbstractWahlParameterLoader extends AbstractCustomParameterLoader<WahlParameterWerte, WahlParameter, WahlParameterLoaderContext> {

  private static final long serialVersionUID = 1L;

  protected AbstractWahlParameterLoader() {
    super();
  }

  public boolean resolveBooleanValue(WahlOderWahltermin wahlOderWahltermin, WahlParameterWerte wahlParameterWerte) {
    return Boolean.TRUE.toString().equalsIgnoreCase(resolveStringValue(wahlOderWahltermin, wahlParameterWerte));
  }

  public <V extends Enum<?>> V resolveEnumValue(Wahl wahl, WahlParameterWerte wahlParameterWerte, Class<V> enumClass) {
    return super.resolveEnumValue(createContext(wahl, wahlParameterWerte), enumClass);
  }

  public <V extends Enum<?>> V resolveEnumValue(WahlOderWahltermin wahlOderWahltermin, WahlParameterWerte wahlParameterWerte, Class<V> enumClass) {
    if (wahlOderWahltermin.isInstanceOfWahl()) {
      return resolveEnumValue(createContext(wahlOderWahltermin.getWahlIfWahl(), wahlParameterWerte), enumClass);
    }

    return resolveEnumValue(createContext(wahlOderWahltermin.getWahlterminIfWahltermin(), wahlParameterWerte), enumClass);
  }

  @Nullable
  public Date resolveDateValue(WahlOderWahltermin wahlOderWahltermin, WahlParameterWerte wahlParameterWerte) {
    return CustomParameter.parseDate(resolveStringValue(wahlOderWahltermin, wahlParameterWerte));
  }

  @Nullable
  public Long resolveLongValue(WahlOderWahltermin wahlOderWahltermin, WahlParameterWerte wahlParameterWerte) {
    String value = resolveStringValue(wahlOderWahltermin, wahlParameterWerte);

    if (isNumeric(value)) {
      return Long.valueOf(value);
    }

    return null;
  }

  @Nullable
  public BigDecimal resolveBigDecimalValue(WahlOderWahltermin wahlOderWahltermin, WahlParameterWerte wahlParameterWerte) {
    String value = resolveStringValue(wahlOderWahltermin, wahlParameterWerte);

    if (isBlank(value)) {
      return null;
    }

    try {
      return new BigDecimal(value);
    } catch (Exception e) {
      return null;
    }
  }

  @Nullable
  public String resolveStringValue(Wahltermin wahltermin, WahlParameterWerte template) {
    return resolveStringValue(createContext(wahltermin, template), null); // default-value für bestehende Wahltermine
  }

  @Nullable
  public String resolveStringValue(Wahl wahl, WahlParameterWerte template) {
    return resolveStringValue(createContext(wahl, template), null); // default-value für bestehende Wahlen
  }

  @Nullable
  public String resolveStringValue(Wahltermin wahltermin, WahlParameterWerte template, String valueParameterNichtVorhanden) {
    return resolveStringValue(createContext(wahltermin, template), valueParameterNichtVorhanden); // default-value für bestehende Wahltermine
  }

  @Nullable
  public String resolveStringValue(Wahl wahl, WahlParameterWerte template, String valueParameterNichtVorhanden) {
    return resolveStringValue(createContext(wahl, template), valueParameterNichtVorhanden); // default-value für bestehende Wahlen
  }

  @Nullable
  private String resolveStringValue(WahlParameterLoaderContext context, String valueParameterNichtVorhanden) {
    WahlParameter parameter = getParameter(context);

    if (parameter == null) {
      return valueParameterNichtVorhanden;
    }

    return resolveStringValue(context);
  }

  public String resolveStringValue(WahlOderWahltermin wahlOderWahltermin, WahlParameterWerte template) {
    if (wahlOderWahltermin == null) {
      return ""; // ELECT-13914
    }
    if (wahlOderWahltermin.isInstanceOfWahl()) {
      return resolveStringValue(wahlOderWahltermin.castTo(Wahl.class).getEntity(), template);
    }

    if (wahlOderWahltermin.isInstanceOfWahltermin()) {
      return resolveStringValue(wahlOderWahltermin.castTo(Wahltermin.class).getEntity(), template);
    }

    throw new IllegalStateException("Weder Wahl noch Wahltermin: " + wahlOderWahltermin);
  }

  public File resolveFileValue(Wahl wahl, WahlParameterWerte template) {
    return resolveFileValue(createContext(wahl, template));
  }

  private WahlParameterLoaderContext createContext(Wahltermin wahltermin, WahlParameterWerte template) {
    return new WahlParameterLoaderContext(wahltermin, template);
  }

  private WahlParameterLoaderContext createContext(Wahl wahl, WahlParameterWerte template) {
    return new WahlParameterLoaderContext(wahl, template);
  }

  @Override
  protected void handleParameterNichtNichtGesetz(Object template) {
    throw new ElectCommonException(WahlParameterNichtGesetzException.MESSAGE_KEY, template);
  }

  public WahlParameter getParameter(WahlOderWahltermin wahlOderWahltermin, WahlParameterWerte wahlParameterWerte) {
    if (wahlOderWahltermin.isInstanceOfWahl()) {
      return getParameter(wahlOderWahltermin.getWahlIfWahl(), wahlParameterWerte);
    }

    return getParameter(wahlOderWahltermin.getWahlterminIfWahltermin(), wahlParameterWerte);
  }

  public WahlParameter getParameter(Wahltermin wahltermin, WahlParameterWerte wahlParameterWerte) {
    return getParameter(new WahlParameterLoaderContext(wahltermin, wahlParameterWerte));
  }

  public WahlParameter getParameter(Wahl wahl, WahlParameterWerte wahlParameterWerte) {
    return getParameter(new WahlParameterLoaderContext(wahl, wahlParameterWerte));
  }

  @Override
  @Nullable
  public WahlParameter getParameter(WahlParameterLoaderContext context) {
    try {
      WahlParameterWerte template = context.getTemplate();

      if (context.hasWahl()) {
        return getWahlParameterDao().findByWahlAndSchluessel(context.getWahl(), template.getKey());
      }

      if (context.hasWahltermin()) {
        return getWahlParameterDao().findByWahlterminNotWahlAndSchluessel(context.getWahltermin(), template.getKey());
      }
    } catch (NoResultException ignored) {
    }

    return null;
  }

  protected abstract AbstractWahlParameterDAO getWahlParameterDao();
}
