package cn.hmsoft.frame.timer;

import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.quartz.CronExpression;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.SchedulerFactory;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

import cn.hmsoft.application.AppHelper;
import cn.hmsoft.application.SpringHelper;
import cn.hmsoft.frame.constants.FrameStatus;
import cn.hmsoft.frame.data.dao.FrameTimerDao;
import cn.hmsoft.frame.data.model.FrameTimer;
import cn.hmsoft.helper.LocalDateHelper;
import cn.hmsoft.helper.StringHelper;
import cn.hmsoft.log.LogHelper;

/*************************
 * 系统定时器辅助类
 * 
 * @author zxq
 * @create 2018-05-10 15:31:38
 * @version 5.0.0
 * @email: revisit@126.com
 * @Company: www.hmsoft.cn
 */
public final class FrameTimerUtil {

	private final static String JobGroupName = "HmSoftJobGroup";
	private final static String JobNameStartWith = "HmSoftJobName";
	public final static String TimerJobParamName = "TimerJobParamName";

	private static SchedulerFactory JobSchedulerFactory = null;
	private static Map<Integer, FrameTimer> TimerMap = null;
	/*********************
	 * 系统定时器模块是否正常启用
	 */
	private static boolean FrameTimerStatus = false;
	/***************************
	 * 数据库是否可用
	 */
	private static boolean TimerDatabaseFlag = false;
	/*************************************************
	 * 设置基本参数
	 */
	static {
		synchronized (FrameTimerUtil.class) {
			if (JobSchedulerFactory == null) {
				Properties p = new Properties();
				// 表明scheduler的主线程是否为守护线程
				p.setProperty("org.quartz.scheduler.makeSchedulerThreadDaemon",
						TimerModuleConfig.FrameTimerThreadDaemon);
				// 使用ThreadPool的实现的名字。Quartz自带的线程池是“org.quartz.simpl.SimpleThreadPool”，几乎满足所有用户的需求。它的行为很简单，测试的也很好。它提供了固定大小的线程池，生命周期与Scheduler相同。
				p.setProperty("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
				// 可以是任意的正整数。实际上只有1到100会用到。这是并行执行job可用的线程数。如果只有几个job，一天也只有几次触发，那么一个线程就足够了！如果有成百上千的job，且每一个每一分钟都会触发。那么可能想要让线程的数量达到50或者100了。
				p.setProperty("org.quartz.threadPool.threadCount", "" + TimerModuleConfig.FrameTimerThreadCount);
				// 可以设为true，那么线程池中创建的线程都是守护线程。默认为false
				p.setProperty("org.quartz.threadPool.makeThreadsDaemons", TimerModuleConfig.FrameTimerThreadDaemon);

				TimerMap = new LinkedHashMap<Integer, FrameTimer>();
				try {
					// 启动任务池
					JobSchedulerFactory = new StdSchedulerFactory(p);
					JobSchedulerFactory.getScheduler().start();
					FrameTimerStatus = true;
				} catch (Exception e) {
					LogHelper.error("frame timer module load error , please check quartz's file");
				}
			}
		}

	}

	private static FrameTimerUtil util = null;

	private FrameTimerUtil() {
		TimerMap = new LinkedHashMap<Integer, FrameTimer>();
	}

	public static FrameTimerUtil getInstance() {
		if (util == null) {
			synchronized (FrameTimerUtil.class) {
				if (util == null)
					util = new FrameTimerUtil();
				// 只有正常初始quartz之后，以下操作才有意义
				if (FrameTimerStatus == true) {
					List<FrameTimer> timerArray = SpringHelper.getBean(FrameTimerDao.class)
							.listTimer(AppHelper.AppName);
					for (FrameTimer timer : timerArray) {
						util.initTimer(timer);
					}

				}
			}
		}
		return util;
	}

	/************************
	 * 启动一个定时器
	 */
	public boolean initTimer(FrameTimer timer) {
		if (!timer.getTimer_status().equals(FrameStatus.Active.toString())
				|| (timer.getTimer_end_time() != null && timer.getTimer_end_time().isBefore(LocalDateTime.now())))
			return false;
		String timer_cron = timer.getTimer_cron();
		if (StringHelper.isEmpty(timer_cron)) {
			// 一次性任务，直接运行
			FrameTimerConfig config = null;
			try {
				config = (FrameTimerConfig) Class.forName(timer.getTimer_class()).newInstance();
			} catch (Exception e) {
				// 不是FrameTimerConfig子类，无法正常初始化
				LogHelper.error(
						"frame timer [" + timer.getTimer_name() + "] timer_class instanct fail,please check code !");
				return false;
			}
			timer.prepareParamMap();
			// 直接执行..
			try {
				config.execute(timer);
				return true;
			} catch (Exception e) {
				LogHelper.log(e);
				return false;
			}
		} else {
			// 定时任务
			// 判断表达式是否正确
			if (!CronExpression.isValidExpression(timer_cron)) {
				LogHelper.error("frame timer [" + timer.getTimer_name() + "]，cron expression [" + timer.getTimer_cron()
						+ "] is error !");
				return false;
			}
			// 初始化JOB
			try {
				JobKey key = new JobKey(JobNameStartWith + timer.getTimer_id(), JobGroupName);

				@SuppressWarnings("unchecked")
				JobDetail jobDetail = JobBuilder.newJob((Class<Job>) Class.forName(timer.getTimer_class()))
						.withIdentity(key).build();
				jobDetail.getJobDataMap().put(TimerJobParamName, timer);

				LocalDateTime start_time = timer.getTimer_start_time(), end_time = timer.getTimer_end_time();

				// 没有配置结束时间，加30年
				if (end_time == null)
					end_time = LocalDateTime.now().plusYears(30);
				// 如果没有开始时间，10秒后开始
				if (start_time == null)
					start_time = LocalDateTime.now().plusSeconds(10);

				CronTrigger trigger = TriggerBuilder.newTrigger()
						.withIdentity(JobNameStartWith + timer.getTimer_id(), JobGroupName)
						.withSchedule(CronScheduleBuilder.cronSchedule(timer_cron))
						.startAt(LocalDateHelper.toDate(start_time)).endAt(LocalDateHelper.toDate(end_time)).build();
				JobSchedulerFactory.getScheduler().scheduleJob(jobDetail, trigger);

				if (trigger.getNextFireTime() != null)
					timer.setTimer_next_time(LocalDateHelper.toLocalDateTime(trigger.getNextFireTime()));
				// 如果数据库可用，更新数据库
				if (TimerDatabaseFlag)
					SpringHelper.AppGobalDao.update(timer);
				LogHelper.info("frame timer [" + timer.getTimer_name() + "] init:" + timer.getTimer_class());

				TimerMap.put(timer.getTimer_id(), timer);
				// 如果是自动启动，马上进行调度
				if (timer.getAuto_run_flag().equals(FrameStatus.Active.toString()))
					JobSchedulerFactory.getScheduler().triggerJob(key);
			} catch (Exception e) {
				LogHelper.error(e);
				return false;
			}
		}
		return true;
	}
	public boolean shutdown() {
		try {
			JobSchedulerFactory.getScheduler().shutdown();
			return true;
		} catch (Exception e) {
			LogHelper.error(e);
			return false;
		}
	}

}
