import MoveObjcet from "./MoveObject";
import Collider, { CircleCollider, ColliderGroup } from "./Collider";
import { MConfigs } from "../Global/MConfigs";
import { getBallScore, getProp } from "../Global/GUtils";
import MainScene from "../Scene/MainScene";
import MArray from "../MComponents/MArray";
import Pool, { PoolElement } from "../MComponents/Pool";
import { MConst } from "../Global/MConst";
import Drop from "./Drop";
import Bullet from "./Bullet";
import { MUtils } from "../Global/MUtils";
import DebugMgr from "../Mgr/DebugMgr";
import GameMgr from "../Mgr/GameMgr";
import MTimer from "../MComponents/MTimer";
import SoundMgr from "../Mgr/SoundMgr";
import MovieClipMgr from "../Mgr/MovieClipMgr";
import MTweenMgr from "../MComponents/MTween";

export default class Ball extends MoveObjcet implements PoolElement {
    private colorIndex = 0;
    private sizeIndex = 0;
    private bitmap: egret.Bitmap = null;
    public static readonly textures: MArray<egret.Texture> = new MArray<egret.Texture>();
    private labelScore: egret.BitmapText = null;
    private mCenterX = 0;
    private scaleRatio: number = 1;
    private initScore: number = null;
    private initColorIndex: number = 0;
    public collider: CircleCollider = null;
    private isBornStage = false;

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

