/**
 * Created by rockyl on 2019-07-03.
 *
 * 实体
 */

import {Component} from "./Component";
import {EngineConfig} from "../engine-config";
import {ScillaEngine} from "./ScillaEngine";
import {bubbling, traverse} from "./utils";
import {EventDispatcher} from "./EventDispatcher";

/**
 * 实体类
 */
export class Entity extends EventDispatcher {
	name: string = 'Entity';
	protected _uuid: string;

	//是否游离状态
	protected _isFree: boolean = true;
	//是否有效
	protected _enabled: boolean = EngineConfig.entityEnabled;
	protected _parent: Entity = null;
	protected _children: Entity[] = [];
	protected _components: Component[] = [];

	constructor(name?: string, uuid?: string) {
		super();

		if (name) {
			this.name = name;
		}
		if (uuid) {
			this._uuid = uuid;
		}
	}

	/**
	 * 获取唯一ID
	 */
	get uuid(): string {
		return this._uuid;
	}

	/**
	 * 是否有效状态
	 */
	get enabled(): boolean {
		return this._enabled;
	}

	set enabled(value: boolean) {
		if (this._enabled !== value) {
			this._enabled = value;
			let that = this;
			traverse(this, function (child: Entity) {
				if (child !== that && !child._enabled) {
					return true;
				}

				child._invokeEnabledState(value);
				return false;
			}, -1, true)
		}
	}

	_invokeEnabledState(enabled: boolean) {
		if (this._enabled) {
			if (enabled) {
				this.onEnable();
			} else {
				this.onDisable();
			}
		}
	}

	get isParentActive(): boolean {
		return this._parent && this._parent.enabled && !this._parent.isFree;
	}

	get isActive(): boolean {
		return this.isParentActive && this._enabled;
	}

	/**
	 * 是否游离状态
	 */
	get isFree(): boolean {
		return this._isFree;
	}

	/**
	 * 使游离
	 */
	_free() {
		this._isFree = true;

		traverse(this, function (child: Entity) {
			if (child.isParentActive) {
				child._invokeEnabledState(false);
			}
			child._free();
			return false;
		}, 1)
	}

	/**
	 * 使约束
	 */
	_restrict() {
		this._isFree = false;

		traverse(this, function (child: Entity) {
			if (child.isParentActive) {
				child._invokeEnabledState(true);
			}
			child._restrict();
			return false;
		}, 1)
	}

	//----- tree

	/**
	 * 获取父实体
	 */
	get parent(): Entity {
		return this._parent;
	}

	/**
	 * 获取根实体
	 */
	get root(): RootEntity {
		let p: any = this;

		do {
			if (p instanceof RootEntity) {
				return p;
			}
		}
		while (p = p.parent)
	}

	/**
	 * 是否含有子实体
	 * @param child
	 */
	containsChild(child: Entity): boolean {
		return this.getChildIndex(child) >= 0;
	}

	/**
	 * 添加子实体时
	 * @param child
	 * @private
	 */
	protected _onChildAdded(child: Entity) {
		child._parent = this;

		if (!this._isFree && child._isFree) {
			if (child.isParentActive) {
				child._invokeEnabledState(true);
			}
			child._restrict();
		}
	}

	/**
	 * 子实体被移除时
	 * @param child
	 * @private
	 */
	_onChildRemoved(child: Entity) {
		child._parent = null;

		if (!this._isFree && !child._isFree) {
			if (child.isActive) {
				child._invokeEnabledState(false);
			}
			child._free();
		}
	}

	/**
	 * 添加子实体，重复添加则会加到最后
	 * @param child
	 */
	addChild(child: Entity) {
		this.addChildAt(child, this._children.length);
	}

	/**
	 * 添加子实体到指定索引，重复添加可作为顺序交换
	 * @param child
	 * @param index
	 */
	addChildAt(child: Entity, index) {
		if (child.parent && child.parent !== this) {
			child.parent.removeChild(child);
		}

		const currentIndex = this.getChildIndex(child);

		if (index < 0 || currentIndex == index) {  //如果索引小于0或没变化
			return;
		}

		index = Math.min(this._children.length, index);
		if (currentIndex >= 0 || index < this._children.length) {
			if (currentIndex >= 0) {
				this._children.splice(currentIndex, 1);
			}
			this._children.splice(index, 0, child);
		} else {
			this._children.push(child);
		}
		this._onChildAdded(child);
	}

