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 } from "../Global/GUtils";
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 "../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";

@Decorator.OnUpdate
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 labelScore: eui.Label;
    public btnMusic: MButton;
    public labelTime: eui.Label;
    private ballCount = 0;
    private curCreateBallInterval = 0;
    private curMaxBallNum = 1;
    public startId: number = null;
    private timer: number = MConfigs.countDown * 1000;
    private timing = false;

    //中途提交相关
    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() {
        if (this.isSubmiting) return;
        if (this.needSubmitCount == 0) {
            return;
        } else if (this.needSubmitCount < 0) {
            //异常，有bug
            console.error("invalid needSubmitCount value");
            return;
        }

        this.isSubmiting = true;
        Net.sendPost(Net.Url.constantSubmit, {
            startId: this.startId,
            seq: NetUtils.encryptSeq(this.constantSubmitSeq),
            score: MConst.ConstantSubmitScoreNum,
            token: NetUtils.md5(this.startId + this.constantSubmitSeq + MConst.ConstantSubmitScoreNum + "dui88")
        }, () => {
            this.constantSubmitSeq++;
            this.needSubmitCount--;
            this.isSubmiting = false;
            // console.warn(`[submit score success] seq:${this.constantSubmitSeq}, localScore:${this._localScore}, needSubmitCount:${this.needSubmitCount}`);

            if (this.needSubmitCount > 0) {
                this.constantSubmit();
            } else {
                this.onSubmited.call();
            }
        }, () => {
            this.isSubmiting = false;
            // console.error(`[submit score fail] seq:${this.constantSubmitSeq}, localScore:${this._localScore}, needSubmitCount:${this.needSubmitCount}`);
        });
    }

    // 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) {
        this.localScore += (v - this._score);
        this._score = v;

        this.labelScore.text = v.toString();
    }

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

    private _powerScore: number = 100;
    public get powerScore(): number {
        return this._powerScore;
    }
    public set powerScore(v: number) {
        v = Math.max(v, 100);
        this.labelPowerScore.text = v.toString() + "%";
        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--;
        }

        private context: MainScene = null;

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

        create(context: MainScene) {
            context.ballCount++;
            return super.create(context);
        }
    }(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 => {
            await RES.loadGroup("game");
            await RES.loadGroup("car");
            await RES.loadGroup("animation");
            resolve();
        });
    }

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

    onSkinComplete() {
        super.onSkinComplete();

        /* setTimeout(() => {
            this.shake();
        }, 2000);
        setTimeout(() => {
            this.shake();
        }, 2000 + 1000 / 60 * 4); */

        GameMgr.instance.start();

        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.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.x += deltaX;
        };
        this.addChild(playerController);

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

        this.btnMusic.onTap(this, () => GameMgr.instance.switchSound());
        //开始倒计时
        this.timing = true;



        /* let tex = RES.getRes("star_png");
       
        for (let i = 0; i < MConfigs.boomEffectColor.length; i++) {
            let bit = new egret.Bitmap(tex);
            MUtils.setColorFilter(bit, MConfigs.boomEffectColor[i]);
            bit.x = i * 50 + 100;
            bit.y = 500;
            this.gameStage.addChild(bit);
            console.error(bit);
        } */

        /* let b = this.ballPool.create(this).init(1, 0, 3);
        b.x = 300;
        b.y = 500;
        setTimeout(() => {
            b.playBoomEffect();
        }, 1000); */
    }

    private createWall() {
        //创建地面节点
        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 ball = this.ballPool.create(this).init(1,1);
        ball.x = 500;
        ball.y = 600; */
        // ball.physics.velocity.x = 1 * 2;
        let size = MUtils.randomInt(0, MConfigs.size.length);
        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);
    }

    onUpdate(dt: number) {
        //检查球的创建
        if (this.ballCount < this.curMaxBallNum) {
            if (this.curCreateBallInterval >= MConst.CreateBallInterval) {
                this.createBall();
                this.curCreateBallInterval = 0;
            } else {
                this.curCreateBallInterval += dt;
            }
        } else {
            this.curCreateBallInterval = 0;
        }

        //倒计时
        if (this.timing) {
            if (this.timer > 0) {
                this.timer -= dt;
            } else {
                this.timer = 0;
                this.timing = false;
                //时间到
                this.pause();
                this.finalSubmit();
            }
            this.labelTime.text = Math.ceil(this.timer / 1000).toString() + "S";
        }
    }

    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(SelectPropsPanel, { context: this });
    }

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

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

    public async finalSubmit(propsId?: string, useCount?: number) {
        Loading.instace.show();
        await new Promise(resolve => {
            if (this.needSubmitCount > 0) {
                if (!this.isSubmiting) {
                    this.constantSubmit();
                }
                this.onSubmited.add((remove: () => void) => {
                    remove();
                    resolve();
                });
            } else {
                resolve();
            }
        });

        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.warn(`[score] server:${serverScore} local:${this.score}`);
        }

        Net.sendPost(Net.Url.finalSubmit, {
            startId: this.startId,
            score: this.localScore,
            useSpId: propsId || null,
            useSpCnt: useCount || null,
            token: NetUtils.md5(this.startId + this.localScore + "dui88")
        }, (res) => {
            const data = res.data;
            if (data.prize) {
                PanelCtrl.instance.show(WinningPanel, {
                    score: data.finalScore,
                    highestScore: data.maxScore,
                    rewardImgUrl: data.prize.icon,
                    rewardName: data.prize.name
                });
            } else {
                Loading.instace.hide();
                //未中奖
                PanelCtrl.instance.show(class extends Panel {
                    get skinKey() { return "NotWinningPanel" }
                    public labelScore: eui.Label;
                    public labelHighestScore: eui.Label;
                    public btnClose: MButton;
                    public btnRestart: MButton;
                    data: {
                        score: number,
                        highestScore: number
                    }


                    onSkinComplete() {
                        super.onSkinComplete();
                        this.labelScore.text = this.data.score.toString() + "分";
                        this.labelHighestScore.text = this.data.highestScore.toString() + "分";
                        this.btnClose.onTap(this, () => this.close());
                        this.btnRestart.onTap(this, () => this.close());
                    }

                    private close() {
                        this.hidePanel();
                        SceneCtrl.instance.change(StartScene);
                    }
                }, {
                    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();
    }
}