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 "../Game/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 MEvent from "../MComponents/MEvent";
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 ToastPanel from "../MComponents/MToastPanel";
import { arrayRemove } from "../Global/GUtils";

@Decorator.OnUpdate
export default class MainScene extends Scene implements Decorator.IDefaultCallback {
    get skinKey() { return "MainScene" }
    public groupTop: eui.Group;
    public labelBulletScore: eui.Label;
    public labelPowerScore: eui.Label;
    public labelScore: egret.BitmapText;
    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();
            }
        }
    }

    /**
     * 中途提交
     * 机制：前后端约定一个数值，每当得分超过此值时提交这个值的分数，剩余的分数记录在localScore。
     * 最终提交是若localScore还有值，则最后一次中途提交此分数
     * 所有中途提交结束才会进行最终提交
     * @param score 要提交的分数
     */
    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.uri.constantSubmit, {
                startId: this.startId,
                seq: seq,
                score: score,
                token: NetUtils.md5(this.startId.toString() + seq.toString() + score.toString() + "duiba4jxabc")
            }, () => {
                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];

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

        this.localScore += (v - this._score);
        this._score = v;
    }

    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;
    }

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

        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 class extends PoolMgr<Drop>{
        createElement([context]: [MainScene]) {
            return new Drop(context);
        }

        create(context: MainScene) {
            return super.create(context);
        }
    }(this.dropLayer);

    async preLoadRes() {
        await super.preLoadRes();
        return new Promise(async resolve => {
            Loading.instace.show();
            await RES.loadGroup("game");
            await RES.loadGroup("car");
            await RES.loadGroup("animation");
            await RES.loadGroup("sound");
            Loading.instace.hide();
            resolve();
        });
    }

    constructor(data?: any) {
        super(data);
        this.startId = data.startId;
        this.maxScore = data.maxScore;
    }



    onSkinComplete() {
        super.onSkinComplete();
        Loading.instace.hide();
        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);
        //默认关闭音乐
        /* SoundMgr.instance.enabled = true;
        this.toggleMusic.selected = true; */
        // SoundMgr.instance.enabled = false;
        GameMgr.instance.init(this);

        //创建墙和地面
        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;

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

        // this.bulletScore = 140;
        // this.powerScore = 3000;

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

    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 = 2000;
    }

    public ballList: Ball[] = [];

    onUpdate(dt: number) {
        if (!this.inited) return;

        //更新球的分数
        for (let i = 0; i < this.ballList.length; i++) {
            this.ballList[i].updateScoreLabel();
        }

        //检查球的创建
        if (this.createBallCD > 0) {
            this.createBallCD -= dt;
        }
        if (this.ballCount < this.curMaxBallNum) {
            if (this.createBallCD <= 0) {
                this.createBall();
            }
        }

        //更新分数标签
        this.labelScore.text = this.score.toString();
        this.labelScore.x = 375 - this.labelScore.width / 2;

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

        //更新火力分数标签
        this.labelPowerScore.text = this.powerScore.toString() + "%";

        //倒计时
        if (this.timing) {
            if (this.timer > 0) {
                this.timer -= dt;
            } else {
                this.timer = 0;
                this.timing = false;
                //时间到
                this.gameOver();
            }

            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;
    }

    gameOver() {
        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;
    }

    /**
     * 最终提交分数
     * @param propsId 
     * @param onSubmited 
     */
    public async finalSubmit(propsId?: string, onSubmited?: () => 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.uri.finalSubmit, {
            startId: this.startId,
            score: this.score,
            useSpId: propsId || "",
            token: NetUtils.md5(this.startId.toString() + this.score.toString() + "duiba4jxabc"),
            validate: validate
        }, (res) => {
            Loading.instace.hide();
            onSubmited && onSubmited();
        }, 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++;
    }
}