package com.eactive.eai.common.scheduler;

import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;

import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

import org.quartz.CronTrigger;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.TriggerKey;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;

import com.eactive.eai.common.dao.DAOFactory;
import com.eactive.eai.common.util.Logger;

public class Scheduler {
    static Logger logger = Logger.getLogger(Logger.LOGGER_SCHEDULER);
	
	private String schedulerId;
	private String status;
	private org.quartz.Scheduler quartzScheduler;
	
	public String getSchedulerId() {
		return schedulerId;
	}

	public void setSchedulerId(String schedulerId) {
		this.schedulerId = schedulerId;
	}

	public String getStatus() {
		return status;
	}

	public void setStatus(String status) {
		this.status = status;
	}
	
	public org.quartz.Scheduler getQuartzScheduler() {
		return quartzScheduler;
	}

	public void setQuartzScheduler(org.quartz.Scheduler quartzScheduler) {
		this.quartzScheduler = quartzScheduler;
	}
	
	public void clear() {
		try {
            Set<JobKey> keys = quartzScheduler.getJobKeys(GroupMatcher.jobGroupEquals(this.schedulerId));
            Iterator<JobKey> iter = keys.iterator();
            while (iter.hasNext()) {
            	JobKey key = iter.next();
            	logger.warn(key + " deleted");
            	quartzScheduler.deleteJob(key);
            }
	    }
		catch (SchedulerException se) {
			logger.error( se.getMessage(), se);
		}
	}
	
	public static Scheduler create(String schedulerId) throws Exception {
		
		Scheduler scheduler = new Scheduler();
		
		SchedulerFactory sf = new StdSchedulerFactory();
		org.quartz.Scheduler sch = sf.getScheduler();
		scheduler.setSchedulerId(schedulerId);
		scheduler.setQuartzScheduler(sch);
		scheduler.setStatus(SchedulerKeys.STATUS_CREATED);
		return scheduler;
	}
	
	public void start() {
		
		if(SchedulerKeys.STATUS_STOPPED.equals(this.status) 
				|| SchedulerKeys.STATUS_CREATED.equals(this.status)) {
			this.status = SchedulerKeys.STATUS_STARTING;
			try {
				this.quartzScheduler.start();
			}
			catch(Exception ex) {
				logger.error( ex.getMessage(), ex);
				status = SchedulerKeys.STATUS_FAIL;
			}
			status = SchedulerKeys.STATUS_RUNNING;
		}
		else {
			logger.warn("skip start : " + schedulerId + " status [" + this.status + "]");
		}
	}
	
	public void stop() {
		
		if(SchedulerKeys.STATUS_RUNNING.equals(this.status)) {
			this.status = SchedulerKeys.STATUS_STOPPING;
			try {
				this.quartzScheduler.shutdown();
			}
			catch(Exception ex) {
				logger.error( ex.getMessage(), ex);
				status = SchedulerKeys.STATUS_FAIL;
			}
			this.status = SchedulerKeys.STATUS_STOPPED;
			
		}
		else {
			logger.warn("skip stop : " + schedulerId + " status [" + this.status + "]");
		}
	}
	
	public void scheduleAllJobs() {
		try {
			
			DAOFactory factory = DAOFactory.newInstance();
			
			SchedulerDAO dao = (SchedulerDAO) factory.create(SchedulerDAO.class);
	        
			HashMap<String, ScheduleJobInfo> map = dao.getAllSchedule();
			Iterator<String> it = map.keySet().iterator();
			String key = "";
			ScheduleJobInfo info = null;
			
			while(it.hasNext()) {
	        	key = it.next();
	        	info = map.get(key);
	        	logger.info("Schedule this job :" + info);
	        	scheduleJob( info );
	        	
			}
		}
		catch(Exception ex) {
			logger.error( ex.getMessage(), ex);
		}
	}
	
	public void scheduleJob( ScheduleJobInfo info ) throws Exception {
		
		String jobNm = info.getSchedulerId();
		// delete previous Job
		deleteJob(jobNm);
		
		@SuppressWarnings("rawtypes")
		Class jobClass = Class.forName(info.getJobClass());
		
		@SuppressWarnings("unchecked")
		JobDetail job = newJob(jobClass)
        .withIdentity(jobNm, schedulerId)
        .usingJobData("TYPE", info.getSchedDstcd())
        .usingJobData("DIR", info.getScanDirName())
        .usingJobData("MSG", info.getTimerMsg() )
        .build();
		
		CronTrigger trigger = newTrigger()
        .withIdentity(getTriggerName(jobNm), schedulerId)
        .withSchedule(cronSchedule(info.getCronExp()))
        .build();
		
		Date ft = this.quartzScheduler.scheduleJob(job, trigger);
        logger.warn(job.getKey() + " has been scheduled to run at: " + ft
                + " and repeat based on expression: "
                + trigger.getCronExpression());
	}
	
	public void deleteJob(String jobNm) {
		try {
			JobKey key = new JobKey(jobNm, schedulerId);
			this.quartzScheduler.deleteJob(key);
		}
		catch(Exception ex) {
			logger.error( ex.getMessage(), ex);
		}
	}
	
	public void triggerJob(String jobNm) {
		try {
			JobKey key = new JobKey(jobNm, schedulerId);
			this.quartzScheduler.triggerJob(key);
		}
		catch(Exception ex) {
			logger.error( ex.getMessage(), ex);
		}
	}
	
	public void pauseJob(String jobNm) {
		try {
			TriggerKey tkey = new TriggerKey(getTriggerName(jobNm), schedulerId);
			this.quartzScheduler.pauseTrigger(tkey);
			
			JobKey key = new JobKey(jobNm, schedulerId);
			this.quartzScheduler.pauseJob(key);			
		}
		catch(Exception ex) {
			logger.error( ex.getMessage(), ex);
		}
	}

	public void resumeJob(String jobNm) {
		try {
			
			TriggerKey tkey = new TriggerKey(getTriggerName(jobNm), schedulerId);
			this.quartzScheduler.resumeTrigger(tkey);
			
			JobKey key = new JobKey(jobNm, schedulerId);
			this.quartzScheduler.resumeJob(key);
		}
		catch(Exception ex) {
			logger.error( ex.getMessage(), ex);
		}
	}
	
	private String getTriggerName(String jobName) {
		return jobName + "-trigger";
	}

	@Override
	public String toString() {
		return "Scheduler [schedulerId=" + schedulerId + ", status=" + status
				+ ", quartzScheduler=" + quartzScheduler + "]";
	}
	
}
