import Car from "./Car";
import {MConst} from "../Global/MConst";
import PlayerController from "./PlayerController";
import Ball from "./Ball";
import Physics from "../Phycics/Physics";
import GameObject from "./GameObject";
import {RectCollider, ColliderGroup} from "../Phycics/Collider";
import {MConfigs} from "../Global/MConfigs";
import Drop from "./Drop";
import {MUtils} from "../Global/MUtils";
import {arrayRemove, getBallScore} from "../Global/GUtils";
import MTimer from "../Global/MTimer";
import PhycicsSystem from "../Phycics/PhycicsSystem";
import Bullet, {HorizontalMoveMgr} from "./Bullet";
import {BallPoolGroup} from "../Pools/BallPool";
import AnimationPoolGroup from "../Pools/AnimationPool";
import FrameAnimation, {FrameAnimationMgr} from "../Component/FrameAnimation";
import GuideMgr from "../Mgr/GuideMgr";
import {Pool, PoolGroup, PoolElement} from "../Component/Pool";
import {BulletPool} from "../Pools/BulletPool";
import SpBoomEffectPoolGroup, {SpBoomEffectMgr} from "./SpBoomEffect";
import DropPoolGroup from "../Pools/DropPool";
import DropBlinkMgr from "../Mgr/DropBlinkMgr";
import GameMgr from "../Mgr/GameMgr";
import {DataMgr} from "../Mgr/DataMgr";
import {getAsset, getTexture} from "../utils";

type LayerName = "Bullet" | "Ball" | "Drop" | "Effect";

export default class Game {
	/****** property ******/
	/**
	 *
	 *  当前得分
	 * @readonly
	 */
	public get score(): number {
		return this._score;
	}

	/**
	 * 子弹分数
	 * @readonly
	 */
	public get bulletScore(): number {
		return this._bulletScore;
	}

	/**
	 * 火力分数
	 * @readonly
	 */
	public get powerScore(): number {
		return this._powerScore;
	}

	/**设置游戏暂停状态 */
	public set pause(v: boolean) {
		this._pause = v;

		this.timing = !this._pause; //取反

		for (let mgr of this.mgrs) {
			mgr.pause = this._pause;
		}
	}

	public get pause() {
		return this._pause;
	}

	private _pause: boolean = false;

	private _updateData = {};

	/**游戏结束回调函数 */
	public onGameOver: () => void;

	/**分数改变时的回调函数 delta是分数的变化量 */
	public onScoreChange: (delta: number) => void;

	/****** public method ******/
	/**复活 */
	public revive() {
		this._car.revive();
		this.pause = false;
	}

	/**销毁游戏 */
	public destroy() {
		this.node.dispose();
		for (let mgr of this.mgrs) {
			mgr.onDestroy();
		}
		MTimer.removeOnFrame("Game");
		MTimer.removeOnFrame("Car");
	}

	private mgrs: GameMgr[] = [];
	public _phycicsSystem: PhycicsSystem = new PhycicsSystem();
	public _boomEffectMgr: SpBoomEffectMgr = new SpBoomEffectMgr();
	public _animationMgr: FrameAnimationMgr = new FrameAnimationMgr();
	public _horizontalMoveMgr: HorizontalMoveMgr = new HorizontalMoveMgr();

	private needSubmitCount = 0; //实际分数减去ConstantSubmitScoreNum的次数

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

	private loadRes() {
		FrameAnimation.loadRes(
			getTexture("217164f4-a185-429c-8706-818137a4e438"),
			getAsset('763d2430-ad16-42c6-8bf0-d7337b05b247')
		);
		FrameAnimation.loadRes(
			getTexture("53c65221-3fbc-41d9-8cef-8a846876fe06"),
			getAsset('b82d6a84-8423-4be2-add5-ed9129e8c700')
		);
	}

	private initMgrs() {
		this.mgrs = [
			this._dropBlinkMgr,
			this._phycicsSystem,
			this._boomEffectMgr,
			this._animationMgr,
			this._horizontalMoveMgr
		];

		for (let mgr of this.mgrs) {
			mgr.onInit();
		}
	}

