import { Label, dragonBones, instantiate } from 'cc';
import { find } from 'cc';
import { _decorator, Component, Node } from 'cc';
import { v3 } from 'cc';
import { Charactor } from './Charactor/Charactor';
import { GameEventEnum, GameSystemInputType } from './GameStruct';
import { CharactorAI } from './Charactor/CharactorAI';
import { JCMGO } from '../ThirdParty/JCMGO';
import { BuiltinMatchInfos } from './View/GameMatchView';
const { ccclass, property } = _decorator;

@ccclass('GameMode')
export class GameMode extends Component {

    protected onLoad(): void {
        //监听处理游戏开始
        window.gm.node.on(GameEventEnum.GameStart, this.handleGameStart.bind(this));
        //监听处理游戏结束
        window.gm.node.on(GameEventEnum.GameEnd, this.handleGameEnd.bind(this));
        //监听匹配结束
        window.gm.node.on(GameEventEnum.MatchEnd, () => {
            //显示游戏角色
            find("Canvas/CharactorGroup").active = true;
        });
    }

    protected start(): void {
        this.initLines();
        //显示匹配界面
        find("Canvas-001/GameMatchView").active = true;
    }

    protected lateUpdate(): void {
        this.updateFollowCamera();
        this.uploadInputs();
    }

    handleGameStart() {
        this.displayReadyGo();
        //打开游戏UI界面
        find("Canvas-001/GameModeView").active = true;
        //ai判断
        for (let i = 0; i < window.gm.state.aiFlags.length; i++) {
            if ( window.gm.state.aiFlags[i]) {
                this.getCharactorNode(i).addComponent(CharactorAI);
            }
        }
    }

    handleGameEnd() {
        //关闭游戏UI界面
        find("Canvas-001/GameModeView").active = false;
        //打开结算界面
        find("Canvas-001/GameSettleView").active = true;
        //关闭socket
        window.gm.socketPlayer.close();
    }

    /**初始化-起跑线/终点 */
    private initLines() {
        let startLine = find("Canvas/BandGroup/Start");
        let endLine = instantiate(startLine);
        endLine.name = "End";
        endLine.position = endLine.position.add(v3(window.gm.config.endPointDistance));
        find("Canvas/BandGroup").addChild(endLine);
    }

    /**更新相机跟随 */
    private updateFollowCamera() {
        let bgGroupNode = this.getBGGroupNode();
        let cameraNode = this.getCameraNode();
        let charactorNode = this.getCharactorNode(window.gm.state.myPlayerIndex);
        //相机跟随我控制的角色
        cameraNode.position = cameraNode.position.set(charactorNode.position.x, cameraNode.position.y)
        //根据相机位置，动态补充背景
        while (cameraNode.position.x >= bgGroupNode.children[1].position.x) {
            bgGroupNode.children[0].setPosition(
                bgGroupNode.children[0].position.add(v3(720))
            )
            bgGroupNode.children[0].setSiblingIndex(1);
        }
    }

    /**显示ReadyGo动画 */
    private displayReadyGo() {
        let dp = find("Canvas-001/ReadyGoDisplay").getComponent(dragonBones.ArmatureDisplay);
        dp.node.active = true;
        dp.addEventListener(dragonBones.EventObject.COMPLETE, () => {
            dp.node.destroy();
        }, this);
        window.gm.node.emit(GameEventEnum.ReadyGo);
    }

    //========================获取节点========================

    private _cameraNode: Node;
    private getCameraNode() {
        if (!this._cameraNode) this._cameraNode = find("Canvas/Camera");
        return this._cameraNode;
    }
    
    private _bgGroupNode: Node;
    private getBGGroupNode() {
        if (!this._bgGroupNode) {
            this._bgGroupNode = find("Canvas/BGGroup");
            if (this._bgGroupNode.children.length == 1) {
                this._bgGroupNode.addChild(instantiate(this._bgGroupNode.children[0]))
            }
        }
        return this._bgGroupNode;
    }

    private _charactorGroup: Node;
    public getCharactor(index: number) {
        if (!this._charactorGroup) this._charactorGroup = find("Canvas/CharactorGroup")
        return this._charactorGroup.children[index].getComponent(Charactor);
    }
    public getCharactorNode(index: number) {
        if (!this._charactorGroup) this._charactorGroup = find("Canvas/CharactorGroup")
        return this._charactorGroup.children[index];
    }

    //========================游戏逻辑处理（逻辑/渲染已分离）========================
    /**处理进入房间 */
    handleEnterRoom(data: JCMGO.RoomAddPlayerBst) {
        if (data.isSelf) window.gm.state.myPlayerIndex = data.playerIndex;
    }

    /**处理匹配完成 */
    handleMatchComplete(data: JCMGO.RoomEndMatchingBst) {
        JCMGO.ExactMath.setSeed(data.timestamp);
        let aiInfoIndexes = [];
        for (let i = 0; i < BuiltinMatchInfos.aiPlayerInfos.length; i++) aiInfoIndexes.push(i);
        aiInfoIndexes.sort(() => JCMGO.ExactMath.sub(0.5, JCMGO.ExactMath.random()))
        window.gm.state.matchPlayerInfos = new Array(window.gm.config.maxMembers);
        for (let i = 0; i < window.gm.state.matchPlayerInfos.length; i++) {
            let playerInfo = data.roomInfo.playerInfos[i];
            if (!playerInfo) { //这个索引位没有真人，就分配个ai
                playerInfo = BuiltinMatchInfos.aiPlayerInfos[aiInfoIndexes.shift()];
                window.gm.state.aiFlags[i] = true;
            } else {
                window.gm.state.aiFlags[i] = false;
            }
            window.gm.state.matchPlayerInfos[i] = JCMGO.ObjectUtils.merge({}, playerInfo) as MatchPlayerInfo;
        }
        window.gm.node.emit(GameEventEnum.MatchEnd);
    }

