import { Component } from 'cc';
export class Queue<T> {
    protected _list: Array<T> = []

    constructor () {
        this._list = [];
    }
    /**添加到队列尾*/
    public enqueue (item: T): void {
        this._list.push(item);
    }
    /**获取队列头，并删除*/
    public dequeue (): T {
        return this._list.shift();
    }
    /**获取队列头，并不删除*/
    public peek (): T {
        if (this._list.length == 0) return null;
        return this._list[0];
    }
    /**查询某个元素，并不删除*/
    public seek (index: number): T {
        if (this._list.length < index) return null;
        return this._list[index];
    }
    public remove (index: number): T {
        if (this._list.length < index) return null;
        const lists = this._list.splice(index, 1);
        return lists[0];
    }
    public removeItem (item: T): boolean {
        for (let i = 0; i < this._list.length; i++) {
            if (this._list[i] == item) {
                this.remove(i);
                return true;
            }
        }
        return false;
    }
    /**转换成标准数组*/
    public toArray (): Array<T> {
        return this._list.slice(0, this._list.length);
    }
    /**是否包含指定元素*/
    public has (item: T): boolean {
        return this._list.indexOf(item, 0) != -1;
    }
    /**清空*/
    public clear (): void {
        this._list.length = 0;
    }
    public get length (): number {
        return this._list.length;
    }
    public empty ():boolean {
        return this.length == 0;
    }
    public foreach (compareFn: (a: T) => boolean): void {
        for (let i = this._list.length - 1; i >= 0; i--) {
            if (!compareFn.call(null, this._list[i])) break;
        }
    }
}


export enum eRuntimeStatusEnum {
    NONE = 1 << 0,
    INIT = 1 << 1,
    ENTER = 1 << 2,
    RUNNING = 1 << 3,
    EXIT = 1 << 4,
    DESTROY = 1 << 5,
}

export class State {
    protected _owner: StateMachine  = null;
    // 实体控制器引用
    protected _state = 0

    protected _status: eRuntimeStatusEnum = eRuntimeStatusEnum.NONE
    // 帧数统计,每帧update的时候+1,每次enter和exit的时候清零,用于处理一些定时事件,比较通用
    // 所以抽离到基础属性里面了，有需要的需要自己在状态里面进行加减重置等操作，基类只提供属性字段
    protected _times = 0
    protected _tick = 0 // 用于计数

    // 设置运行状态，对外开放的接口
    public setStatus (v: number): void {
        this._status |= v;
    }

    // 重置运行状态
    // 支持重置多个状态位
    resetStatus (v: number): void {
        this._status &= (this._status ^ v);
    }

    /**
     * hasStatus
     * @param Status
     * @returns boolean
     * 是否存在运行状态,支持多个状态查询
     */
    public hasStatus (v: number): boolean {
        return (this._status & v) > 0;
    }

    public set owner (v: StateMachine) {
        this._owner = v;
    }

    public get owner ():StateMachine {
        return this._owner;
    }
    public get entity ():any {
        return this._owner.entity;
    }
    public get state ():number {
        return this._state;
    }
    public set state (v: number) {
        this._state = v;
    }

    public enter (preState: State = null): boolean {
        console.info('you must overwrite the func state.enter !');
        return true;
    }
    public update (dt: number): void {
        console.info('you must overwrite the func state.update ! dt:', dt);
    }
    public exit (): void {
        console.info('you must overwrite the func state.exit !');
    }

    public destroy (): void {
        this._owner = null;

        this._state = 0;
    }
}

export class StateMachine extends Component {
    protected _currentState: State = null
    protected _previousState: State = null
    protected _gState: State = null
    protected _states:Map<number, State> = new Map();
    protected _stateQueue: Queue<number> = new Queue<number>();
    protected _storeMap = {};
    public changedSignal:(from:number, to:number)=>void = null;
    public enterSignal: (current:number)=>void = null;
    public exitSignal: (current:number)=>void = null;
    private _lastDt = 0;
    private _totalDt = 0;
    protected _frequency = 1; // 频率，每隔多少帧执行一次
    protected _frequencyDt = 0.16; // 帧时间
    private _stateDt = 0; // 状态总时间

    //设置状态机的耗时
    public get stateDt (): number {
        return this._stateDt;
    }

    //状态机的超时判断
    public isTimeout (time: number): boolean {
        if (this._stateDt > time) {
            return true;
        }
        return false;
    }
    //设置触发频率，单位帧，60fs为基准
    public set frequency (v: number) {
        this._frequency = v;
        this._frequencyDt = v * 0.16;
    }
    public get frequency (): number {
        return this._frequency;
    }
    //实体，可以是node，也可以是class
    protected _entity: any = null

    public set entity (v: any) {
        this._entity = v;
    }
    public get entity (): any {
        return this._entity;
    }
    /**
     * 注册状态
     * @param type
     * @param state
     */
    public registerState (type: number, state: State): void {
        state.state = type;
        state.owner = this;
        this._states.set(type, state);
        state.setStatus(eRuntimeStatusEnum.INIT);
    }

