package azkaban.scheduler;

import java.util.HashMap;
import java.util.Map;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.ReadablePeriod;

import azkaban.trigger.ConditionChecker;
import azkaban.utils.Utils;

public class BasicTimeChecker implements ConditionChecker {

	public static final String type = "BasicTimeChecker";
	
	private DateTime firstCheckTime;
	private DateTime nextCheckTime;
	private DateTimeZone timezone;
	private boolean isRecurring = true;
	private boolean skipPastChecks = true;
	private ReadablePeriod period;
	
	private String id = type; 
	
	public BasicTimeChecker(
			String id,
			DateTime firstCheckTime,
			DateTimeZone timezone,
			boolean isRecurring, 
			boolean skipPastChecks,
			ReadablePeriod period) {
		this.id = id;
		this.firstCheckTime = firstCheckTime;
		this.timezone = timezone;
		this.isRecurring = isRecurring;
		this.skipPastChecks = skipPastChecks;
		this.period = period;
		this.nextCheckTime = new DateTime(firstCheckTime);
		this.nextCheckTime = calculateNextCheckTime();
	}
	
	public DateTime getFirstCheckTime() {
		return firstCheckTime;
	}

	public boolean isRecurring() {
		return isRecurring;
	}

	public boolean isSkipPastChecks() {
		return skipPastChecks;
	}

	public ReadablePeriod getPeriod() {
		return period;
	}

	public DateTime getNextCheckTime() {
		return nextCheckTime;
	}
	
//	public BasicTimeChecker(
//			DateTime firstCheckTime,
//			Boolean isRecurring, 
//			Boolean skipPastChecks,
//			String period) {
//		this.firstCheckTime = firstCheckTime;
//		this.isRecurring = isRecurring;
//		this.skipPastChecks = skipPastChecks;
//		this.period = parsePeriodString(period);
//		this.nextCheckTime = new DateTime(firstCheckTime);
//		this.nextCheckTime = calculateNextCheckTime();
//	}
	
	public BasicTimeChecker(
			String id,
			DateTime firstCheckTime,
			DateTimeZone timezone,
			DateTime nextCheckTime,
			boolean isRecurring, 
			boolean skipPastChecks,
			ReadablePeriod period) {
		this.id = id;
		this.firstCheckTime = firstCheckTime;
		this.timezone = timezone;
		this.nextCheckTime = nextCheckTime;
		this.isRecurring = isRecurring;
		this.skipPastChecks = skipPastChecks;
		this.period = period;
	}
	
	@Override
	public Boolean eval() {
		return nextCheckTime.isBeforeNow();
	}

	@Override
	public void reset() {
		this.nextCheckTime = calculateNextCheckTime();
		
	}
	
	/*
	 * TimeChecker format:
	 * type_first-time-in-millis_next-time-in-millis_timezone_is-recurring_skip-past-checks_period
	 */
	@Override
	public String getId() {
//		return getType() + "$" +
//				firstCheckTime.getMillis() + "$" +
//				nextCheckTime.getMillis() + "$" +
//				firstCheckTime.getZone().getID().replace('/', '0') + "$" +
//				//"offset"+firstCheckTime.getZone().getOffset(firstCheckTime.getMillis()) + "_" +
//				(isRecurring == true ? "1" : "0") + "$" +
//				(skipPastChecks == true ? "1" : "0") + "$" +
//				createPeriodString(period);
		return id;
	}

	@Override
	public String getType() {
		return type;
	}

	@SuppressWarnings("unchecked")
	public static ConditionChecker createFromJson(Object obj) throws Exception {
		Map<String, Object> jsonObj = (HashMap<String, Object>) obj;
		if(!jsonObj.get("type").equals(type)) {
			throw new Exception("Cannot create checker of " + type + " from " + jsonObj.get("type"));
		}
		long firstTimeMillis = Long.valueOf((String)jsonObj.get("firstCheckTime"));
		String timezoneId = (String) jsonObj.get("timezone");
		long nextTimeMillis = Long.valueOf((String)jsonObj.get("nextCheckTime"));
		DateTimeZone timezone = DateTimeZone.forID(timezoneId);
		DateTime firstCheckTime = new DateTime(firstTimeMillis).withZone(timezone);
		DateTime nextCheckTime = new DateTime(nextTimeMillis).withZone(timezone);
		boolean isRecurring = Boolean.valueOf((String)jsonObj.get("isRecurring"));
		boolean skipPastChecks = Boolean.valueOf((String)jsonObj.get("skipPastChecks"));
		ReadablePeriod period = Utils.parsePeriodString((String)jsonObj.get("period"));
		String id = (String) jsonObj.get("id");
		return new BasicTimeChecker(id, firstCheckTime, timezone, nextCheckTime, isRecurring, skipPastChecks, period);
	}
	
