import { Utils } from '../common/Utils';
import { BaseComponent } from './BaseComponent';

const __ = {
	private: Symbol('private'),
}

/**
 * @class BlueprintComponent
 * 蓝图组件。
 * @memberof THING
 * @extends THING.BaseComponent
 * @public
 */
class BlueprintComponent extends BaseComponent {

	static mustCopyWithInstance = true;

	/**
	 * 加载蓝图资源并运行。
	 */
	constructor() {
		super();

		this[__.private] = {};
		let _private = this[__.private];

		_private.pendingPromise = null;
		_private.resources = [];
		// _private.childBlueprints = [];

		_private.debugger = null;
		_private.disposed = false;
	}

	onRemove() {
		let _private = this[__.private];

		_private.resources.forEach(resource => {
			resource.dispose();
		});
		_private.resources.length = 0;

		if (_private.debugger) {
			_private.debugger.dispose();
			_private.debugger = null;
		}

		_private.pendingPromise = null;
		_private.disposed = true;

		super.onRemove();
	}

	// #region Private

	// #endregion

	/**
	 * @typedef {object} BlueprintComponentLoadArgs
	 * @property {string|Array<string>} url The resource URL(s).
	 * @property {object|Array<object>} data The json data(s).
	 */

	/**
	 * 从URL或数据加载。
	 * @param {BlueprintComponentLoadArgs} options 选项。
	 * @returns {THING.BlueprintComponent}
	 * @public
	 * @example
	 * let object = new THING.Object3D();
	 * let blueprints = await object.blueprint.load({ url: './blueprints/myBP.json' });
	 * // @expect(blueprints.length == 1)
	 */
	load(options = {}) {
		let _private = this[__.private];

		let url = options['url'];
		let data = options['data'] || [];
		let path = options['path'] || null;
		let workPath = options['workPath'] || '';

		let resourceManager = this.app.resourceManager;

		if (_private.disposed) {
			console.warn('BlueprintComponent is disposing or disposed, cannot load blueprint.');
			return null;
		}

		_private.pendingPromise = new Promise((resolve, reject) => {
			let blueprints = [];
			// Get data through url when has url(can use cache in async mode).
			if (url) {
				if (Utils.isString(url)) {
					url = [url];
				}

				url.forEachAsync((_url) => {
					return resourceManager.loadBlueprintAsync(_url).then(datas => {
						datas.forEach(data => {
							let blueprint = this._loadBlueprintData(data, _url._getPath(), workPath);
							blueprint && blueprints.push(blueprint);
						});
					}, (err) => {
						reject(err);
					})
				}).then(() => {
					resolve(blueprints);
				});
			}
			// Get data through url(in sync mode).
			else if (data) {
				if (data.info) {
					let bodyData = resourceManager.getBlueprintBodyData(data);

					// Load blueprint resource(s)
					bodyData.forEach(body => {
						let blueprint = this._loadBlueprintData(body, path, workPath);
						blueprint && blueprints.push(blueprint);
					});
				}
				else {
					let blueprint = this._loadBlueprintData(data, path, workPath);
					blueprint && blueprints.push(blueprint);
				}
				resolve(blueprints);
			}
		});

		return _private.pendingPromise;
	}

	_loadBlueprintData(data, path, workPath) {
		let _private = this[__.private];
		if (_private.disposed) {
			return null;
		}
		let resources = _private.resources;

		let blueprint = Utils.createObject('Blueprint', { app: this.app, object: this.object, path, workPath });
		resources.push(blueprint);

		blueprint.load(data);
		return blueprint;
	}

	/**
	 * 运行所有蓝图
	 * @param options
	 * @public
	 */
	run(options = {}) {
		let _private = this[__.private];
		if (_private.pendingPromise) {
			_private.pendingPromise.then(() => {
				_private.resources.forEach(resource => {
					resource.getRunner().start(null, options);
				});
			}, (err) => {
				Utils.error(err)
			});
		}
	}

	/**
	 * 清除所有蓝图。
	 * @public
	 */
	clear() {
		let _private = this[__.private];
		_private.resources.forEach(resource => {
			resource.dispose();
		});
		_private.resources = []
	}

	/**
	 * 结束所有蓝图。
	 * @public
	 */
	stop() {
		let _private = this[__.private];
		_private.resources.forEach(resource => {
			resource.getRunner().stop();
		});
	}

	/**
	 * 暂停所有蓝图。
	 * @public
	 */
	pause() {
		let _private = this[__.private];
		if (_private.pendingPromise) {
			_private.pendingPromise.then(() => {
				_private.resources.forEach(resource => {
					resource.getRunner().pause();
				});
			});
		}
	}

	/**
	 * 恢复所有蓝图。
	 * @public
	 */
	resume() {
		let _private = this[__.private];
		if (_private.pendingPromise) {
			_private.pendingPromise.then(() => {
				_private.resources.forEach(resource => {
					resource.getRunner().resume();
				});
			});
		}
	}

	/**
	 * 根据名称设置变量。
	 * @param {string} name 变量名称。
	 * @param {*} value 变量值。
	 * @public
	 */
	setVar(name, value) {
		let _private = this[__.private];
		if (_private.pendingPromise) {
			_private.pendingPromise.then(() => {
				_private.resources.forEach(resource => {
					resource.setVar(name, value);
				});
			});
		}
	}

	/**
	 * 根据名称获取变量。变量存在则返回对应的变量,不存在则返回null。(如果有多蓝图,则返回第一个相等的属性)
	 * @param {string} name 变量名称。
	 * @returns {Promise}
	 * @public
	 */
	async getVar(name) {
		let _private = this[__.private];
		if (_private.pendingPromise) {
			await _private.pendingPromise;
			for (var i = 0; i < _private.resources.length; i++) {
				if (_private.resources[i].hasVar(name)) {
					var value = _private.resources[i].getVar(name);
					return value;
				}
			}
			return null;
		}
		else {
			return null;
		}
	}