    /**
     * 移除状态
     * @param type
     */
    public unregisterState (type: number): void {
        this._states.delete(type);
    }

    public setData(key: string, value: any): void {
        this._storeMap[key] = value;
    }
    public removeData(key: string): void {
        delete this._storeMap[key];
    }
    public getData(key: string): any {
        return this._storeMap[key];
    }
    public resetData(): void {
        this._storeMap = {};
    }
    protected update (dt: number): void {
        this._lastDt = dt;

        this._totalDt += dt;
        this._stateDt += dt;
        if (this._totalDt < this._frequencyDt) return;

        if (this._gState && this._gState.hasStatus(eRuntimeStatusEnum.RUNNING) && !this._gState.hasStatus(eRuntimeStatusEnum.EXIT)) {
            this._gState.update(this._totalDt);
        }

        if (this._currentState && this._currentState.hasStatus(eRuntimeStatusEnum.RUNNING) && !this._currentState.hasStatus(eRuntimeStatusEnum.EXIT)) {
            this._currentState.update(this._totalDt);
        }
        this._totalDt = 0;
    }
    // for non compoment call
    public tick(dt:number) {
        this.update(dt);
    }
    /**
     * 切换状态,如果有上一个状态，先退出上一个状态，再切换到该状态
     * @param type
     */
    public changeState (type: number): boolean {
        if (!this._states.has(type)) {
            return false;
        }
        this._stateQueue.clear();
        this._stateQueue.enqueue(type);
        return this.doNextState();
    }
    public hasNextState ():boolean {
        if (this._stateQueue.length <= 0) {
            return false;
        }
        return true;
    }
    public changeToNextState ():void {
        this.doNextState();
    }
    protected doNextState (): boolean {
        if (this._stateQueue.length <= 0) {
            return false;
        }
        const type = this._stateQueue.dequeue();
        if (!this._states.has(type)) {
            return false;
        }
        this._previousState = this._currentState;
        this._currentState = this._states.get(type);
        if (this._previousState) this._stateExit(this._previousState);
        if (this.changedSignal) this.changedSignal(this._previousState.state, this._currentState.state);
        if (this._stateEnter(this._currentState, this._previousState)) {
            this.update(this._lastDt);//快速进入状态，不等下一帧
        }

        return true;
    }
    private _stateExit (state: State): void {
        state.setStatus(eRuntimeStatusEnum.EXIT);
        state.exit();
        if (this.exitSignal) this.exitSignal(state.state);
    }
    private _stateEnter (state: State, preState: State = null): boolean {
        this._stateDt = 0;
        state.resetStatus(eRuntimeStatusEnum.ENTER | eRuntimeStatusEnum.RUNNING | eRuntimeStatusEnum.EXIT);

        state.setStatus(eRuntimeStatusEnum.ENTER);
        const result = state.enter(preState);
        state.setStatus(eRuntimeStatusEnum.RUNNING);
        if (this.enterSignal) this.enterSignal(state.state);
        return result;
    }

    /**
     * 设置下一个状态，如果队列有，追加到最后，如果当前没有运行的状态，直接运行
     * @param type
     * @returns
     */
    public setNextState (type: number): boolean {
        if (!this._states.has(type)) {
            return false;
        }
        this._stateQueue.enqueue(type);
        if (!this._currentState) {
            return this.doNextState();
        } else if (this._currentState.hasStatus(eRuntimeStatusEnum.EXIT)) {
            return this.doNextState();
        }
        return true;
    }
    public setGlobalState (type: number): boolean {
        if (!this._states.has(type)) {
            return false;
        }
        if (this._gState) {
            this._stateExit(this._gState);
            this._gState = null;
        }

        this._gState = this._states.get(type);
        this._stateEnter(this._gState);
        return true;
    }

    public clearAllState (): void {
        if (this._gState) {
            this._stateExit(this._gState);
            this._gState = null;
        }

        if (this._currentState) {
            this._stateExit(this._currentState);
            this._currentState = null;
        }
        this._previousState = null;
        this._states.forEach((v, k) => {
            v.setStatus(eRuntimeStatusEnum.DESTROY);
            v.destroy();
            return true;
        });
        this._states.clear();
        this._stateQueue.clear();
        this.changedSignal = null;
        this.enterSignal = null;
        this.exitSignal = null;
    }

    public getCurrentState (): State {
        return this._currentState;
    }

    public getCurrentStateValue (): number {
        return this._currentState.state;
    }

    public getPreviousState (): State {
        return this._previousState;
    }
    public getPrevStateValue (): number {
        return this._previousState.state;
    }

    public getGlobalState (): State {
        return this._gState;
    }

    public destroy (): boolean {
        this.clearAllState();
        this.changedSignal = null;
        this.enterSignal = null;
        this.exitSignal = null;
        return super.destroy();
    }
}
