package com.sdtool.common.tool;

import com.dySweetFishPlugin.tool.lang.ThreadFactoryWithNamePrefix;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.LongSupplier;

/**
 * Created by jlutt on 2018-04-08.
 * 周期线程池，定点时间执行
 *
 * @author jlutt
 */
public class ParticularTimeTasker {

    protected final Logger logger = LogManager.getLogger(this.getClass().getSimpleName());
    private final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, new ThreadFactoryWithNamePrefix("[定时线程池]"));
    private Runnable task;

    private LongSupplier calcNextDelaySupplier;

    private ParticularTimeTasker() {
    }

    public ParticularTimeTasker(Runnable task, LongSupplier calcNextDelaySupplier) {
        this.task = task;
        this.calcNextDelaySupplier = calcNextDelaySupplier;
    }

    /**
     * 计算当前时间到指定下一个指定时间的秒数
     *
     * @return 指定时间的秒数
     */
    private long computeNextDelay() {
        return this.calcNextDelaySupplier.getAsLong();
    }

    /**
     * 启动执行
     */
    public void start() {
        Runnable taskWrapper = () -> {
            try {
                task.run();
            } catch (Exception ex) {
                logger.error("任务执行失败", ex);
            } finally {
                //重新安排调度在下次执行计划
                start();
            }
        };
        long delay = computeNextDelay();
        executorService.schedule(taskWrapper, delay, TimeUnit.SECONDS);
    }

    public void stop() {
        executorService.shutdownNow();
        try {
            if (!executorService.awaitTermination(1, TimeUnit.DAYS)) {
                logger.error("线程关闭失败，还有线程在执行中，无法立即关闭线程池");
            } else {
                logger.info("所有任务都已执行完毕，线程池已关闭");
            }
        } catch (InterruptedException ex) {
            logger.error(ex.getMessage(), ex);
            Thread.currentThread().interrupt(); // 恢复中断状态
        }
    }
}