	/**
	 * 移除实体
	 * @param child
	 */
	removeChild(child: Entity) {
		const index = this.getChildIndex(child);
		if (index >= 0) {
			return this.removeChildAt(index);
		}
	}

	/**
	 * 移除指定索引的实体
	 * @param index
	 */
	removeChildAt(index: number) {
		const child = this._children[index];

		if (child) {
			this._onChildRemoved(child);
			this._children.splice(index, 1);

			return child;
		}
	}

	/**
	 * 根据实体获取索引
	 * @param child
	 */
	getChildIndex(child: Entity): number {
		return this._children.indexOf(child)
	}

	/**
	 * 获取指定索引的实体
	 * @param index
	 */
	getChildAt(index): Entity {
		return this._children[index];
	}

	/**
	 * 获取指定名字的实体
	 * @param name
	 */
	getChildrenByName(name): Entity[] {
		let children = [];
		for (let child of this._children) {
			if (child.name === name) {
				children.push(child);
			}
		}

		return children;
	}

	/**
	 * 移除所有子实体
	 */
	removeChildren() {
		while (this._children.length > 0) {
			this.removeChildAt(0);
		}
	}

	/**
	 * 获取所有子实体
	 */
	get children(): Entity[] {
		return this._children;
	}

	//----- component

	/**
	 * 增加组件
	 * @param component
	 */
	addComponent(component: Component) {
		this.onAddComponent(component);
		this._components.push(component);
	}

	/**
	 * 在指定索引增加组件，重复添加可作为顺序交换
	 * @param component
	 * @param index
	 */
	addComponentAt(component: Component, index: number) {
		const currentIndex = this._components.indexOf(component);
		if (currentIndex == index) {
			return;
		}

		if (currentIndex >= 0) {
			this._components.splice(currentIndex, 1);
		}
		this._components.splice(index, 0, component);
		this.onAddComponent(component);
	}

	/**
	 * 移除组件
	 * @param component
	 */
	removeComponent(component: Component) {
		this.onRemoveComponent(component);
		const index = this._components.indexOf(component);
		if (index >= 0) {
			this._components.splice(index, 1);
		}
	}

	/**
	 * 移除所有组件
	 */
	removeAllComponents() {
		while (this._components.length > 0) {
			this.removeComponent(this._components[0]);
		}
	}

	/**
	 * 根据组件名称获取指定类的组件列表
	 * @param name
	 */
	getComponentsByName<T extends Component>(name: string): T[] {
		return <T[]>this._components.filter((value: Component) => {
			return value.constructor['__class__'] === name;
		});
	}

	/**
	 * 获取指定类的组件列表
	 * @param clazz
	 */
	getComponents<T extends Component>(clazz: new() => T): T[] {
		return <T[]>this._components.filter((component: Component) => {
			return component instanceof clazz;
		});
	}

	/**
	 * 获取指定类的组件
	 * @param name
	 */
	getComponentByName<T extends Component>(name: string): T {
		return this.getComponentsByName<T>(name)[0];
	}

	/**
	 * 获取指定类的组件
	 * @param clazz
	 */
	getComponent<T extends Component>(clazz: new() => T): T {
		return this.getComponents<T>(clazz)[0];
	}

	/**
	 * 获取所有组件
	 */
	get components(): Component[] {
		return this._components;
	}

	//----- lifecycle

	/**
	 * 遍历所有组件
	 * @param func
	 */
	forEachComponent(func: (component) => boolean) {
		for (let component of this._components) {
			if (func(component)) {
				break;
			}
		}
	}

	active() {
		this.forEachComponent(comp => {
			if (comp.enabled) {
				return comp.onCreate();
			}
		});
	}

	/**
	 * 当组件生效时
	 */
	onEnable() {
		this.forEachComponent(comp => {
			if (comp.enabled) {
				return comp.$onAwake();
			}
		});
	}

