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

import HashObject from "./HashObject";
import {ScillaComponent} from "./ScillaComponent";
import {EngineConfig} from "../engine-config";

/**
 * 节点遍历(先序遍历)
 * @param target 目标节点
 * @param hitChild 遇到子节点回调
 * @param level 深度，默认全部遍历
 * @param includeSelf 是否包括自身
 * @param fullCallback 子节点遍历完后回调
 * @param params 其他参数
 */
export function traverse(target: Entity, hitChild: (child: Entity, ...params) => boolean, level = -1, includeSelf = false, fullCallback?: (current: Entity) => void, ...params) {
	let interrupt;
	if (includeSelf) {
		hitChild(target, ...params);
	}

	if (level !== 0) {
		for (let child of target.children) {
			if (hitChild(child, ...params)) {
				interrupt = true;
				continue;
			}
			if(child.children.length > 0){
				traverse(child, hitChild, level - 1, false, fullCallback, ...params);
			}
		}
	}

	!interrupt && fullCallback && fullCallback(target);
}

/**
 * 节点遍历(后序遍历且倒序)
 * @param target 目标节点
 * @param hitChild 遇到子节点回调
 * @param level 深度，默认全部遍历
 * @param includeSelf 是否包括自身
 * @param fullCallback 子节点遍历完后回调
 * @param params 其他参数
 */
export function traversePostorder(target: Entity, hitChild: (child: Entity, ...params) => boolean, level = -1, includeSelf = false, fullCallback?: (current: Entity) => void, ...params) {
	let interrupt;

	if (level !== 0) {
		for (let i = target.children.length - 1; i >= 0; i--) {
			const child = target.children[i];

			if(traversePostorder(child, hitChild, level - 1, false, fullCallback, ...params)){
				return true;
			}
			if (hitChild(child, ...params)) {
				return true;
			}
		}
	}

	if (includeSelf) {
		hitChild(target, ...params);
	}

	!interrupt && fullCallback && fullCallback(target);
}

/**
 * 节点冒泡
 * @param target 目标节点
 * @param hitParent 遇到父节点回调
 * @param includeSelf 是否包括自身
 * @param params 其他参数
 */
export function bubbling(target: Entity, hitParent: (parent: Entity, ...params) => boolean, includeSelf = false, ...params) {
	if (includeSelf) {
		hitParent(target, ...params);
	}
	let entity = target;
	while (entity = entity.parent) {
		if (hitParent(entity, ...params)) {
			break;
		}
	}
}

/**
 * 实体类
 * 实体类只是单纯的父子节点逻辑，不含有其他逻辑
 */
export class Entity extends HashObject {
	name: string = 'Entity';
	protected _uuid: string;

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

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

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

	get uuid(): string {
		return this._uuid;
	}

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

	set enabled(value: boolean) {
		if (this._enabled !== value) {
			this._enabled = value;

			traverse(this, function (child: Entity) {
				child._invokeEnabledState(value);
				return false;
			}, -1, true)
		}
	}

	_invokeEnabledState(enabled) {
		if (this._enabled && enabled) {
			this.onEnable();
		} else if (!this._enabled && !enabled) {
			this.onDisable();
		}
	}

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

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

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

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

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

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

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

	//----- tree

	/**
	 * 获取父节点
	 */
	get parent() {
		return this._parent;
	}

	/**
	 * 是否含有子节点
	 * @param child
	 */
	containsChild(child: Entity) {
		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) {
			this.removeChildAt(index);
		}
	}

	/**
	 * 移除指定索引的节点
	 * @param index
	 */
	removeChildAt(index) {
		const child = this._children[index];
		this._onChildRemoved(child);

		this._children.splice(index, 1);
	}

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

	/**
	 * 获取指定索引的节点
	 * @param index
	 */
	getChildByIndex(index) {
		return this._children[index];
	}

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

	/**
	 * 获取所有子节点
	 */
	get children() {
		return this._children;
	}

	//----- component

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

	/**
	 * 在指定索引增加组件，重复添加可作为顺序交换
	 * @param component
	 * @param index
	 */
	addComponentAt(component: ScillaComponent, index) {
		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: ScillaComponent) {
		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 clazz
	 */
	getComponents(clazz: any): any[] {
		return this._components.filter((component: any) => {
			return typeof clazz === 'string' ? component.constructor.__class__ === clazz : component instanceof clazz;
		});
	}

	/**
	 * 获取指定类的组件
	 * @param clazz
	 */
	getComponent(clazz: any) {
		return this.getComponents(clazz)[0];
	}

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

	//----- lifecycle

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

	/**
	 * 当组件生效时
	 */
	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.forEachComponent(comp => {
			if (comp.enabled) {
				return comp.$onUpdate(t);
			}
		});
	}

	/**
	 * 当子节点遍历结束后
	 */
	afterUpdate() {
		this.forEachComponent(comp => {
			if(comp.enabled){
				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: ScillaComponent) {
		component._setup(this);
		if (EngineConfig.awakeComponentWhenAdded) {
			this.awakeComponent(component);
		}
	}

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

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

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

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

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

	/**
	 * 调用实体上的组件的方法
	 * @param hitEntity 遇到的实体
	 * @param method 方法名
	 * @param params 参数
	 */
	private invokeOnEntity = (hitEntity: Entity, method, ...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;
		}
	};
}
