/*
 * Copyright 2010 LinkedIn, Inc
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package azkaban.scheduler;

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

import azkaban.user.User;
import azkaban.utils.Utils;


/**
 * Schedule for a job instance. This is decoupled from the execution.
 * 
 * @author Richard Park
 * 
 */
public class ScheduledFlow {

	//use projectId.flowId to form a unique scheduleId
	private final String scheduleId;	
    
    private final ReadablePeriod period;
    private DateTime nextExecTime;
    private final String user;
    private final String userSubmit;
    private final DateTime submitTime;
    private final DateTime firstSchedTime;
//    private SchedStatus schedStatus;
    
    public enum SchedStatus {
    	LASTSUCCESS ("lastsuccess"), 
    	LASTFAILED ("lastfailed"), 
    	LASTPAUSED ("lastpaused");
    	
    	private final String status;
    	SchedStatus(String status){
    		this.status = status;
    	}
    	private String status(){
    		return this.status;
    	}
    }

    /**
     * Constructor 
     * 
     * @param jobName Unique job name
     * @param nextExecution The next execution time
     * @param ignoreDependency 
     */
    public ScheduledFlow(String scheduleId,
    					String user,
    					String userSubmit,
    					DateTime submitTime,
    					DateTime firstSchedTime,
                        DateTime nextExecution) {
        this(scheduleId, user, userSubmit, submitTime, firstSchedTime, nextExecution, null);
    }

    /**
     * Constructor
     * 
     * @param jobId
     * @param nextExecution
     * @param period
     * @param ignoreDependency
     */
    public ScheduledFlow(String scheduleId,
    					String user,
    					String userSubmit,
    					DateTime submitTime,
    					DateTime firstSchedTime,
                        DateTime nextExecution,
                        ReadablePeriod period) {
        super();
        this.scheduleId = Utils.nonNull(scheduleId);
        this.user = user;
        this.userSubmit = userSubmit;
        this.submitTime = submitTime;
        this.firstSchedTime = firstSchedTime;
        this.period = period;
        this.nextExecTime = Utils.nonNull(nextExecution);
//        this.schedStatus = SchedStatus.LASTSUCCESS;
    }
    
    public ScheduledFlow(String scheduleId, 
    		String user, 
    		String userSubmit,
			DateTime submitTime, 
			DateTime firstSchedTime,
			ReadablePeriod period) {
    	super();
        this.scheduleId = Utils.nonNull(scheduleId);
        this.user = user;
        this.userSubmit = userSubmit;
        this.submitTime = submitTime;
        this.firstSchedTime = firstSchedTime;
        this.period = period;
        this.nextExecTime = new DateTime();
//        this.schedStatus = SchedStatus.LASTSUCCESS;
	}

    public ScheduledFlow(String scheduleId, 
    		String user, 
    		String userSubmit,
			DateTime submitTime, 
			DateTime firstSchedTime) {
    	super();
        this.scheduleId = Utils.nonNull(scheduleId);
        this.user = user;
        this.userSubmit = userSubmit;
        this.submitTime = submitTime;
        this.firstSchedTime = firstSchedTime;
        this.period = null;
        this.nextExecTime = new DateTime();
//        this.schedStatus = SchedStatus.LASTSUCCESS;
	}

//	public SchedStatus getSchedStatus() {
//		return this.schedStatus;
//	}
//
//	public void setSchedStatus(SchedStatus schedStatus) {
//		this.schedStatus = schedStatus;
//	}

	/**
     * Updates the time to a future time after 'now' that matches the period description.
     * 
     * @return
     */
    public boolean updateTime() {
    	if (nextExecTime.isAfterNow()) {
    		return true;
    	}
    	
        if (period != null) {
        	DateTime other = getNextRuntime(nextExecTime, period);
    		
    		this.nextExecTime = other;
    		return true;
    	}

        return false;
    }
    
    /**
     * Calculates the next runtime by adding the period.
     * 
     * @param scheduledDate
     * @param period
     * @return
     */
    private DateTime getNextRuntime(DateTime scheduledDate, ReadablePeriod period)
    {
        DateTime now = new DateTime();
        DateTime date = new DateTime(scheduledDate);
        int count = 0;
        while (!now.isBefore(date)) {
            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;
    }
    
    /**
     * Returns the unique id of the job to be run.
     * @return
     */

    /**
     * Returns true if the job recurrs in the future
     * @return
     */
    public boolean isRecurring() {
        return this.period != null;
    }

    /**
     * Returns the recurrance period. Or null if not applicable
     * @return
     */
    public ReadablePeriod getPeriod() {
        return period;
    }

	public DateTime getFirstSchedTime() {
		return firstSchedTime;
	}

	/**
     * Returns the next scheduled execution
     * @return
     */
    public DateTime getNextExecTime() {
        return nextExecTime;
    }

	public String getUserSubmit() {
		return userSubmit;
	}

	public DateTime getSubmitTime() {
		return submitTime;
	}

	@Override
    public String toString()
    {
        return "ScheduledFlow{" +
//        	   "scheduleStatus=" + schedStatus +
               "nextExecTime=" + nextExecTime +
               ", period=" + period +
               ", firstSchedTime=" + firstSchedTime +
               ", submitTime=" + submitTime +
               ", userSubmit=" + userSubmit +
               ", user=" + user +
               ", scheduleId='" + scheduleId + '\'' +
               '}';
    }

	public String getUser() {
		return user;
	}
	
	public String getScheduleId() {
		return scheduleId;
	}
	
	public String getFlowId(){
		return this.scheduleId.split("\\.")[1];
	}
	
	public String getProjectId(){
		return this.scheduleId.split("\\.")[0];
	}
}
