/**
 * 动画主控制器
 * @config target 动画对象，可以是数组，如果是数组的话会批量分发onframe等事件
 * @config life(1000) 动画时长
 * @config delay(0) 动画延迟时间
 * @config loop(true)
 * @config gap(0) 循环的间隔时间
 * @config onframe
 * @config easing(optional)
 * @config ondestroy(optional)
 * @config onrestart(optional)
 *
 * TODO pause
 */

import easingFuncs, {AnimationEasing} from './easing';
import type Animation from './Animation';

type OnframeCallback = (percent: number) => void;
type ondestroyCallback = () => void
type onrestartCallback = () => void

export type DeferredEventTypes = 'destroy' | 'restart'
type DeferredEventKeys = 'ondestroy' | 'onrestart'

export interface ClipProps {
    life?: number
    delay?: number
    loop?: boolean
    gap?: number
    easing?: AnimationEasing

    onframe?: OnframeCallback
    ondestroy?: ondestroyCallback
    onrestart?: onrestartCallback
}

export default class Clip {

    // 生命周期
    private _life: number
    // 延时
    private _delay: number

    private _initialized: boolean = false
    // 开始时间
    private _startTime = 0 // 开始时间单位毫秒

    private _pausedTime = 0
    private _paused = false

    animation: Animation

    loop: boolean
    gap: number
    easing: AnimationEasing

    // For linked list. Readonly
    next: Clip
    prev: Clip

    onframe: OnframeCallback
    ondestroy: ondestroyCallback
    onrestart: onrestartCallback

    constructor(opts: ClipProps) {

        this._life = opts.life || 1000;

        this._delay = opts.delay || 0;

        // this._startTime = new Date().getTime() + this._delay;

        // 是否循环
        this.loop = opts.loop == null ? false : opts.loop;

        this.gap = opts.gap || 0;

        this.easing = opts.easing || 'linear';

        this.onframe = opts.onframe;
        this.ondestroy = opts.ondestroy;
        this.onrestart = opts.onrestart;
    }

    step(globalTime: number, deltaTime: number): boolean {
        // Set startTime on first step, or _startTime may has milleseconds different between clips
        // PENDING
        if (!this._initialized) {
            this._startTime = globalTime + this._delay;
            this._initialized = true;
        }

        if (this._paused) {
            this._pausedTime += deltaTime;
            return;
        }

        let percent = (globalTime - this._startTime - this._pausedTime) / this._life;

        // PENDING: Not begin yet. Still run the loop.
        // In the case callback needs to be invoked.
        // Or want to update to the begin state at next frame when `setToFinal` and `delay` are both used.
        // To avoid the unexpected blink.
        if (percent < 0) {
            percent = 0;
        }

        percent = Math.min(percent, 1);

        const easing = this.easing;
        const easingFunc = typeof easing === 'string'
            ? easingFuncs[easing as keyof typeof easingFuncs] : easing;
        const schedule = typeof easingFunc === 'function'
            ? easingFunc(percent)
            : percent;

        this.onframe && this.onframe(schedule);

        // 结束
        if (percent === 1) {
            if (this.loop) {
                this._restart(globalTime);
                this.onrestart && this.onrestart();
            }
            else {
                return true;
            }
        }

        return false;
    }

    private _restart(globalTime: number) {
        const remainder = (globalTime - this._startTime - this._pausedTime) % this._life;
        this._startTime = globalTime - remainder + this.gap;
        this._pausedTime = 0;
    }

    pause() {
        this._paused = true;
    }

    resume() {
        this._paused = false;
    }
}