	/**
	 * 设置变量。
	 * @param {object} value 变量值。
	 * @public
	 */
	setVars(value) {
		let _private = this[__.private];
		if (_private.pendingPromise) {
			_private.pendingPromise.then(() => {
				_private.resources.forEach(resource => {
					resource.setVars(value);
				});
			});
		}
	}


	/**
	 * 获取蓝图所有变量。
	 * @returns {Promise<object>}
	 * @public
	 */
	async getVars() {
		let _private = this[__.private];
		if (_private.pendingPromise) {
			await _private.pendingPromise;
			if (_private.resources && _private.resources.length) {
				var value = _private.resources[0].getVars();
				return value;
			}
			else {
				return {};
			}
		}
		else {
			return {};
		}
	}

	/**
	 * 注册事件
	 * @param {string} type 事件类型。
	 * @param {Function} callback 回调函数。
	 * @public
	 */
	addEventListener(type, callback) {
		let _private = this[__.private];
		if (_private.pendingPromise) {
			_private.pendingPromise.then(() => {
				_private.resources.forEach(resource => {
					resource.addEventListener(type, callback);
				});
			});
		}
	}

	/**
	 * 取消注册事件。
	 * @param {string} type 事件类型。
	 * @param {Function} callback 回调函数。
	 * @public
	 */
	removeEventListener(type, callback) {
		let _private = this[__.private];
		if (_private.pendingPromise) {
			_private.pendingPromise.then(() => {
				_private.resources.forEach(resource => {
					resource.removeEventListener(type, callback);
				});
			});
		}
	}

	/**
	 * 触发事件
	 * @param {string} type 事件类型。
	 * @param {object} event 事件信息。
	 * @public
	 */
	triggerEvent(type, event) {
		let _private = this[__.private];
		if (_private.pendingPromise) {
			_private.pendingPromise.then(() => {
				_private.resources.forEach(resource => {
					resource.triggerEvent(type, event);
				});
			});
		}
	}

	getDebugger() {
		let _private = this[__.private];

		_private.debugger = _private.debugger || Utils.createObject('BlueprintDebugger', { app: this.app });

		_private.resources.forEach(resource => {
			resource.setDebugger(_private.debugger);
		});

		return _private.debugger;
	}

	// #region ChildBlueprint
	/**
	 * 运行子图
	 * @param {string} url 子图路径
	 * @param {object} params
	 * @returns {Promise}
	 * @public
	 */
	async runChildBlueprintFile(url, params = {}) {
		let _private = this[__.private];
		if (_private.disposed) {
			console.warn('BlueprintComponent is disposing or disposed, cannot load blueprint.');
			return null;
		}

		let resourceManager = this.app.resourceManager;
		let datas = await resourceManager.loadBlueprintAsync(url);
		return await this.runChildBlueprintData(datas[0].blueprints[0], params);
	}

	async runChildBlueprintData(data, params = {}) {
		let _private = this[__.private];
		if (_private.disposed) {
			console.warn('BlueprintComponent is disposing or disposed, cannot load blueprint.');
			return null;
		}
		if (data) {
			let blueprint = Utils.createObject('Blueprint', { app: this.app, object: this.object });
			if (data) {
				blueprint.load(data);
				let result = await this.runChildBlueprint(blueprint, params);
				return {
					result,	// 执行结果
					blueprint // 蓝图实例
				};
			}
		}
	}

	mapInputParam(node, param) {
		let inputParamMap = node.getData().paramKeys;
		let realParam = {};
		for (let key in inputParamMap) {
			let value = param[key];
			realParam[inputParamMap[key]] = value;
		}
		return realParam;
	}

	mapOutputParam(node, param) {
		let outputParamMap = node.getData().paramKeys;
		let realParam = {};
		for (let key in outputParamMap) {
			let value = outputParamMap[key];
			realParam[key] = param[value];
		}
		return realParam;
	}

	/**
	 * 运行子蓝图
	 * @param {object} name  子蓝图
	 * @param blueprint
	 * @param {object} param 子蓝图的参数
	 * @private
	 */
	async runChildBlueprint(blueprint, param) {
		let bp = blueprint;
		let startNode = bp.getNode("BPChildBlueprintStart");
		let endNode = bp.getNode("BPChildBlueprintEnd");
		if (!startNode || !endNode) {
			console.error('startNode or endNode is not found');
			return null;
		}
		let resolve = null;
		let promise = new Promise((res, rej) => {
			resolve = res;
		})
		const eventID = 'OnExecuteBPChildBlueprintEnd'
		function callback(event) {
			resolve && resolve(event.outputs);
			resolve = null;
			bp.removeEventListener(eventID, callback);
			// 销毁的权利留给外边,因为销毁的话,有的蓝图节点会一并销毁自己创建的东西
			// bp.dispose();
		}
		bp.addEventListener(eventID, callback);
		// bp.getRunner().start(this.mapInputParam(startNode, param), { node: startNode, execName: "exec" });
		// TODO:调用了_run私有函数, 因为如果使用start方法,因为startNode没有连接输入,会忽略掉这里传入的参数
		let rootRunner = bp.getRunner();
		rootRunner._run(startNode, this.mapInputParam(startNode, param), {}, "exec");
		let outputs = await promise;
		let result = this.mapOutputParam(endNode, outputs);
		return result;
	}
	// #endregion

}

export { BlueprintComponent }