package com.ecep.contract.util;

import javafx.beans.value.ObservableValue;
import lombok.Setter;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * 延时执行器
 */
public class DelayOnceExecutor {
    private final Runnable runnable;
    private final Executor executor;
    private CompletableFuture<Void> future;
    /**
     * 定义一个Consumer类型的异常处理器
     */
    @Setter
    private Consumer<Exception> exceptionHandler;

    /**
     * 构造函数
     */
    public DelayOnceExecutor(Runnable runnable, long delay, TimeUnit unit) {
        this.runnable = runnable;
        executor = CompletableFuture.delayedExecutor(delay, unit);
    }

    /**
     * 执行 runnable，如果future不为空，则取消之前的任务
     */
    public void tick() {
        if (future != null) {
            future.cancel(true);
        }
        future = CompletableFuture.runAsync(this::run, executor);
    }

    public void tickNow() {
        if (future != null) {
            future.cancel(true);
        }
        future = CompletableFuture.runAsync(this::run);
    }

    void run() {
        try {
            runnable.run();
        } catch (Exception e) {
            if (exceptionHandler != null) {
                exceptionHandler.accept(e);
            }
        }
    }

    /**
     * 取消任务
     */
    public void cancel() {
        if (future != null) {
            future.cancel(true);
        }
    }

    /**
     * 监听属性变化，当属性变化时，执行 runnable
     *
     * @param properties 要监听的属性
     */
    public DelayOnceExecutor listen(ObservableValue<?>... properties) {
        for (ObservableValue<?> property : properties) {
            property.addListener((observable, oldValue, newValue) -> this.tick());
        }
        return this;
    }

    /**
     * Consumer<Exception> exceptionHandler;
     *
     * @param exceptionHandler 异常处理
     */
    public DelayOnceExecutor exception(Consumer<Exception> exceptionHandler) {
        this.exceptionHandler = exceptionHandler;
        return this;
    }
}