        this.scoreUpdateFlag = true;
        if (v < 0) v = 0;
        let that = this; //性能瓶颈优化
        that._score = v;
    }

    private onScoreIsZero() {
        if (GameMgr.instance.guideFlag == true) {
            this.drop();
        } else {
            if (this.sizeIndex > 0) {
                this.split();
            } else {
                this.drop();
            }
        }

        this.playBoomEffect();
        SoundMgr.instance.playEffect("boom_mp3");
        this.context.ballPool.destroy(this);
    }

    constructor(context: MainScene) {
        super(context);
        this.bitmap = new egret.Bitmap();
        this.addChild(this.bitmap);

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

        let collider = this.addComponent(CircleCollider) as CircleCollider;
        collider.group = ColliderGroup.Ball;
        this.collider = collider;
    }

    public updateScoreLabel() {
        if (this.scoreUpdateFlag == true) {
            const score = this.score;
            if (score >= 1000) {
                this.labelScore.text = (Math.floor(score / 100) / 10).toString() + "k";
            } else {
                this.labelScore.text = this.score.toString();
            }
            this.labelScore.x = this.mCenterX - this.labelScore.width / 2 * this.scaleRatio;
            this.scoreUpdateFlag = false;

            //判断是否需要更改颜色
            if (this.initScore == null) return;
            if (this.score > 0) {
                let indexOffset = Math.floor((this.initScore - this.score) / this.initScore / (1 / (this.initColorIndex + 1)));
                if (this.initColorIndex - indexOffset != this.colorIndex) { //目标颜色和当前颜色不一致
                    //更新颜色
                    this.colorIndex = this.initColorIndex - indexOffset;
                    this.bitmap.texture = RES.getRes(`ball_${this.colorIndex}_png`)
                }
            } else {
                this.onScoreIsZero();
            }
        }
    }

    init(direction: 1 | -1, colorIndex: number, sizeIndex: number, score?: number): Ball {
        let scaleRatio = MConfigs.size[sizeIndex]; //整体缩放系数
        if (typeof scaleRatio == "undefined" || scaleRatio > 5 || colorIndex < 0) {
            console.error("invalid params", sizeIndex, scaleRatio, colorIndex);
            return;
        }

        this.scaleRatio = scaleRatio;
        this.colorIndex = colorIndex;
        this.initColorIndex = colorIndex;
        this.sizeIndex = sizeIndex;

        //创建图像
        let texture = RES.getRes(`ball_${colorIndex}_png`);
        this.bitmap.texture = texture;

        //设置图像大小
        this.bitmap.width = texture.textureWidth * scaleRatio;
        this.bitmap.height = texture.textureHeight * scaleRatio;
        let bitmapWidth = this.bitmap.width;
        let bitmapHeight = this.bitmap.height;
        this.mCenterX = bitmapWidth / 2;

        //创建文字
        let textScore = this.labelScore;
        textScore.text = "0";
        textScore.scaleX = textScore.scaleY = scaleRatio;
        textScore.x = bitmapWidth / 2 - textScore.width / 2 * scaleRatio;
        textScore.y = bitmapHeight / 2 - textScore.height / 2 * scaleRatio;

        //添加碰撞器
        let collider = this.getComponent(CircleCollider) as CircleCollider;
        collider.setData(bitmapWidth / 2, bitmapHeight / 2, bitmapHeight / 2);

        if (!score) {
            //设置初始分数
            this.initScore = getBallScore(this.context.bulletScore, this.context.powerScore, this.colorIndex);
            this.score = this.initScore;
        } else {
            this.initScore = score;
            this.score = this.initScore;
        }

        this.physics.rotateVelocity = direction * MConst.BallRotateSpeed * Math.pow(1 / scaleRatio, 0.5);

        return this;
    }

    startBornStage(dir: 1 | -1) {
        this.isBornStage = true;
        if (dir == 1) {
            this.x = 0 - (this.width / 2 + 10);
        } else if (dir == -1) {
            this.x = MConst.DesignResolution.x + (this.width / 2 + 10);
        }
        this.y = MConst.BallInitPosY;

        this.physics.velocity.x = this.getRandomVelocityX(dir) + 1 * dir;
        this.physics.onMoved = ((owner) => {
            if (dir == 1) {
                if (this.x > this.width / 2) {
                    this.startGravityStage(dir);
                    this.physics.onMoved = null;
                }
            } else if (dir == -1) {
                if (this.x < MConst.DesignResolution.x - this.width / 2) {
                    this.startGravityStage(dir);
                    this.physics.onMoved = null;
                }
            }
        });
    }

    private getRandomVelocityX(direction: 1 | -1) {
        return direction * MConst.BallVelocityX * (1 + ((1 / this.scaleRatio) - 1) * MUtils.random(0, MConst.BallVelocityXRandomFactor));
    }

    startGravityStage(direction: 1 | -1) {
        this.isBornStage = false;
        this.physics.velocity.x = this.getRandomVelocityX(direction);
        this.physics.acceleration.y = MConst.Gravity;
    }

    onCollisionEnter(other: Collider) {
        if (other.group == ColliderGroup.Bullet) {
            let power = (other.owner as Bullet).power;
            this.score = this.score - power;
            this.context.score += power;
        }

        if (other.group == ColliderGroup.Ground) {
            //最小的垂直速度
            const minVelocityY = MConst.BallVelocityY * (1 + ((1 / this.scaleRatio) - 1) * MUtils.random(0, MConst.BallVelocityYRandomFactor));
            //根据物理能量损耗规律计算得到的垂直速度
            const physicsVelocity = Math.abs(this.physics.velocity.y) * 0.9; //能量损耗系数
            this.physics.velocity.y = -(Math.max(minVelocityY, physicsVelocity));

            //播放灰尘动画
            let clip = MovieClipMgr.instance.get("duang");
            clip.x = this.x - 160 * this.scaleRatio;
            clip.y = this.y + 12 * this.scaleRatio;
            clip.scaleX = clip.scaleY = 1.76 * this.scaleRatio;
            this.context.animationLayer.addChild(clip);
            clip.autoRecyclePlay("duang");
            //判断是否会震动地面
            if (this.sizeIndex >= MConfigs.size.length - 2) {
                this.context.shake();
                SoundMgr.instance.playEffect("dong_mp3");
            }
        }

        if (other.group == ColliderGroup.Wall && !this.isBornStage) {
            this.physics.velocity.x = -this.physics.velocity.x;
            this.physics.rotateVelocity = -this.physics.rotateVelocity;
        }

        if (other.group == ColliderGroup.Car) {
            let callback = () => {
                egret.Tween.get(this.labelScore).set({ textColor: 0xff4b4b }).wait(300).set({ textColor: 0xffffff }).wait(300).call(callback);
            };
            callback();
        }
    }

    onCollisionStay(other: Collider) {
        if (other.group == ColliderGroup.Wall && !this.isBornStage) {
            let dir: 1 | -1 = other.owner.x < 0 ? 1 : -1;
            this.physics.velocity.x = dir * Math.abs(this.physics.velocity.x);
            this.physics.rotateVelocity = dir * Math.abs(this.physics.rotateVelocity);

        }
    }

    /**@inheritdoc */
    onElementRecycle() {
        this.visible = false;
        this.physics.velocity.y = 0;
        this.physics.velocity.x = 0;
        this.physics.acceleration.y = 0;
        this.physics.acceleration.x = 0;
        this.disableAllComponents();
    }
    onElementInit() {
        this.visible = true;
        this.enableAllComponents();
    }

    private split() {
        let sizeIndex = this.sizeIndex - 1;
        let score = Math.ceil(this.initScore / 2 * (Math.random() * 0.4 + 0.8));
        let colorIndex = Math.max(this.initColorIndex - 1, 0);

        let callback = (direction: 1 | -1) => {
            let ball = this.context.ballPool.create(this.context).init(direction, colorIndex, sizeIndex, score);
            ball.x = this.x;
            ball.y = this.y;
            ball.startGravityStage(direction);
            ball.physics.velocity.y = -MConst.BallSplitVelocityY;

        }

        callback(1);
        callback(-1);
    }

    private drop() {
        //掉落数
        let index = 1;
        //根据子弹分数判断索引
        let bulletScore = this.context.bulletScore;
        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.context.dropPool.create(this.context).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 playBoomEffect() {
        const color = MConfigs.boomEffectColor[MUtils.randomInt(0, MConfigs.boomEffectColor.length)];
        const scaleRatio = this.scaleRatio;

        //星星
        const range = MConst.DefaultBallWidth * 1.5 * this.scaleRatio;
        for (let i = 0; i <= 7; i++) {
            const star = this.starPool.get().init(color);
            const randomMax = 0.5 + (scaleRatio - 0.3125) / 2;
            const scale = MUtils.random(randomMax - 0.64 * scaleRatio, randomMax);
            star.width = MConst.DefaultStarSize.width * scale;
            star.height = MConst.DefaultStarSize.height * scale;
            star.x = this.x - star.width / 2;
            star.y = this.y - star.height / 2;

            MTweenMgr.instance.get(star)
                .to({
                    x: this.x + MUtils.random(-1, 1) * range - star.width / 2,
                    y: this.y + MUtils.random(-1, 1) * range - star.height / 2,
                    alpha: 0
                }, BoomEffectDuration, false)
                .call(() => {
                    this.starPool.recycle(star);
                });

        }

        //环
        for (let i = 0; i <= 2; i++) {
            let ring = this.ringPool.get().init(color);
            ring.width = MConst.DefaultRingWidth * scaleRatio;
            ring.height = MConst.DefaultRingWidth * scaleRatio;
            ring.anchorOffsetX = ring.width / 2;
            ring.anchorOffsetY = ring.height / 2;
            ring.x = this.x;
            ring.y = this.y;
            ring.scaleX = ring.scaleY = 0.186 * (i + 1);
            MTweenMgr.instance.get(ring)
                .to({
                    scaleX: ring.scaleX * 1.792,
                    scaleY: ring.scaleY * 1.792,
                    alpha: 0
                }, BoomEffectDuration + (i - 1) * BoomEffectDuration * 0.33, false)
                .call(() => {
                    this.ringPool.recycle(ring);
                });
        }
    }

    private starPool = new EffectBitmapPool(this.context.animationLayer, "star_png");
    private ringPool = new EffectBitmapPool(this.context.animationLayer, "ring_png");
}

const BoomEffectDuration = 400;

class EffectBitmapPool extends Pool<EffectBitmap>{
    private layer: egret.DisplayObjectContainer = null;
    private resName: string = null;
    constructor(layer: egret.DisplayObjectContainer, resName: string) {
        super();
        this.layer = layer;
        this.resName = resName;
    }
    createElement() {
        let star = new EffectBitmap(RES.getRes(this.resName));
        this.layer.addChild(star);
        return star;
    }
}


class EffectBitmap extends egret.Bitmap implements PoolElement {
    init(color: number) {
        MUtils.setColorFilter(this, color);
        return this;
    }
    onElementInit() {
        this.visible = true;
        this.alpha = 1;
    }
    onElementRecycle() {
        this.visible = false;
    }
}
