/**
 * Created by rockyl on 2018/11/23.
 */

import {Entity, traverse, traversePostorder} from "./Entity";
import {injectProp} from "../tools/utils";
import {setupContext as setupInteractContext} from "./context/InteractContext";
import {clear, ScaleMode, setupContext as setupRenderContext} from "./context/RenderContext";
import './requestAnimationFrame';

/**
 * 创建引擎实例
 * @param options
 */
export function createEngineInstance(options?) {
	let instance = new ScillaEngine();
	instance.setup(options);
}

/**
 * 引擎类
 */
class ScillaEngine {
	/**
	 * 默认配置
	 */
	options: any = {
		fps: 60,
		designWidth: 750,
		designHeight: 1334,
		scaleMode: ScaleMode.FIXED_WIDTH,
		touchEnabled: true,
	};

	_flush: number = 0;
	_currentFlush: number = 0;
	tsStart: number;
	tsLast: number;
	lastFPS: number = 0;

	private readonly root: Entity;

	constructor() {
		this.root = new Entity('root');
		this.root._restrict();
	}
	
	setup(options?){
		injectProp(this.options, options);

		const {canvas, designWidth, designHeight, scaleMode, modifyCanvasSize, touchEnabled} = options;

		let canvasElement = typeof canvas == 'object' ? canvas : document.getElementById(canvas);

		setupInteractContext({
			canvas: canvasElement,
			touchHandler: {
				onTouchBegin: this.onTouchBegin.bind(this),
				onTouchMove: this.onTouchMove.bind(this),
				onTouchEnd: this.onTouchEnd.bind(this),
			},
			touchEnabled,
		});

		setupRenderContext({
			canvas: canvasElement,
			designWidth,
			designHeight,
			scaleMode,
			modifyCanvasSize,
		});
	}

	/**
	 * 开始引擎
	 */
	start() {
		this.root.enabled = true;

		this.tsStart = Date.now();
		this.startTick();
	}

	/**
	 * 暂停引擎
	 */
	pause() {
		this.root.enabled = false;

		this.stopTick();
	}

	/**
	 * 获取节点路径
	 * @param entity
	 */
	getEntityPath(entity?: Entity): string {
		let path = '';
		let current = entity || this.root;
		while (current.parent) {
			path = current.parent.children.indexOf(current) + (path.length > 0 ? '|' : '') + path;
			current = current.parent;
		}
		return path;
	}

	/**
	 * 根据节点路径获取节点
	 * @param path
	 */
	getEntityByPath(path?: string): Entity {
		let target = this.root;

		if (path.length > 0) {
			let arr = path.split('|');
			for (let item of arr) {
				target = target.children[item];
				if (!target) {
					target = null;
					break;
				}
			}
		}
		return target;
	}

	/**
	 * 获取当前帧率
	 */
	get fps() {
		return this.lastFPS;
	}

	/**
	 * 开始时钟
	 */
	private startTick() {
		this._flush = 60 / this.options.fps - 1 >> 0;
		if (this._flush < 0) {
			this._flush = 0;
		}

		requestAnimationFrame(this.flush);
	}

	/**
	 * 停止时钟
	 */
	private stopTick() {

	}

	/**
	 * 时钟触发
	 */
	private flush(tsNow): void {
		if (this._flush == 0) {
			this.onFrameTick(tsNow);
		} else {
			if (this._currentFlush == 0) {
				this.onFrameTick(tsNow);
				this._currentFlush = this._flush;
			} else {
				this._currentFlush--;
			}
		}

		requestAnimationFrame(this.flush);
	}

	nextTicks = [];

	nextTick(func, tickCount = 1) {
		this.nextTicks.push({func, tickCount});
	}

	private onFrameTick(tsNow) {
		clear();
		this.lastFPS = Math.floor(1000 / (tsNow - this.tsLast));
		this.tsLast = tsNow;
		const ts = tsNow - this.tsStart;
		traverse(this.root, function (child) {
			if (!child.isFree && child.enabled) {
				child.onUpdate(ts);
			} else {
				return true;
			}
		}, -1, true, function (current) {
			current.afterUpdate();
		});
		//const tsPass = Date.now() - tsNow;

		for (let i = 0, li = this.nextTicks.length; i < li; i++) {
			const item = this.nextTicks[i];

			item.tickCount--;
			if (item.tickCount <= 0) {
				item.func(ts);

				this.nextTicks.splice(i, 1);
				i--;
				li--;
			}
		}
	}

	/**
	 * 代理出来的onTouchBegin方法
	 * @param event
	 */
	private onTouchBegin(event) {
		traversePostorder(this.root, function (child) {
			return child.onInteract(0, event);
		})
	}

	/**
	 * 代理出来的onTouchMove方法
	 * @param event
	 */
	private onTouchMove(event) {
		traversePostorder(this.root, function (child) {
			return child.onInteract(1, event);
		})
	}

	/**
	 * 代理出来的onTouchEnd方法
	 * @param event
	 */
	private onTouchEnd(event) {
		traversePostorder(this.root, function (child) {
			return child.onInteract(2, event);
		})
	}
}
