import Scene from "../MViews/MScene";
import Car from "../Game/Car";
import { MConst } from "../Global/MConst";
import PlayerController from "../Game/PlayerController";
import ParticleMgr from "../Mgr/ParticleMgr";
import Ball from "../Global/Ball";
import Physics from "../Game/Physics";
import GameObject from "../Game/GameObject";
import Collider, { RectCollider, ColliderGroup } from "../Game/Collider";
import { MConfigs } from "../Global/MConfigs";
import PoolMgr from "../Mgr/PoolMgr";
import MTweenMgr from "../MComponents/MTween";
import Drop from "../Game/Drop";
import { MUtils } from "../Global/MUtils";
import { Decorator } from "../Decorator/Decorator";
import GameMgr from "../Mgr/GameMgr";
import Net from "../Global/Net";
import PanelCtrl from "../MCtrls/MPanelCtrl";
import { NetUtils } from "../Global/NetUtils";
import SceneCtrl from "../MCtrls/MSceneCtrl";
import StartScene from "./StartScene";
import Loading from "../../loading/Loading";
import SoundMgr from "../Mgr/SoundMgr";
import GameOverPanel from "../Panel/GameOverPanel";
import { initNECaptcha } from "../MComponents/initNECaptcha";
import { restart, arrayRemove } from "../Global/GUtils";
import ToastPanel from "../MComponents/MToastPanel";
import MTimer from "../MComponents/MTimer";
import DebugMgr from "../Mgr/DebugMgr";
import PhycicsSystem from "../Game/PhycicsSystem";

@Decorator.OnUpdate
@Decorator.OnDestroy
export default class MainScene extends Scene implements Decorator.IDefaultCallback {
    get skinKey() { return "MainScene" }
    public groupTop: eui.Group;
    public labelBulletScore: eui.Label;
    public labelScore: egret.BitmapText;
    public labelPowerScore: eui.Label;
    public toggleMusic: eui.ToggleButton;
    public labelTime: eui.Label;
    public gameStage: egret.DisplayObjectContainer = new egret.DisplayObjectContainer();
    public ballLayer: egret.DisplayObjectContainer = new egret.DisplayObjectContainer();
    public bulletLayer: egret.DisplayObjectContainer = new egret.DisplayObjectContainer(); //子弹层
    public animationLayer = new egret.DisplayObjectContainer();
    public dropLayer: egret.DisplayObjectContainer = new egret.DisplayObjectContainer();
    public bulletPool: ParticleMgr = new ParticleMgr(this.bulletLayer);
    private ballCount = 0;
    // private curCreateBallInterval = 0;
    private curMaxBallNum = 1;
    public startId: number = null;
    public timer: number = MConfigs.countDown * 1000;
    private timing = false;
    public reviveSeq: number = 1;
    public maxScore: number = null;
    private inited = false;
    private isSubmited = false;
    private createBallCD: number = 0;
    private car: Car = null;

    //中途提交相关
    // public onSubmited = new MEvent<(remove?: () => void) => void>();
    private constantSubmitSeq = 1;
    private needSubmitCount = 0; //实际分数减去ConstantSubmitScoreNum的次数
    private isSubmiting = false;
    private _localScore: number = 0;
    public get localScore(): number {
        return this._localScore;
    }
    public set localScore(v: number) {
        this._localScore = v;

        if (this._localScore >= MConst.ConstantSubmitScoreNum) {
            let count = Math.floor(this._localScore / MConst.ConstantSubmitScoreNum);
            this.needSubmitCount += count;
            this._localScore -= MConst.ConstantSubmitScoreNum * count;
            if (!this.isSubmiting) {
                this.constantSubmit();
            }
        }
    }
    private constantSubmit(score: number = MConst.ConstantSubmitScoreNum) {
        if (this.isSubmiting) return;
        if (this.needSubmitCount == 0) {
            return;
        } else if (this.needSubmitCount < 0) {
            //异常，有bug
            console.error("invalid needSubmitCount value");
            return;
        }

        this.isSubmiting = true;
        let seq = NetUtils.encryptSeq(this.constantSubmitSeq);

        return new Promise((resolve, reject) => {
            Net.sendPost(Net.Url.constantSubmit, {
                startId: this.startId,
                seq: seq,
                score: score,
                token: NetUtils.md5(this.startId.toString() + seq.toString() + score.toString() + "duiba4icbc")
            }, () => {
                this.constantSubmitSeq++;
                this.needSubmitCount--;
                this.isSubmiting = false;

                resolve();
            }, (res) => {
                this.isSubmiting = false;
                Net.defaultOnFail(res);
                reject();
            });
        });
    }

