import Scene from "../views/Scene";
import Car from "../Game/Car";
import { MConst } from "../Global/MConst";
import PlayerController from "../Game/PlayerController";
import { ThroughSubmitData } from "../../libs/tw/data/custom/throughSubmit/ThroughSubmitData";
import ParticleMgr from "../Mgr/ParticleMgr";
import Ball from "../Global/Ball";
import Physics from "../Game/Physics";
import PhycicsSystem from "../Game/PhycicsSystem";
import { getBallScore, arrayRemove, getProp } from "../Global/GUtils";
import GameObject from "../Game/GameObject";
import Collider, { RectCollider, ColliderGroup, CircleCollider } from "../Game/Collider";
import { MConfigs } from "../Global/MConfigs";
import PoolMgr from "../Mgr/PoolMgr";
import MTweenMgr from "../components/MTween";
import Drop from "../Game/Drop";
import DebugMgr from "../Mgr/DebugMgr";
import { MUtils } from "../Global/MUtils";
import { Decorator } from "../Decorator/Decorator";
import GameMgr from "../Mgr/GameMgr";
import Net from "../Global/Net";
import PanelCtrl from "../Ctrls/PanelCtrl";
import SelectPropsPanel from "../Panel/SelectPropsPanel";
import { NetUtils } from "../Global/NetUtils";
import MEvent from "../components/MEvent";
import WinningPanel from "../Panel/WinningPanel";
import MButton from "../MUI/MButton";
import Panel from "../views/Panel";
import SceneCtrl from "../Ctrls/SceneCtrl";
import StartScene from "./StartScene";
import Loading from "../Loading/Loading";
import MovieClipMgr from "../Mgr/MovieClipMgr";
import { initNECaptcha } from "../components/InitNECaptcha";
import ToastPanel from "../Components/ToastPanel";
import { Buried } from "../../libs/tw/util/Buried";
import SoundMgr from "../Mgr/SoundMgr";
import MLabel from "../MUI/MLabel";

@Decorator.OnUpdate
@Decorator.OnDestroy
export default class MainScene extends Scene implements Decorator.IDefaultCallback {
    get skinKey() { return "MainScene" }
    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);

    public groupTop: eui.Group;
    public labelBulletScore: eui.Label;
    public labelPowerScore: eui.Label;
    public labelScore2: egret.BitmapText = null
    public btnMusic: eui.ToggleButton;
    public labelTime: eui.Label;
    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() + MConst.Md5ConstStr)
            }, () => {
                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;

    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); */
    public dropPool = new DropPool(this.dropLayer);


    async preLoadRes() {
        await super.preLoadRes();
        Loading.instace.show();
        return new Promise(async resolve => {
            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;
        PhycicsSystem.instance.enabled = true;
    }

    onSkinComplete() {
        super.onSkinComplete();

        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.btnMusic.selected = SoundMgr.instance.enabled;
        this.btnMusic.addEventListener(eui.UIEvent.CHANGE, (evt: eui.UIEvent) => {
            SoundMgr.instance.enabled = (evt.target as eui.ToggleButton).selected;
        }, this);
        //开始倒计时
        this.timing = true;
        this.inited = true;

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

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

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







        //掉落数
        /* let index = 1;
        //根据子弹分数判断索引
        let bulletScore = 10;
        if (bulletScore <= 30) index = 0;
        else if (bulletScore > 30 && bulletScore <= 70) index = 1;
        else if (bulletScore > 70) index = 2;
        //根据索引获取配置
        let config: {
            num: number,
            factor: object
        } = MConfigs.dropPool[index];

        let keys = Object.keys(config.factor);
        let values = keys.map(e => config.factor[e]);
        let dropIds = getProp(keys, values, config.num);

        let drops: Drop[] = [];
        for (let id of dropIds) {
            drops.push(this.dropPool.create(this).init(id));
        }

        let dir = this.x > MConst.DesignResolution.x / 2 ? -1 : 1;

        for (let i = 0; i < drops.length; i++) {
            let drop = drops[i];
            drop.x = this.x - drop.bitmap.width / 2;
            drop.y = this.y - drop.bitmap.height / 2;
            let offsetRatio = (i - 1) + (2 - (drops.length - 1));
            let x = MConst.DropVelocityX.x + offsetRatio * MConst.DropVelocityX.offset;

            x *= MUtils.random(1 - MConst.DropRandomFactor, 1 + MConst.DropRandomFactor);
            drop.physics.velocity.x = dir * x;
            drop.physics.velocity.y = -(x * 5 * MUtils.random(0.9, 1.1));

            if (GameMgr.instance.guideFlag && i == 1) {
                MTimer.set(20, () => {
                    GameMgr.instance.runGuide(1, drop.x, drop.y, true);
                });
            }
        } */
    }

    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.labelScore2.text = this.score.toString();
        this.labelScore2.x = 375 - this.labelScore2.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 = ; //!!!此时可能改变了v
        // let column1 = Math.ceil((bulletScore / 2) / 10)
        // let column2 = Math.ceil((bulletScore - column1 * 10) / 10);
        this.fireBulletNumConfig = Math.ceil((Math.clamp(bulletScore, 15, 140) / 2) / 10);

        //更新火力分数标签
        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();
        this.finalSubmit();
    }

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

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

    public async finalSubmit(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.Url.finalSubmit, {
            startId: this.startId,
            score: this.score,
            useSpId: "",
            useSpCnt: "",
            token: NetUtils.md5(this.startId.toString() + this.score.toString() + MConst.Md5ConstStr),
            validate: validate
        }, (res) => {
            Loading.instace.hide();

            const data = res.data;
            PanelCtrl.instance.show(WinningPanel, {
                score: data.finalScore,
                highestScore: data.maxScore
            });
        }, 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();
        this.resume();
        this.reviveSeq++;
    }

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