/**
 * Created by rockyl on 2018/8/16.
 */
import Block from "./Block";
import {Background} from "./Background";
import Player from "./Player";
import {props} from "../props";
import {createSvga, playSound} from "./utils";
import {Base} from "./Base";
import {GuideLayer} from "./GuideLayer";
import {GoldBag} from "./GoldBag";
import ObjectPool = engine.ObjectPool;

const PoolName: string = 'gold-bag';

ObjectPool.registerPool(PoolName, function () {
	return new GoldBag();
}, function (item: GoldBag, data) {
	item.reset(data);
});

export default class GameView extends engine.Container {
	background: Background;
	base: Base;
	frontContainer: engine.Container;
	blockContainer: engine.Container;
	guideLayer: GuideLayer;
	player: Player;
	hitEffect: svga.Svga;

	currentBlock: Block;
	needHitTest;

	index = -1;
	blockComplete;

	baseOffset: number;

	timer;

	_pos;
	_score;
	_remainToShowGoldBag;
	lastLandType;

	_touchEnabled;
	private _hasSetup;

	constructor() {
		super();

		this.baseOffset = -props.baseOffset + props.playerOffset;
		this.once(engine.Event.ADDED_TO_STAGE, this.setup, this);
	}

	setup() {
		if (this._hasSetup) {
			return;
		}
		this._hasSetup = true;

		const {width, height} = this.stage;

		const background = this.background = new Background();
		this.addChild(background);
		background.setup();

		const frontContainer = this.frontContainer = new engine.Container();
		frontContainer.x = width / 2;
		this.addChild(frontContainer);

		const guideLayer = this.guideLayer = new GuideLayer();
		this.addChild(guideLayer);

		const base = this.base = new Base();
		frontContainer.addChild(base);
		base.setup();

		const blockContainer = this.blockContainer = new engine.Container();
		frontContainer.addChild(blockContainer);

		const player = this.player = new Player();
		frontContainer.addChild(player);
		player.setup();
		player.addEventListener('jump-on-top', this.onPlayerJumpOnTop, this);

		ObjectPool.recycleObject(PoolName, ObjectPool.getObject(PoolName, {
			y: 0,
			remain: 0,
		}));

		this.hitEffect = createSvga('被撞烟雾', 'hitEffectAnchor');

		this.pos = 0;

		this.background.setup();
		this.addEventListener(engine.Event.ENTER_FRAME, this.onEnterFrame, this);

		this.reset();
	}

	async reset(revive = false) {
		this.base.reset();
		this.index = -1;
		this._score = 0;

		if (revive) {
		} else {
			this.pos = 0;
			const blockContainer = this.blockContainer;
			for (let i = 0, li = blockContainer.children.length; i < li; i++) {
				const block = <Block>blockContainer.getChildAt(i);
				block.playLeave();
			}
		}

		for (let i = 0; i < props.initBlockCount; i++) {
			this.addBlock(false);
		}

		for (let i = 0, li = this.goldBags.length; i < li; i++) {
			const goldBag = this.goldBags[i];
			this.frontContainer.removeChild(goldBag);
			ObjectPool.recycleObject(PoolName, goldBag);
		}
		this.goldBags.splice(0);
		//this.addGoldBag();

		this.playZoom('in');
		await this.resetPlayer(revive);
	}

	async resetPlayer(revive = false) {
		this.player.reset(revive);
		this.player.y = this.baseOffset - (this.index + 1) * props.blockHitHeight;
	}

	async start(revive = false) {
		if (!revive) {
			await this.player.playReady();
		}

		let guideFlagKey = 'jump-high-guide_' + props.guideFlagKey;
		let guideFlag = localStorage.getItem(guideFlagKey);
		if (!guideFlag) {
			localStorage.setItem(guideFlagKey, '1');
			await this.guideLayer.show('', {y: this.stage.height + this.player.y - 280});
		}

		this.lastLandType = 0;
		this._remainToShowGoldBag = props.goldBagScoreMultiple - props.goldBagScoreSubtraction;
		this._touchEnabled = true;
		setTimeout(() => {
			this.addBlock();
		}, 100);
		engine.globalEvent.dispatchEvent('jump-high-game-start');
	}

	pause() {
		if (this.currentBlock) {
			engine.Tween.pauseTweens(this.currentBlock);
		}
		engine.Tween.pauseTweens(this.player);
	}

	resume() {
		if (this.currentBlock) {
			engine.Tween.resumeTweens(this.currentBlock);
		}
		engine.Tween.resumeTweens(this.player);
	}

	async revive() {
		this.blockContainer.getChildAt(this.index).visible = false;
		this.index--;
		await this.resetPlayer(true);
		await this.playZoom('in');
		this.start(true);
	}

	addBlock(animation = true) {
		this.index++;

		const blockContainer = this.blockContainer;
		let block: Block;

		if (blockContainer.children.length > this.index) {
			block = <Block>blockContainer.getChildAt(this.index);
			block.visible = true;
		} else {
			block = new Block();
			blockContainer.addChild(block);
		}
		block.reset({
			type: Math.floor(Math.random() * props.blockAssets.length),
		});
		block.y = this.baseOffset - this.index * props.blockHitHeight;

		this.blockComplete = false;
		block.playEnter(this.index, animation).then(
			(data) => {
				this.blockComplete = true;
			}
		);

		if (animation) {
			this.needHitTest = true;
		}
		this.currentBlock = block;

		/*if (this.blockCount > 0 && this.blockCount % props.goldBagMultiple === 0) {
			this.addGoldBag();
		}*/
	}

	private goldBags: GoldBag[] = [];

