package com.yanzuoguang.redis;

import com.alicp.jetcache.Cache;
import com.yanzuoguang.util.thread.ThreadHelper;

import java.util.concurrent.TimeUnit;

/**
 * 运行函数
 *
 * @author 颜佐光
 */
public class CacheLock<T, M> implements Runnable {

    /**
     * 是否执行标记
     */
    private boolean runFlag = false;

    /**
     * 缓存对象
     */
    private final Cache<T, M> cache;

    /**
     * Redis 锁定时间(豪秒)
     */
    private final int lockTime;

    /**
     * 每次等待时间(毫秒)
     */
    private final int waitUnitTime;
    /**
     * 关键字
     */
    private final T key;
    /**
     * 执行函数
     */
    private final Runnable funcWait;
    /**
     * 执行函数
     */
    private Runnable func;

    /**
     * 等待次数
     */
    private int waitCount;

    /**
     * 构造函数
     *
     * @param cache        缓存对象
     * @param lockTime     锁定时间
     * @param waitUnitTime 等待单位
     * @param key          关键字
     */
    public CacheLock(Cache<T, M> cache, int lockTime, int waitUnitTime, T key) {
        this(cache, lockTime, waitUnitTime, key, null, null);
    }

    /**
     * 构造函数
     *
     * @param cache        缓存对象
     * @param lockTime     锁定时间
     * @param waitUnitTime 等待单位
     * @param key          关键字
     * @param func         执行函数
     */
    public CacheLock(Cache<T, M> cache, int lockTime, int waitUnitTime, T key, Runnable func) {
        this(cache, lockTime, waitUnitTime, key, null, func);
    }

    /**
     * 构造函数
     *
     * @param cache        缓存对象
     * @param lockTime     锁定时间
     * @param waitUnitTime 等待单位
     * @param key          关键字
     * @param funcWait     等待期间执行的函数
     * @param func         执行函数
     */
    public CacheLock(Cache<T, M> cache, int lockTime, int waitUnitTime, T key, Runnable funcWait, Runnable func) {
        this.cache = cache;
        this.lockTime = lockTime;
        this.waitUnitTime = waitUnitTime;
        this.key = key;
        this.funcWait = funcWait;
        this.func = func;
    }

    /**
     * 等待次数
     *
     * @return 等待次数
     */
    public int getWaitCount() {
        return waitCount;
    }

    /**
     * 开始执行,每个关键字会等待其他关键字执行完成后执行
     *
     * @param func 运行函数
     */
    public void run(Runnable func) {
        this.func = func;
        this.run();
    }

    /**
     * 开始执行,每个关键字会等待其他关键字执行完成后执行
     */
    @Override
    public void run() {
        if (this.func == null) {
            return;
        }
        // 需要运行的函数
        do {
            // 开启唯一性锁,防止多人运行同一关键字的函数
            cache.tryLockAndRun(key, lockTime, TimeUnit.SECONDS, this::funcRun);
            // 假如没有运行,则等待50毫秒后继续运行
            if (!runFlag) {
                this.waitCount++;
                ThreadHelper.sleep(waitUnitTime);
            }
        } while (!runFlag);
    }

    private void funcRun() {
        try {
            if (this.waitCount > 0 && this.funcWait != null) {
                funcWait.run();
            }
            if (this.func != null) {
                func.run();
            }
        } finally {
            runFlag = true;
        }
    }

    /**
     * 开始执行,每个关键字会等待其他关键字执行完成后执行
     *
     * @param cache        缓存对象
     * @param lockTime     锁定时间
     * @param waitUnitTime 等待单位
     * @param key          关键字
     * @param func         执行函数
     */
    public static <T, M> void run(Cache<T, M> cache, int lockTime, int waitUnitTime, T key, Runnable func) {
        run(cache, lockTime, waitUnitTime, key, null, func);
    }

    /**
     * 开始执行,每个关键字会等待其他关键字执行完成后执行
     *
     * @param cache        缓存对象
     * @param lockTime     锁定时间
     * @param waitUnitTime 等待单位
     * @param key          关键字
     * @param func         执行函数
     */
    public static <T, M> void run(Cache<T, M> cache, int lockTime, int waitUnitTime, T key, Runnable funcWait, Runnable func) {
        CacheLock lock = new CacheLock(cache, lockTime, waitUnitTime, key, funcWait, func);
        lock.run();
    }
}