import MoveObjcet from "../Game/MoveObject";
import Collider, { CircleCollider, ColliderGroup } from "../Game/Collider";
import { MConfigs } from "./MConfigs";
import { getBallScore, getProp } from "./GUtils";
import MainScene from "../Scene/MainScene";
import MArray from "../components/MArray";
import Pool, { PoolElement } from "../Components/Pool";
import { ThroughSubmitData } from "../../libs/tw/data/custom/throughSubmit/ThroughSubmitData";
import { MConst } from "./MConst";
import Drop from "../Game/Drop";
import Bullet from "../Game/Bullet";
import { MUtils } from "./MUtils";
import DebugMgr from "../Mgr/DebugMgr";
import GameMgr from "../Mgr/GameMgr";
import MTimer from "../components/MTimer";
import SoundMgr from "../Mgr/SoundMgr";
import MovieClipMgr from "../Mgr/MovieClipMgr";
import MTweenMgr from "../components/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: eui.Label = null;
    private labelScoreShadow: egret.TextField = null;
    private mCenterX = 0;
    private scaleRatio: number = 1;
    private initScore: number = null;
    private initColorIndex: number = 0;
    public collider: CircleCollider = null;
    private isBornStage = false;

    private _score = 0;
    public get score() {
        return this._score;
    }
    public set score(v: number) {
        if (v < 0) v = 0;
        let that = this; //性能瓶颈优化
        that._score = v;

        //分数标签更新
        that.labelScore.text = v.toString();
        that.labelScoreShadow.text = v.toString();
        that.labelScore.x = that.mCenterX - that.labelScore.width / 2 - 3 * that.scaleRatio;
        that.labelScoreShadow.x = that.labelScore.x;

        //判断是否需要更改颜色
        if (this.initScore == null) return;
        if (v > 0) {
            let loseScore = (that.initScore - v) / that.initScore;
            let indexOffset = Math.floor(loseScore / (1 / (that.initColorIndex + 1)));
            let targetColorIndex = that.initColorIndex - indexOffset;
            if (targetColorIndex != that.colorIndex) { //目标颜色和当前颜色不一致
                //更新颜色
                that.colorIndex = that.initColorIndex - indexOffset;
                that.bitmap.texture = Ball.textures.get(that.colorIndex);
            }
        } else {
            if (this.sizeIndex > 0) {
                this.split();
            } else {
                this.drop();
            }
            this.playBoomEffect();
            SoundMgr.instance.playEffect("boom_mp3");
            this.context.ballPool.destroy(this);
        }
    }

    public static init() {
        if (Ball.textures.length <= 6) {
            Ball.textures.clear();
            for (let i = 0; i <= 5; i++) {
                Ball.textures.push(RES.getRes(`ball_${i}_png`));
            }
        }
    }

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

        this.labelScore = new eui.Label();
        this.labelScoreShadow = new eui.Label();
        this.addChild(this.labelScoreShadow); //阴影在下
        this.addChild(this.labelScore);
        this.labelScoreShadow.alpha = 0.3;
        this.labelScoreShadow.textColor = 0x000000;
        this.labelScore.textColor = 0xffffff;

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

    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 = Ball.textures.get(colorIndex);
        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.size = 79 * scaleRatio;
        textScore.x = bitmapWidth / 2 - textScore.width / 2 - 3 * scaleRatio;
        textScore.y = bitmapHeight / 2 - textScore.height / 2 + 6 * scaleRatio;
        //创建文字阴影
        let shadow = this.labelScoreShadow
        shadow.text = textScore.text;
        shadow.x = textScore.x
        shadow.y = textScore.y + 3 * scaleRatio;
        shadow.size = textScore.size;

        //添加碰撞器
        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);
        // console.error(Math.abs(this.physics.velocity.x));
        this.physics.onMoved.add((owner, remove) => {
            if (dir == 1) {
                if (this.x > this.width / 2) {
                    this.startGravityStage(dir);
                    remove();
                }
            } else if (dir == -1) {
                if (this.x < MConst.DesignResolution.x - this.width / 2) {
                    this.startGravityStage(dir);
                    remove();
                }
            }
        });
    }

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

    startGravityStage(direction: 1 | -1) {
        this.isBornStage = false;
        this.physics.velocity.x = this.getRandomVelocityX(direction);
        // console.error(Math.abs(this.physics.velocity.x));
        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) {
            this.physics.velocity.y = -(MConst.BallVelocityY * (1 + ((1 / this.scaleRatio) - 1) * MUtils.random(0, 0.1)));

            //播放灰尘动画
            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;
            // this.applyPhysicsRotate(other);

        }

        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();

            MTimer.onFrame((dt, remove) => {
                if (!GameMgr.instance.pause) {
                    egret.Tween.removeTweens(this.labelScore);
                    this.labelScore.textColor = 0xffffff;
                    remove();
                }
            });
        }
    }

    private applyPhysicsRotate(wall: Collider) {
        /* //获取墙面的法向量
        let wallVec: egret.Point = wall.owner.x < 0 ? new egret.Point(1, 0) : new egret.Point(-1, 0);
        //获取墙面左面的法向量
        let wallLeftVec = MUtils.getVector(MUtils.degreesAngelAdd(MUtils.getAngle(wallVec), 90));
        //当前速度向量
        let velocity: egret.Point = new egret.Point(this.physics.velocity.x, -this.physics.velocity.y);
        let dir = MUtils.dot(velocity, wallLeftVec); */
        // let dir = Math.abs(this.physics.velocity.y) - 0.5 * MConst.BallVelocityY;
        let dir = this.physics.velocity.x;
        if (dir < 0) { //改变旋转方向
            this.physics.rotateVelocity = -MConst.BallRotateSpeed;
        } else {
            this.physics.rotateVelocity = MConst.BallRotateSpeed;
        }
    }

    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.posX = this.posX;
            ball.posY = this.posY;
            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.posX > MConst.DesignResolution.x / 2 ? -1 : 1;

        for (let i = 0; i < drops.length; i++) {
            let drop = drops[i];
            drop.x = this.posX - drop.bitmap.width / 2;
            drop.y = this.posY - drop.bitmap.height / 2;
            let offsetRatio = (i - 1) + (2 - (drops.length - 1));
            let x = MConst.DropVelocityX.x + offsetRatio * MConst.DropVelocityX.offset;
            x *= MUtils.random(0.9, 1.1);
            drop.physics.velocity.x = dir * x;
            drop.physics.velocity.y = -(x * 5 * MUtils.random(0.9, 1.1));
        }
    }

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