    // private needCreateBall = false;
    /**炮车的开火速度 */
    public fireSpeed: number = MConst.BulletFireSpeed.min;
    /**开火子弹数量配置 */
    /* public fireBulletNumConfig: {
        0: number
        1: number
    } = [1, 1]; */
    public fireBulletNumConfig: number = 1;

    public test1: number = 1;
    public test2: number = 1;
    public testIndex: 1 | 2 = 1;

    private _score: number = 0;
    public get score(): number {
        return this._score;
    }
    public set score(v: number) {
        this.localScore += (v - this._score);
        this._score = v;
    }

    private bulletScoreUpdateFlag = false;

    private _bulletScore: number = 0;
    public get bulletScore(): number {
        return this._bulletScore;
    }
    public set bulletScore(v: number) {
        if (v == this._bulletScore) return;
        v = Math.max(v, 0);
        this._bulletScore = v;
        this.bulletScoreUpdateFlag = true;
    }

    private _powerScore: number = 100;
    public get powerScore(): number {
        return this._powerScore;
    }
    public set powerScore(v: number) {
        v = Math.max(v, 100);
        this._powerScore = v;
    }

    public ballPool = new class extends PoolMgr<Ball>{
        createElement([context]: [MainScene]) {
            return new Ball(context);
        }

        destroy(ball: Ball) {
            super.destroy(ball);
            this.context.ballCount--;
            this.context.ballList = arrayRemove(this.context.ballList, ball);
        }

        private context: MainScene = null;

        constructor(context: MainScene) {
            super(context.ballLayer);
            this.context = context;
        }

        create(context: MainScene) {
            context.ballCount++;
            let ball = super.create(context);
            this.context.ballList.push(ball);
            return ball;
        }
    }(this);

    public dropPool = new DropPool(this.dropLayer);

    async preLoadRes() {
        await super.preLoadRes();
        return new Promise(async resolve => {
            resolve();
        });
    }

    constructor(data?: any) {
        super(data);
        this.startId = data.startId;
        this.maxScore = data.maxScore;
        PhycicsSystem.instance.enabled = true;

        /*  this.addEventListener(egret.Event.ENTER_FRAME, this.onUpdate, this);
         this.addEventListener(egret.Event.REMOVED_FROM_STAGE, () => {
             this.removeEventListener(egret.Event.ENTER_FRAME, this.onUpdate, this);
         }, this); */
    }

    onSkinComplete() {
        super.onSkinComplete();

        this.toggleMusic.selected = SoundMgr.instance.enabled;
        this.toggleMusic.addEventListener(eui.UIEvent.CHANGE, (evt: eui.UIEvent) => {
            SoundMgr.instance.enabled = (evt.target as eui.ToggleButton).selected;
        }, this);

        GameMgr.instance.init(this);

        Ball.init();

        //创建墙和地面
        this.createWall();

        this.addChild(this.gameStage);
        let bg = new egret.Bitmap(RES.getRes("main_bg_jpg"));
        bg.x = -10;
        bg.y = -10;
        this.gameStage.addChild(bg);
        //子弹层
        this.bulletLayer.touchEnabled = false;
        this.gameStage.addChild(this.bulletLayer);

        //炮车
        let car = new Car(this);
        car.anchorY = 1;
        car.posX = this.width / 2;
        car.posY = MConst.GroundLine;
        this.gameStage.addChild(car);
        car.addComponent(Physics);
        this.car = car;

        //球的层
        this.ballLayer.touchEnabled = false;
        this.gameStage.addChild(this.ballLayer);

        //掉落物层
        this.dropLayer.touchEnabled = false;
        this.gameStage.addChild(this.dropLayer);

        //动画层
        this.animationLayer.touchEnabled = false;
        this.gameStage.addChild(this.animationLayer);

        //玩家控制器
        let playerController = new PlayerController();
        playerController.onTouchMove = (deltaX) => {
            if (GameMgr.instance.pause) return;


            car.move(deltaX);
        };
        this.addChild(playerController);

        //UI层置顶
        this.groupTop.touchThrough = true;
        this.addChild(this.groupTop);

        //开始倒计时
        this.timing = true;
        this.inited = true;

        if (GameMgr.instance.guideFlag == true) {
            GameMgr.instance.runGuide(0, car.posX - 6, car.posY - 62);
        }

        this.labelScore = new egret.BitmapText();
        this.labelScore.font = RES.getRes("num_fnt");
        this.labelScore.x = 375;
        this.labelScore.y = 252;
        this.addChild(this.labelScore);
    }