	addGoldBag() {
		let goldBag = <GoldBag>ObjectPool.getObject(PoolName, {
			y: this.baseOffset - (this.blockCount + props.goldBagDistance + props.goldBagJumpSubtraction) * props.blockHitHeight,
			remain: props.goldBagDistance,
		});
		this.frontContainer.addChild(goldBag);
		this.goldBags.push(goldBag);
	}

	private nextToUpdateScore;

	async playOpenGoldBag() {
		for (let i = 0, li = this.goldBags.length; i < li; i++) {
			const goldBag = this.goldBags[i];
			goldBag.remain--;

			if (goldBag.remain <= 0) {
				this.goldBags.splice(i, 1);
				i--;
				li--;

				this.nextToUpdateScore = true;
				await goldBag.playOpen();
				this.frontContainer.removeChild(goldBag);
				ObjectPool.recycleObject(PoolName, goldBag);
			}
		}
	}

	onPlayerJumpOnTop() {
		if (this.nextToUpdateScore) {
			this.nextToUpdateScore = false;
			this.scoreChange(4);
			playSound('撞击钱袋音效');
		}
	}

	get blockCount() {
		return this.index - props.initBlockCount + 1;
	}

	get pos() {
		return this._pos;
	}

	set pos(v) {
		this._pos = v;
		this.updatePos();
	}

	updatePos() {
		this.frontContainer.y = this.stage.height + this._pos;
	}

	private onEnterFrame(event) {
		if (this.needHitTest) {
			if (this.currentBlock) {
				const {x: bx, y: by, dir} = this.currentBlock;
				const {x: px, y: py} = this.player;
				const {blockHitWidth, blockHitHeight, playerWidth} = props;

				let hitOn = false;
				if (Math.abs(px - bx) < (blockHitWidth + playerWidth) / 2) {
					this.player.changeBaseY(by - blockHitHeight);
					if (py > by - blockHitHeight) {
						hitOn = true;
					}
				}

				if (hitOn) {
					this.onHitOn(dir);
				}
			}
		}
	}

	async onHitOn(dir) {
		this._touchEnabled = false;
		this.needHitTest = false;

		clearInterval(this.timer);

		this.currentBlock.stop();
		this.playHitEffect(dir);
		await this.player.hitAway(dir);
		this.playZoom('out');
		await this.player.parachute(dir);

		engine.globalEvent.dispatchEvent('jump-high-game-end');
	}

	async jump() {
		if (!this._touchEnabled) {
			return;
		}
		this._touchEnabled = false;
		this.playOpenGoldBag();
		const result: any = await this.player.jump();
		if (result) {
			if (result.aboveBlock) {
				let pos = Math.abs(this.currentBlock.x);
				let type = 0;
				if (pos > 0) {
					type = pos > 0 && pos < props.scoreThreshold ? 2 : 3;
				}
				let lastLandType = this.lastLandType;
				this.lastLandType = type;
				if (type === 0) {
					if (lastLandType !== type) {  //如果前一次不是完美落地说明block没到正中间，所以没有重叠
						type = 1;
					}
				}
				if (type < 3) {
					this.currentBlock.playEffect();
				}
				this.player.playLand(type, this.currentBlock.dir);
				this.currentBlock.stop();
				this.scoreChange(type);
				await this.playShake();
				await new Promise(resolve => {
					engine.Tween.get(this, null, null, true)
						.to({pos: props.blockHitHeight * this.index}, 300, engine.Ease.cubicOut)
						.call(resolve)
				});
				this.addBlock();
			}
			this._touchEnabled = true;
		}
	}

	private scoreChange(type) {
		let scoreAdd = props.scoreWeights[type];
		this._score += scoreAdd;
		this._remainToShowGoldBag -= scoreAdd;
		let score = this._score;
		engine.globalEvent.dispatchEvent('jump-high-score', {
			type,
			score,
			scoreAdd,
		});

		console.log(score, this._remainToShowGoldBag);

		if (this._remainToShowGoldBag <= 0) {
			this._remainToShowGoldBag += props.goldBagScoreMultiple;
			this.addGoldBag();
			console.log('addGoldBag');
		}
	}

	playHitEffect(dir) {
		let hitEffect = this.hitEffect;
		hitEffect.scaleX = dir;
		hitEffect.y = this.player.y - props.hitEffectAnchor.y;
		hitEffect.play(true, false);
		hitEffect.once(engine.Event.END_FRAME, function () {
			this.frontContainer.removeChild(hitEffect);
		}, this);
		this.frontContainer.addChild(hitEffect);
	}

	playZoom(type: 'in' | 'out', duration = 700) {
		this.background.playZoom(type, duration);
		let count = this.stage.height / props.blockHitHeight;
		return new Promise(resolve => {
			this.frontContainer.anchorY = -props.blockHitHeight * (this.index + count * 1.3) + props.baseOffset;
			let scale = type === 'in' ? 1 : /*Math.max(*/Math.min((this.stage.height / props.blockHitHeight / (this.index + count)), props.maxScale)/*, props.minScale)*/;
			console.log(scale);
			engine.Tween.get(this.frontContainer, null, null, true)
				.to({scaleX: scale, scaleY: scale}, duration, engine.Ease.cubicInOut)
				.call(resolve);
		})
	}

	playShake(){
		const {x, y} = this.frontContainer;
		return new Promise(resolve => {
			const shakeOffset = 7;
			const duration = 30;
			engine.Tween.get(this.frontContainer, null, null, true)
				.to({x: x, y: y - shakeOffset}, duration)
				.to({x: x, y: y + shakeOffset}, duration)
				.to({x: x + shakeOffset, y: y}, duration)
				.to({x: x - shakeOffset, y: y}, duration)
				.to({x: x, y: y}, duration)
				.call(resolve)
		})
	}
}
