package com.yanzuoguang.util.helper;

import com.yanzuoguang.util.log.Log;
import com.yanzuoguang.util.thread.ThreadHelper;
import com.yanzuoguang.util.vo.Ref;

/**
 * 超时监控
 *
 * @author 颜佐光
 */
public class YzgTimeout {

    public static final int TIME_OUT_DEFAULT = 15 * 1000;
    public static final int TIME_OUT_TIP = 10 * 1000;
    public static final int TIME_OUT_UNIT = 10;

    /**
     * 超时监控
     *
     * @param cls      日志类
     * @param message  消息
     * @param runnable 运行函数
     */
    public static void timeOut(Class<?> cls, String message, Runnable runnable) {
        timeHeart(TIME_OUT_DEFAULT, TIME_OUT_UNIT, TIME_OUT_TIP, runnable, (time) -> {
            Log.error(cls, "%s超时,已经执行%d豪秒,正在等待执行完成", message, time);
        });
    }

    /**
     * 超时监控
     *
     * @param runnable 运行函数
     * @param heart    心跳函数
     */
    public static void timeHeart(Runnable runnable, YzgTimeoutHeart heart) {
        timeHeart(1000, 1000, 10, runnable, heart);
    }

    /**
     * 超时监控
     *
     * @param tipOutDefault 默认超时时间
     * @param timeOutTip    超时心跳间隔
     * @param tipUnit       监听间隔时间(监听任务完成间隔时间)
     * @param runnable      运行函数
     * @param heart         心跳函数
     */
    public static void timeHeart(int tipOutDefault, int timeOutTip, int tipUnit,
                                 Runnable runnable, YzgTimeoutHeart heart) {
        final Ref<Boolean> isRun = new Ref<>(false);
        ThreadHelper.runThread(() -> {
            try {
                long timeMax = tipOutDefault;
                long start = System.currentTimeMillis();
                do {
                    long end = System.currentTimeMillis();
                    long time = end - start;
                    if (time > timeMax) {
                        timeMax += timeOutTip;
                        heart.heart(time);
                    }
                    ThreadHelper.sleep(tipUnit);
                } while (!isRun.value);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        });
        try {
            runnable.run();
        } finally {
            synchronized (isRun) {
                isRun.value = true;
            }
        }
    }
}