	constructor(parent: engine.Container) {
		DataMgr.game = this;
		this.initMgrs();
		this.pause = false;

		this.loadRes();

		parent.addChild(this.node);
		GuideMgr.instance.container = parent;

		this.node.width = MConst.DesignResolution.width;
		this.node.height = MConst.DesignResolution.height;
		let bg = new engine.Image(getTexture("d99368b8-af5d-4d9e-981e-7bce3e1c1e84"));
		this.node.addChild(bg);
		//子弹层
		this.node.addChild(this.layers.bullet);
		//炮车
		let car = new Car();
		car.x = this.node.width / 2 - car.width / 2;
		car.y = MConst.GroundLine - car.height + 30;
		this.node.addChild(car);
		car.addComponent(Physics);
		car.onDied = () => {
			this.over();
		};
		this._car = car;

		//球的层
		this.node.addChild(this.layers.ball);
		//掉落物层
		this.node.addChild(this.layers.drop);
		//特效层
		this.node.addChild(this.layers.effect);
		//动画层
		this.node.addChild(this.layers.animation);

		//禁用层级交互事件
		for (let k in this.layers) {
			this.layers[k].mouseChildren = false;
			this.layers[k].mouseEnabled = false;
		}

		//玩家控制器
		let playerController = new PlayerController();
		playerController.onTouchMove = (deltaX) => {
			if (!this.pause) {
				car.move(deltaX);
			}
		};
		this.node.addChild(playerController);

		//添加监听器
		MTimer.onFrame("Game", this.onUpdate, this);

		this.createPools();

		//创建墙和地面
		this.createWall();

		//开始倒计时
		this.timing = true;

		// this._BulletScore = 140;
		// this._PowerScore = 2000;
	}


	/****** private method ******/

	private over() {
		this.pause = true;
		engine.globalEvent.dispatchEvent("game-over", this._updateData);
		this.onGameOver && this.onGameOver();
	}

	private createWall() {
		let topWall = new GameObject();
		topWall.x = -500;
		topWall.y = -500;
		this.node.addChild(topWall);
		//添加天花板碰撞器
		let topWallCollider = topWall.addComponent<RectCollider>(RectCollider);
		topWallCollider.setData(0, 0, MConst.DesignResolution.width + 1000, 490);
		topWallCollider.group = ColliderGroup.Top;

		//创建地面节点
		let ground = new GameObject();
		ground.x = -500;
		ground.y = MConst.GroundLine;
		this.node.addChild(ground);
		//添加地面碰撞器
		let groundCollider = ground.addComponent<RectCollider>(RectCollider);
		groundCollider.setData(0, 0, MConst.DesignResolution.width + 1000, 1000);
		groundCollider.group = ColliderGroup.Ground;

		//创建左墙节点
		let leftWall = new GameObject();
		leftWall.x = -500;
		leftWall.y = -500
		this.node.addChild(leftWall);
		//添加左墙碰撞器
		let leftWallCollider = leftWall.addComponent<RectCollider>(RectCollider);
		leftWallCollider.setData(0, 0, 500, MConst.DesignResolution.height + 1000);
		leftWallCollider.group = ColliderGroup.Wall;

		//创建右墙节点
		let rightWall = new GameObject();
		rightWall.x = MConst.DesignResolution.width;
		rightWall.y = -500
		this.node.addChild(rightWall);
		//添加右墙碰撞器
		let rightWallCollider = rightWall.addComponent<RectCollider>(RectCollider);
		rightWallCollider.setData(0, 0, 500, MConst.DesignResolution.height + 1000);
		rightWallCollider.group = ColliderGroup.Wall;
	}


	private createBall() {
		let size = MUtils.randomInt(0, MConfigs.size.length);
		if (GuideMgr.instance.guideFlag == true) {
			size = 3;
		}
		let color = MUtils.randomInt(size, MConst.MaxColorIndex);
		let ball = this._pool.ball.spwan(size);
		let dir: 1 | -1 = Math.random() > 0.5 ? -1 : 1;
		const score = getBallScore(this._BulletScore, this._PowerScore, color);
		ball.init(color, dir, score);
		ball.startBornStage(dir);

		this.createBallCD = 1500;
	}