    handleFrameSyncStart() {
        window.gm.state.isFrameSyncStarted = true;
        window.gm.node.emit(GameEventEnum.GameStart);
    }

    /**上传帧输入 */
    public uploadInputs() {
        if (!window.gm.state.isFrameSyncStarted || window.gm.state.authGameSystemState.gameOver) return;
        //本地预测时间间隔（最终还是以服务器下发的为准）
        let nowTime = Date.now();
        if (window.gm.state.lastUploadTime === 0) window.gm.state.lastUploadTime = nowTime;
        //模拟本地预测帧并更新状态
        let moniFrame = window.gm.socketPlayer.moniServerFrame({sign: window.gm.state.lastSign.toString(), dt: nowTime - window.gm.state.lastUploadTime});
        window.gm.state.lastUploadTime = nowTime;
        window.gm.state.localMoniFrames.push(moniFrame);
        this.handleInputs(window.gm.state.gameSystemState, moniFrame.inputs as GameSystemInput[], moniFrame.dt);
        //上传帧输入到服务器
        window.gm.socketPlayer.uploadInputs(window.gm.state.lastSign.toString());
        window.gm.state.lastSign++;
        //清除缓存的帧输入
        window.gm.socketPlayer.clearInputs();
    }

    /**处理服务器下发的帧数据 */
    public handleRecvFrame(frame: JCMGO.Frame) {
        //更新权威状态
        this.handleInputs(window.gm.state.authGameSystemState, frame.inputs as GameSystemInput[], frame.dt);
        //移除已经被服务器认证的本地帧
        for (let i = window.gm.state.localMoniFrames.length - 1; i >= 0; i--) {
            let localFrame = window.gm.state.localMoniFrames[i];
            if (parseInt(localFrame.sign) <= parseInt(frame.sign)) {
                window.gm.state.localMoniFrames.splice(i, 1);
            }
        }
        //当前状态回滚到权威状态
        window.gm.state.gameSystemState = JCMGO.ObjectUtils.merge({}, window.gm.state.authGameSystemState) as GameSystemState;
        //预测并更新当前状态
        window.gm.state.localMoniFrames.forEach((frame: JCMGO.Frame) => {
            this.handleInputs(window.gm.state.gameSystemState, frame.inputs as GameSystemInput[], frame.dt)
        });
        // find("Canvas-001/DebugUI/Label").getComponent(Label).string = JSON.stringify(
        //     window.gm.state.authGameSystemState.gameOver ? window.gm.state.authGameSystemState : window.gm.state.gameSystemState, 
        //     null, "\t");
    }

    /**处理帧输入 */
    private handleInputs(state: GameSystemState, inputs: GameSystemInput[], dt: number) {
        if (state.gameOver) return;
        //计时
        state.time = JCMGO.ExactMath.add(state.time, dt);
        //判断readygo是否完成
        if (!state.readyComplete) {
            if (state.time > 1600) {
                state.readyComplete = true;
            } else {
                return;
            }
        }
        //处理玩家输入
        if (inputs instanceof Array) {
            inputs.forEach((input: GameSystemInput) => {
                if (input.type === GameSystemInputType.PlayerAcc) {
                    let ps = state.playerStates.find(v => v.index === input.playerIndex);
                    if (ps) {
                        ps.speedX = JCMGO.ExactMath.add(ps.speedX, window.gm.config.acc);
                        ps.lastAccTime = state.time;
                        ps.accCount++;
                    }
                    return;
                }
            });
        }
        //===========================处理游戏逻辑======================
        //更新玩家状态
        state.playerStates.forEach(ps => {
            //如果超过1秒没加速，就开始减速
            if (JCMGO.ExactMath.sub(state.time, ps.lastAccTime) > window.gm.config.decGap) {
                ps.speedX = JCMGO.ExactMath.add(
                    ps.speedX,
                    JCMGO.ExactMath.mul(window.gm.config.dec, JCMGO.ExactMath.div(dt, 1000))
                );
                if (ps.speedX < 0) ps.speedX = 0;
            }
            //更新位置
            ps.moveX = JCMGO.ExactMath.add(
                ps.moveX,
                JCMGO.ExactMath.mul(ps.speedX, JCMGO.ExactMath.div(dt, 1000))
            );
        });
        //判断是否有玩家获胜，游戏结束
        let winners: PlayerState[] = [];
        for (const ps of state.playerStates) {
            if (ps.moveX >= window.gm.config.endPointDistance) {
                winners.push(ps);
            }
        }
        if (winners.length > 0) {
            winners.sort((a, b) => b.moveX - a.moveX);
            state.winPlayerIndex = winners[0].index;
            state.gameOver = true;
            this.emitGameEnd();
        }
    }

    private _emitedGameEnd = false;
    private emitGameEnd() {
        if (this._emitedGameEnd) return;
        if (window.gm.state.authGameSystemState.gameOver) {
            this._emitedGameEnd = true;
            window.gm.node.emit(GameEventEnum.GameEnd);
        }
    }
}