	public static ConditionChecker createFromJson(HashMap<String, Object> obj) throws Exception {
		Map<String, Object> jsonObj = (HashMap<String, Object>) obj;
		if(!jsonObj.get("type").equals(type)) {
			throw new Exception("Cannot create checker of " + type + " from " + jsonObj.get("type"));
		}
		Long firstTimeMillis = Long.valueOf((String) jsonObj.get("firstCheckTime"));
		String timezoneId = (String) jsonObj.get("timezone");
		long nextTimeMillis = Long.valueOf((String) jsonObj.get("nextCheckTime"));
		DateTimeZone timezone = DateTimeZone.forID(timezoneId);
		DateTime firstCheckTime = new DateTime(firstTimeMillis).withZone(timezone);
		DateTime nextCheckTime = new DateTime(nextTimeMillis).withZone(timezone);
		boolean isRecurring = Boolean.valueOf((String)jsonObj.get("isRecurring"));
		boolean skipPastChecks = Boolean.valueOf((String)jsonObj.get("skipPastChecks"));
		ReadablePeriod period = Utils.parsePeriodString((String)jsonObj.get("period"));
		String id = (String) jsonObj.get("id");
		return new BasicTimeChecker(id, firstCheckTime, timezone, nextCheckTime, isRecurring, skipPastChecks, period);
	}
	
	@Override
	public ConditionChecker fromJson(Object obj) throws Exception{
		return createFromJson(obj);
	}
	
//	public static ConditionChecker createFromJson(String obj) {
//		String str = (String) obj;
//		String[] parts = str.split("\\$");
//		
//		if(!parts[0].equals(type)) {
//			throw new RuntimeException("Cannot create checker of " + type + " from " + parts[0]);
//		}
//		
//		long firstMillis = Long.parseLong(parts[1]);
//		long nextMillis = Long.parseLong(parts[2]);
//		//DateTimeZone timezone = DateTimeZone.forTimeZone(TimeZone.getTimeZone(parts[3]));
//		DateTimeZone timezone = DateTimeZone.forID(parts[3].replace('0', '/'));
//		boolean isRecurring = parts[4].equals("1") ? true : false;
//		boolean skipPastChecks = parts[5].equals("1") ? true : false;
//		ReadablePeriod period = parsePeriodString(parts[6]);
//		
//		return new BasicTimeChecker(new DateTime(firstMillis, timezone), new DateTime(nextMillis, timezone), isRecurring, skipPastChecks, period);
//	}
//	
//	@Override
//	public ConditionChecker fromJson(Object obj) {
//		String str = (String) obj;
//		String[] parts = str.split("_");
//		
//		if(!parts[0].equals(getType())) {
//			throw new RuntimeException("Cannot create checker of " + getType() + " from " + parts[0]);
//		}
//		
//		long firstMillis = Long.parseLong(parts[1]);
//		long nextMillis = Long.parseLong(parts[2]);
//		DateTimeZone timezone = DateTimeZone.forID(parts[3]);
//		boolean isRecurring = Boolean.valueOf(parts[4]);
//		boolean skipPastChecks = Boolean.valueOf(parts[5]);
//		ReadablePeriod period = parsePeriodString(parts[6]);
//		
//		return new BasicTimeChecker(new DateTime(firstMillis, timezone), new DateTime(nextMillis, timezone), isRecurring, skipPastChecks, period);
//	}
	
	private DateTime calculateNextCheckTime(){
		DateTime date = new DateTime(nextCheckTime);
		int count = 0;
		while(!DateTime.now().isBefore(date) && skipPastChecks) {
			if(count > 100000) {
				throw new IllegalStateException("100000 increments of period did not get to present time.");
			}
			
			if(period == null) {
				break;
			}else {
				date = date.plus(period);
			}
			count += 1;
		}
		return date;
	}
	
	@Override
	public Object getNum() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Object toJson() {
		Map<String, Object> jsonObj = new HashMap<String, Object>();
		jsonObj.put("type", type);
		jsonObj.put("firstCheckTime", String.valueOf(firstCheckTime.getMillis()));
		jsonObj.put("timezone", timezone.getID());
		jsonObj.put("nextCheckTime", String.valueOf(nextCheckTime.getMillis()));
		jsonObj.put("isRecurrint", String.valueOf(isRecurring));
		jsonObj.put("skipPastChecks", String.valueOf(skipPastChecks));
		jsonObj.put("period", Utils.createPeriodString(period));
		jsonObj.put("id", id);
		
		return jsonObj;
	}

	@Override
	public void setId(String id) {
		this.id = id;
	}

}