    private createWall() {
        let topWall = new GameObject();
        topWall.x = -500;
        topWall.y = -500
        this.addChild(topWall);
        //添加天花板碰撞器
        let topWallCollider = topWall.addComponent<RectCollider>(RectCollider);
        topWallCollider.setData(0, 0, MConst.DesignResolution.x + 1000, 490);
        topWallCollider.group = ColliderGroup.Top;

        //创建地面节点
        let ground = new GameObject();
        ground.x = -500;
        ground.y = MConst.GroundLine;
        this.addChild(ground);
        //添加地面碰撞器
        let groundCollider = ground.addComponent<RectCollider>(RectCollider);
        groundCollider.setData(0, 0, MConst.DesignResolution.x + 1000, 1000);
        groundCollider.group = ColliderGroup.Ground;

        //创建左墙节点
        let leftWall = new GameObject();
        leftWall.x = -500;
        leftWall.y = -500
        this.addChild(leftWall);
        //添加左墙碰撞器
        let leftWallCollider = leftWall.addComponent<RectCollider>(RectCollider);
        leftWallCollider.setData(0, 0, 500, MConst.DesignResolution.y + 1000);
        leftWallCollider.group = ColliderGroup.Wall;

        //创建右墙节点
        let rightWall = new GameObject();
        rightWall.x = this.width;
        rightWall.y = -500
        this.addChild(rightWall);
        //添加右墙碰撞器
        let rightWallCollider = rightWall.addComponent<RectCollider>(RectCollider);
        rightWallCollider.setData(0, 0, 500, MConst.DesignResolution.y + 1000);
        rightWallCollider.group = ColliderGroup.Wall;
    }


    private createBall() {
        let size = MUtils.randomInt(0, MConfigs.size.length);
        if (GameMgr.instance.guideFlag == true) {
            size = 3;
        }
        let color = MUtils.randomInt(size, MConst.MaxColorIndex);

        let ball = this.ballPool.create(this);
        let dir: 1 | -1 = Math.random() > 0.5 ? -1 : 1;
        ball.init(dir, color, size);
        ball.startBornStage(dir);

        this.createBallCD = 1500;
    }

    public ballList: Ball[] = [];

    onUpdate() {
        if (!this.inited) return;

        this.labelScore.text = this.score.toString();
        this.labelScore.x = 375 - this.labelScore.width / 2;

        for (let i = 0; i < this.ballList.length; i++) {
            this.ballList[i].updateScoreLabel();
        }

        //分数更新
        if (this.bulletScoreUpdateFlag) {
            let score = this.bulletScore;
            this.labelBulletScore.text = "x" + score.toString();
            //更新最大球共存数量
            this.updateMaxBallNum(score);
            //更新开火速度
            if (score <= 20) {
                const max = MConst.BulletFireSpeed.max;
                const min = MConst.BulletFireSpeed.min;
                this.fireSpeed = min + (max - min) * (score / 20);
            } else {
                this.fireSpeed = MConst.BulletFireSpeed.max;
            }
            //更新开火子弹数量配置
            /* score = Math.clamp(score, 15, 140); //!!!此时可能改变了v
            let column1 = Math.ceil((score / 2) / 10)
            let column2 = Math.ceil((score - column1 * 10) / 10);
            this.test1 = column1;
            this.test2 = column2; */
            this.bulletScoreUpdateFlag = false;
            this.fireBulletNumConfig = Math.ceil((Math.clamp(score, 15, 140) / 2) / 10);
        }


        this.labelPowerScore.text = this.powerScore.toString() + "%";

        DebugMgr.instance.setLog(this.ballCount.toString());

        //检查球的创建
        if (this.createBallCD > 0) {
            this.createBallCD -= MTimer.deltaTime;
        }

        if (this.ballCount < this.curMaxBallNum) {
            if (this.createBallCD <= 0) {
                this.createBall();
            }
        }

        //倒计时
        if (this.timing) {
            if (this.timer > 0) {
                this.timer -= MTimer.deltaTime;
            } else {
                this.timer = 0;
                this.timing = false;
                //时间到
                this.newgameOver();
                // this.pause();
                // PanelCtrl.instance.show<GameOverPanel>(GameOverPanel).init(this);
            }

            let temp = Math.ceil(this.timer / 1000);
            let second = temp % 60;
            let minute = (temp - second) / 60;
            let str = "";
            str += (minute < 10 ? "0" : "") + minute.toString();
            str += ":";
            str += (second < 10 ? "0" : "") + second.toString();
            this.labelTime.text = str;
        }
    }