	private onUpdate() {
		for (let i = 0; i < this._ballList.length; i++) {
			this._ballList[i].updateScoreLabel();
		}

		//分数更新
		if (this.bulletScoreUpdateFlag) {
			let score = this._BulletScore;
			//更新最大球共存数量
			this.updateMaxBallNum(score);
			this.bulletScoreUpdateFlag = false;
		}

		//检查球的创建
		if (this.createBallCD > 0) {
			this.createBallCD -= MTimer.deltaTime;
		}

		if (this._ballList.length < this.curMaxBallNum) {
			if (this.createBallCD <= 0) {
				this.createBall();
			}
		}

		if (!this.pause) {
			this.outputEvent();
		}
	}

	private outputEvent() {
		let updateData = {
			score: this.score,
			bulletScore: this.bulletScore,
			powerScore: this.powerScore,
		};
		let needSend = false;
		for (let key in updateData) {
			if (updateData[key] != this._updateData[key]) {
				this._updateData[key] = updateData[key];
				needSend = true;
			}
		}
		if (needSend) {
			engine.globalEvent.dispatchEvent("game-update", updateData);
		}
	}

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

	/**
	 * Ball类专用
	 * 不建议调用
	 */
	public _shake() {
		let count = 0;
		let callback = () => {
			if (count > 1) return;
			count++;

			this.node.x = 10;
			engine.Tween.get(this.node)
				.wait(1, true)
				.to({x: 0, y: -10}, 1)
				.to({x: -10, y: 0}, 1)
				.to({x: 0, y: 10}, 1)
				.to({x: 0, y: 0}, 1)
				.call(callback);
		};

		callback();
	}

	public _pool: {
		ball: BallPoolGroup,
		bullet: BulletPool,
		boomEffect: SpBoomEffectPoolGroup
		animation: AnimationPoolGroup,
		drop: DropPoolGroup
	} = null;

	private createPools() {
		this._pool = {
			ball: new BallPoolGroup(this.layers.ball),
			bullet: new BulletPool(this.layers.bullet),
			animation: new AnimationPoolGroup(this.layers.animation),
			boomEffect: new SpBoomEffectPoolGroup(this.layers.effect),
			drop: new DropPoolGroup(this.layers.drop)
		};
	}

	public _playBoomEffect(position: engine.Point, size: number) {
		const effect = this._pool.boomEffect.spwan(MUtils.randomInt(0, 3), size);
		effect.position = position;
	}

	public _createAnimation(key: string) {
		const clip = this._pool.animation.spwan(key);
		clip.onCompleted = () => {
			this._pool.animation.recycle(key, clip);
		}
		return clip;
	}


	/****** private field ******/

	public _dropBlinkMgr = new DropBlinkMgr();

	private layers = {
		bullet: new engine.Container(),
		ball: new engine.Container(),
		drop: new engine.Container(),
		effect: new engine.Container(),
		animation: new engine.Container()
	};

	/**不建议使用 */
	public get _score(): number {
		return this.__score;
	}

	public set _score(v: number) {
		this.localScore += (v - this.__score);
		this.__score = v;
	}

	/**不建议使用 */
	public _car: Car = null;
	private __score: number = 0;
	private curMaxBallNum = 1;
	private timing = false;
	private createBallCD: number = 0;
	private bulletScoreUpdateFlag = false;
	/**不建议使用 */
	public _ballList: Ball[] = [];
	private node: engine.Container = new engine.Container();

	/**不建议使用 */
	public get _BulletScore(): number {
		return this._bulletScore;
	}

	public onGuideDone() {
		engine.globalEvent.dispatchEvent('game-start');
	}

	public set _BulletScore(v: number) {
		if (v == this._bulletScore) return;
		v = Math.max(v, 0);
		this._bulletScore = v;
		this.bulletScoreUpdateFlag = true;
	}

	private _bulletScore: number = 0;

	/**不建议使用 */
	public get _PowerScore(): number {
		return this._powerScore;
	}

	public set _PowerScore(v: number) {
		v = Math.max(v, 100);
		this._powerScore = v;
	}

	private _powerScore: number = 100;
}

