
package de.ivu.elect.jee.cron;

import java.io.Serializable;
import java.util.List;
import java.util.Optional;

import javax.annotation.Resource;
import javax.ejb.ScheduleExpression;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ivu.elect.jee.faces.Messages;

/**
 * Vorlage: http://www.hascode.com/2012/06/task-scheduling-in-java-ee-6-on-glassfish-using-the-timer-service/
 * <p/>
 * Abgeleitete Klasse mit @Startup und @Singleton annotieren
 */
public abstract class AbstractCronTimer {

  public static final String DEAKTIVIERT = "";

  public static final String MINUETLICH = "* * * * *";

  public static final String STUENDLICH = "0 * * * *";

  public static final String MITTERNACHTS = "0 0 * * *";

  public static final String MITTERNACHTS_DIENSTAG_BIS_SAMSTAG = "0 0 * * 2-6"; // Nicht Sonntags oder Montags (Wahlabende)

  private final Logger LOG = LoggerFactory.getLogger(this.getClass());

  @Resource
  protected TimerService timerService;

  @Inject
  protected transient Messages messages;

  public AbstractCronTimer() {
    super();
  }

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void createTimer() {
    createTimer(getCrontabs());
  }

  protected abstract List<Crontab> getCrontabs();

  @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
  public void createTimer(List<Crontab> crontabs) {
    if (isDisabled()) {
      return;
    }

    LOG.info("Starte Timer für '" + getJobName() + "', beende bestehende Timer");

    for (Timer timer : timerService.getTimers()) {
      if (timer != null) {
        try {
          LOG.info("Beende Timer: " + timer);
          timer.cancel();
          LOG.info("Beende Timer: " + timer + " - erfolgreich");
        } catch (Exception e) {
          LOG.warn("Beende Timer: " + timer + " - Fehler: " + e.getMessage());
        }
      }
    }

    LOG.info("Starte Timer für '" + getJobName() + "'");

    for (Crontab crontab : crontabs) {
      Optional<ScheduleExpression> seOptional;
      try {
        seOptional = crontab.getScheduleExpression();
      } catch (Exception e) {
        logError(getJobName(), "timer.not.started.crontab.format.wrong", crontab.getPayload(), e);
        seOptional = Optional.empty();
      }
      if (seOptional.isPresent()) {
        ScheduleExpression cronScheduleExpresison = seOptional.get();
        LOG.info("'" + getJobName() + "' Timer wird mit crontab: '" + cronScheduleExpresison + "' initialisiert für Payload: "
            + crontab.getPayload());
        try {
          Timer timer = timerService.createCalendarTimer(cronScheduleExpresison, new TimerConfig(crontab.getPayload(), false));
          LOG.info("'" + getJobName() + "' Timer erstellt: ScheduleExpression=" + cronScheduleExpresison + ", timer=" + timer
              + ",  für Payload: " + crontab.getPayload());

        } catch (Exception e) {
          logError(getJobName(), "timer.not.started.initialise.error", crontab.getPayload(), e);
        }
      } else {
        LOG.warn("'" + getJobName() + "' Timer NICHT gestartet, kein crontab konfiguriert für Payload: " + crontab.getPayload());
      }
    }

    if (!crontabs.isEmpty()) {
      postCreateTimer();
    }
  }

  protected void postCreateTimer() {
    // nichts zu tun
  }

  protected void logError(String jobName, String messageKey, Serializable payload, Exception e) {
    LOG.error("'" + jobName + "'" + messages.getMessageForMessageKey(messageKey, payload, e.getLocalizedMessage()), e);
  }

  public abstract String getJobName();

  @Timeout
  public void handleTimer(Timer timer) {
    if (isDisabled()) {
      return;
    }
    if (isLoggeHandleTimeout()) {
      LOG.info("HandleTimer für Job '" + getJobName() + "', info: " + timer.getInfo());
    }
    timeout(timer.getInfo());
    if (isLoggeHandleTimeout()) {
      LOG.info("HandleTimer für Job '" + getJobName() + "', info: " + timer.getInfo() + " - done");
    }
  }

  protected boolean isLoggeHandleTimeout(){
    return true;
  }

  protected abstract void timeout(Serializable payload);

  protected boolean isDisabled() {
    return false;
  }

  public boolean isAktiv() {
    if (isDisabled()) {
      return false;
    }

    try {
      for (Timer timer : timerService.getTimers()) {
        if (timer.getTimeRemaining() > -1) {
          return true;
        }
      }
    } catch (Exception e) {
      // egal
    }

    return false;
  }
}