    updateMaxBallNum(bulletScore: number) {
        let num = 0;
        if (bulletScore <= 20) num = 1;
        else if (bulletScore <= 60) num = 2;
        else if (bulletScore <= 100) num = 3
        else if (bulletScore <= 130) num = 4;
        else if (Math.random() < 0.3) num = 6;
        else num = 7;
        this.curMaxBallNum = num;
    }

    async newgameOver(){
        this.pause();
        await this.finalSubmit(null,(data)=>{
            PanelCtrl.instance.show<GameOverPanel>(GameOverPanel).init(this,data);
        })
    }
    gameOver() {
        this.newgameOver();
        // this.pause();
        // PanelCtrl.instance.show<GameOverPanel>(GameOverPanel).init(this);
    }

    pause() {
        GameMgr.instance.pause = true;
        this.timing = false;
    }

    resume() {
        GameMgr.instance.pause = false;
        this.timing = true;
    }

    public async finalSubmit(propsId?: string, onSubmited?: (data) => void) {
        if (this.isSubmited) return;
        this.isSubmited = true;

        Loading.instace.show();
        if (this.needSubmitCount > 0) {
            if (!this.isSubmiting) {
                while (this.needSubmitCount > 0) {
                    await this.constantSubmit();
                }
            }
        }

        let serverScore = this.localScore + (this.constantSubmitSeq - 1 + this.needSubmitCount) * MConst.ConstantSubmitScoreNum;
        if (serverScore != this.score) {
            console.error(`[score error] server:${serverScore} local:${this.score}`);
        } else {
            console.log(`[score] server:${serverScore} local:${this.score}`);
        }

        //中途提交最后一次
        if (this.localScore > 0) {
            this.needSubmitCount++;
            await this.constantSubmit(this.localScore);
            console.log("extra submit:" + this.localScore);
        }

        //滑块验证
        let validate = await new Promise<string>(resolve => {
            if (typeof GameMgr.instance.minEnableCaptchaScore == "number" && this.score >= GameMgr.instance.minEnableCaptchaScore) {
                initNECaptcha({
                    captchaId: MConst.captchaId,
                    callback: (ret: { validate: string }) => {
                        resolve(ret.validate);
                    }
                });
            } else {
                resolve("");
            }
        });

        //调用接口
        Net.sendPost(Net.Url.finalSubmit, {
            startId: this.startId,
            score: this.score,
            useSpId: propsId || "",
            token: NetUtils.md5(this.startId.toString() + this.score.toString() + "duiba4icbc"),
            validate: validate
        }, (res) => {
            Loading.instace.hide();
            onSubmited && onSubmited(res);
            this.isSubmited = false;
        }, true);
    }

    public shake() {
        MTweenMgr.instance.removeTweens(this.gameStage);

        let count = 0;
        let callback = () => {
            if (count > 1) return;
            count++;

            this.gameStage.x = 10;
            MTweenMgr.instance.get(this.gameStage)
                .wait(1, true)
                .to({ x: 0, y: -10 }, 1, true)
                .to({ x: -10, y: 0 }, 1, true)
                .to({ x: 0, y: 10 }, 1, true)
                .to({ x: 0, y: 0 }, 1, true)
                .call(callback);
        }

        callback();
    }

    public revive() {
        this.car.revive();
        ToastPanel.show("复活成功，回到游戏页");
        this.resume();
        this.reviveSeq++;
    }

    onDestroy() {
        SoundMgr.instance.enabled = false;
        PhycicsSystem.instance.enabled = false;
    }
}

class DropPool {
    private layer: egret.DisplayObjectContainer = null;
    constructor(layer: egret.DisplayObjectContainer) {
        this.layer = layer;
    }
    create(context: MainScene) {
        let d = new Drop(context);
        this.layer.addChild(d);
        return d;
    }

    destroy(drop: Drop) {
        drop.destroy();
    }
}