package de.ivu.elect.jee.cron;

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

import javax.ejb.ScheduleExpression;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import static java.util.Optional.of;

/**
 *  on 03.01.2017.
 */
public class Crontab {

  private static final Logger LOG = LoggerFactory.getLogger(Crontab.class);

  private static final String SPLIT_REGEX_CRONTAB = " ";

  private static final int MINUTE_OHNE_SEKUNDE = 0;

  private static final int STUNDE_OHNE_SEKUNDE = 1;

  private static final int TAG_IM_MONAT_OHNE_SEKUNDE = 2;

  private static final int MONAT_OHNE_SEKUNDE = 3;

  private static final int WOCHENTAG_OHNE_SEKUNDE = 4;

  private static final int SEKUNDE = 0;

  private static final int MINUTE_MIT_SEKUNDE = 1;

  private static final int STUNDE_MIT_SEKUNDE = 2;

  private static final int TAG_IM_MONAT_MIT_SEKUNDE = 3;

  private static final int MONAT_MIT_SEKUNDE = 4;

  private static final int WOCHENTAG_MIT_SEKUNDE = 5;

  private final String crontab;

  private final Date start;

  private final Date end;

  private final Serializable payload;

  private Crontab(String crontab, Date start, Date end, Serializable payload) {
    this.crontab = crontab;
    this.start = start;
    this.end = end;
    this.payload = payload;
  }

  public Serializable getPayload() {
    return payload;
  }

  public Optional<ScheduleExpression> getScheduleExpression() {
    if (crontab == null || StringUtils.isBlank(crontab)) {
      return Optional.empty();
    }
    String[] cronTabArray = crontab.trim().split(SPLIT_REGEX_CRONTAB);
    if (cronTabArray.length == 5 || cronTabArray.length == 6) {
      ScheduleExpression se = new ScheduleExpression();
      if (cronTabArray.length == 5) {
        se.minute(filterElect8731(cronTabArray[MINUTE_OHNE_SEKUNDE]))
            .hour(filterElect8731(cronTabArray[STUNDE_OHNE_SEKUNDE]))
            .dayOfMonth(filterElect8731(cronTabArray[TAG_IM_MONAT_OHNE_SEKUNDE]))
            .month(filterElect8731(cronTabArray[MONAT_OHNE_SEKUNDE]))
            .dayOfWeek(filterElect8731(cronTabArray[WOCHENTAG_OHNE_SEKUNDE]));
      } else {
        se.second(filterElect8731(cronTabArray[SEKUNDE]))
            .minute(filterElect8731(cronTabArray[MINUTE_MIT_SEKUNDE]))
            .hour(filterElect8731(cronTabArray[STUNDE_MIT_SEKUNDE]))
            .dayOfMonth(filterElect8731(cronTabArray[TAG_IM_MONAT_MIT_SEKUNDE]))
            .month(filterElect8731(cronTabArray[MONAT_MIT_SEKUNDE]))
            .dayOfWeek(filterElect8731(cronTabArray[WOCHENTAG_MIT_SEKUNDE]));

      }
      if (start != null) {
        se.start(start);
      }
      if (end != null) {
        se.end(end);
      }
      return of(se);
    }

    throw new IllegalStateException("Falsches Crontab-Format! '" + crontab + "'");
  }

  private String filterElect8731(String s) {
    // ELECT-8731
    if (s != null && s.contains("/0")) {
      LOG.warn("Fehlerhafte Cron-Expression - nutze '*' statt: '" + s + "'");
      return "*";
    }
    return s;
  }

  public static Crontab forCrontab(String crontab) {
    return new Crontab(crontab, null, null, "");
  }

  public static Crontab create(String crontab, Serializable payload) {
    return new Crontab(crontab, null, null, payload);
  }

  public static Crontab create(String crontab, Date start, Date end, Serializable payload) {
    return new Crontab(crontab, start, end, payload);
  }

}