	/**
	 * 当组件失效时
	 */
	onDisable() {
		this.forEachComponent(comp => {
			if (comp.enabled) {
				return comp.$onSleep();
			}
		});
	}

	/**
	 * 当时钟更新时
	 * @param t
	 */
	$onUpdate(t) {
		this.preUpdate(t);
		this.updateComponents(t);
		this.onUpdate(t);
	}

	/**
	 * 预更新
	 * @param t
	 */
	protected preUpdate(t) {

	}

	/**
	 * 组件更新
	 * @param t
	 */
	protected updateComponents(t) {
		this.forEachComponent(comp => {
			if (comp.enabled) {
				if (EngineConfig.editorMode) {
					return comp.$onEditorUpdate(t);
				} else {
					return comp.$onUpdate(t);
				}
			}
		});
	}

	/**
	 * 更新
	 * @param t
	 */
	protected onUpdate(t) {

	}

	/**
	 * 当子实体遍历结束后
	 */
	$afterUpdate() {
		this.forEachComponent(comp => {
			if (comp.enabled) {
				if (EngineConfig.editorMode) {
					return comp.$afterEditorUpdate();
				} else {
					return comp.$afterUpdate();
				}
			}
		});
	}

	/**
	 * 当交互时
	 * @param type
	 * @param event
	 */
	onInteract(type, event) {
		if (!this.isFree && this.enabled) {
			let interrupt = false;
			this.forEachComponent(comp => {
				if (comp.enabled && comp.interactable) {
					const r = comp.onInteract(type, event);
					if (r) {
						interrupt = true;
					}
					return false;
				}
			});
			return interrupt;
		} else {
			return false;
		}
	}

	/**
	 * 当添加组件时
	 * @param component
	 */
	onAddComponent(component: Component) {
		component._setup(this);
		if (EngineConfig.awakeComponentWhenAdded) {
			this.awakeComponent(component);
		}
	}

	/**
	 * 唤醒组件
	 * @param component
	 */
	awakeComponent(component) {
		if (!this._isFree && this._enabled) {
			component.$onAwake();
		}
	}

	/**
	 * 当移除组件时
	 * @param component
	 */
	onRemoveComponent(component: Component) {
		if (EngineConfig.sleepComponentWhenRemoved) {
			this.sleepComponent(component);
		}
		component._unSetup();
	}

	/**
	 * 睡眠组件
	 * @param component
	 */
	sleepComponent(component: Component) {
		if (!this._isFree && this._enabled) {
			component.$onSleep();
		}
	}

	/**
	 * 向下广播
	 * 如果某组件调用后返回true，将结束整条链
	 * @param method 方法名
	 * @param level 深度，默认全部遍历
	 * @param params 参数
	 */
	broadcast(method: string, level = -1, ...params) {
		traverse(this, this.invokeOnEntity, level, true, null, method, ...params)
	}

	/**
	 * 向上冒泡
	 * 如果某组件调用后返回true，将结束整条链
	 * @param method 方法名
	 * @param params 参数
	 */
	bubbling(method: string, ...params) {
		bubbling(this, this.invokeOnEntity, false, method, ...params);
	}

	/**
	 * 调用实体上的组件的方法
	 * @param hitEntity 遇到的实体
	 * @param method 方法名
	 * @param params 参数
	 */
	private invokeOnEntity = (hitEntity: Entity, method: string, ...params): boolean => {
		let hitBreak = false;
		hitEntity.forEachComponent(comp => {
			const m = comp[method];
			if (m) {
				const result = m.apply(comp, params);
				if (result) {
					hitBreak = true;
				}
			}
			return false;
		});
		if (hitBreak) {
			return true;
		}
	};
}

/**
 * 根实体类
 */
export class RootEntity extends Entity {
	protected _engine: ScillaEngine;

	constructor(engine: ScillaEngine) {
		super();

		this.name = 'root';
		this._engine = engine;
	}

	/**
	 * 获取引擎，只有活动的root实体才有这个属性
	 */
	get engine() {
		return this._engine;
	}
}
