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

import { MathUtils } from '../math/MathUtils';

import { NodeObject } from './NodeObject';

import { Style } from '../resources/Style';

import { StyleModifier } from '../resources/StyleModifier';



const __ = {

	private: Symbol('private'),

}



let _vec3 = MathUtils.createVec3();

let _scale = MathUtils.createVec3();

let _quat = MathUtils.createQuat();

let _mat4 = MathUtils.createMat4();

let _identity = MathUtils.createMat4();

let _axis = [0, 0, 0];



const _defaultOptions = {};



/**
 * BodyObject - 自身对象
 * 
 * 负责管理对象的可渲染节点及变换。
 * 
 * @class BodyObject
 * @memberof THING
 * @public
 */
class BodyObject {



	/**

	 * 自身对象类,负责管理对象的可渲染节点及变换

	 */

	constructor() {

		this[__.private] = {};

		let _private = this[__.private];



		_private.app = null;



		_private.node = null;

		_private.object = null;



		_private.pivotNode = null;

		_private.rootNode = null;

		_private.renderableNode = null;



		_private.style = null;

		_private.sharedStyle = null;

	}



	// #region Private



	_createParentNode(name, node, isKeepTransform = true) {

		const _private = this[__.private];

		// Create parent node

		let parentNode = Utils.createObject('Node', { app: _private.app });

		parentNode.setName(name);



		parentNode.setAttribute('MatrixUpdateMode', node.getAttribute('MatrixUpdateMode'));



		// Link to root node

		if (node.getParent()) {

			node.getParent().add(parentNode);

		}



		// Keep transform

		if (isKeepTransform) {

			parentNode.setMatrix(node.getMatrix(_mat4));

			node.setMatrix(_identity);

		}



		parentNode.add(node);



		// Copy user data

		let toUserData = parentNode.getUserData();

		let fromUserData = node.getUserData();

		for (let key in fromUserData) {

			toUserData[key] = fromUserData[key];

		}



		return parentNode;

	}



	_updateNode() {

		let _private = this[__.private];



		_private.node = _private.rootNode || _private.pivotNode || _private.renderableNode;

	}



	_updateNodeOptions(options = _defaultOptions) {

		let _private = this[__.private];



		let renderableNode = _private.renderableNode;



		// Set the local scale

		let localScale = options['localScale'];

		if (localScale) {

			if (localScale[0] != 1 || localScale[1] != 1 || localScale[2] != 1) {

				this.createRootNode();



				MathUtils.vec3.multiply(_vec3, renderableNode.getScale(_vec3), localScale);

				renderableNode.setScale(_vec3);

			}

		}



		// Set the local position(offset)

		let localPosition = options['localPosition'];

		if (localPosition) {

			if (localPosition[0] != 0 || localPosition[1] != 0 || localPosition[2] != 0) {

				this.createRootNode();



				MathUtils.vec3.add(_vec3, renderableNode.getPosition(_vec3), localPosition)

				renderableNode.setPosition(_vec3);

			}

		}



		// Set the local angles

		let localAngles = options['localAngles'];

		if (localAngles) {

			if (localAngles[0] != 0 || localAngles[1] != 0 || localAngles[2] != 0) {

				this.createRootNode();



				MathUtils.quat.multiply(_quat, renderableNode.getQuaternion(_quat), MathUtils.getQuatFromAngles(localAngles));

				renderableNode.setQuaternion(_quat);

			}

		}

	}



	_getNodeByName(name, rootNode, filter, complete) {

		let _private = this[__.private];



		let nodes = this._getSubNodes(_private.renderableNode, rootNode);



		for (let i = 0; i < nodes.length; i++) {

			let node = nodes[i];



			if (filter) {

				if (!filter(node)) {

					continue;

				}

			}



			if (node.name == name) {

				if (complete) {

					return complete(node);

				}

				else {

					return node;

				}

			}

		}



		return null;

	}



	// #endregion



	// #region BaseComponent



	dispose() {

		let _private = this[__.private];



		// Unbind sub node for renderable node

		let renderableNode = this.getRenderableNode();

		if (renderableNode) {

			renderableNode.unbindSubNode();

		}



		// Unapply current style

		if (_private.style) {

			_private.style.dispose();

			_private.style = null;

		}



		if (_private.rootNode) {

			_private.rootNode.dispose();

		}

		else if (_private.pivotNode) {

			_private.pivotNode.dispose();

		}

		else if (_private.renderableNode) {

			_private.renderableNode.dispose();

		}

		if (_private.sharedStyle) {

			_private.sharedStyle.release();

			_private.sharedStyle = null;

		}

		_private.rootNode = null;

		_private.pivotNode = null;

		_private.renderableNode = null;



		_private.node = null;

		_private.object = null;

	}



	// #endregion



	init(object, options = _defaultOptions) {

		let _private = this[__.private];



		_private.object = object;

		_private.app = object.app;



		let node = options['node'];

		let rootNode = options['rootNode'];

		let renderableNode = options['renderableNode'];

		let nodeType = options['nodeType'] || 'Node';



		if (rootNode || renderableNode) {

			_private.rootNode = rootNode;

			_private.renderableNode = renderableNode || rootNode;

		}

		else {

			let app = object.app;



			let nodePool = app.resourceManager.getNodePool();

			_private.renderableNode = nodePool ? nodePool.alloc() : Utils.createObject(nodeType, { app });



			if (node) {

				_private.renderableNode.add(node);

			}

		}



		// Update current node

		this._updateNode();



		// Update node options

		let bodyOptions = options['body'];

		if (bodyOptions) {

			this._updateNodeOptions(bodyOptions);

		}



		// Bind renderable node with object

		let userData = _private.node.getUserData();

		userData['baseObject_orderID'] = _private.object.orderId;

	}



	translateOnAxis(axis, distance) {

		MathUtils.vec3.normalize(_axis, axis);

		MathUtils.vec3.transformQuat(_vec3, _axis, this.localQuaternion);

		MathUtils.vec3.scale(_vec3, _vec3, distance);



		let position = this.localPosition;

		this.localPosition = MathUtils.addVector(position, _vec3);

	}



	translateX(distance) {

		this.translateOnAxis(Utils.xAxis, distance);

	}



	translateY(distance) {

		this.translateOnAxis(Utils.yAxis, distance);

	}



	translateZ(distance) {

		this.translateOnAxis(Utils.zAxis, distance);

	}



	// #region State



	/**

	 * 获取/设置可见状态。

	 * @type {boolean}

	 * @example

	 * let object = new THING.Object3D();

	 * let ret = object.body.visible == true;

	 * // @expect(ret == true)

	 * object.body.visible = false;

	 * ret = object.body.visible == false;

	 * // @expect(ret == true)

	 * @public

	 */

	get visible() {

		let _private = this[__.private];



		return _private.renderableNode.getVisible();

	}

	set visible(value) {

		let _private = this[__.private];



		_private.renderableNode.setVisible(value);

	}



	// #endregion



	// #region Transform



	/**

	 * 获取/设置父空间的本地(偏移)位置。

	 * @type {Array<number>}

	 * @example

	 * let object = new THING.Object3D();

	 * let ret = object.body.localPosition[1] == 0;

	 * // @expect(ret == true)

	 * 	object.body.localPosition = [0, 10, 0];

	 * ret = object.body.localPosition[1] == 10;

	 * // @expect(ret == true)

	 * @public

	 */

	get localPosition() {

		let _private = this[__.private];



		let target = [0, 0, 0];

		return _private.renderableNode.getPosition(target);

	}

	set localPosition(value) {

		let _private = this[__.private];



		this.createRootNode();



		_private.renderableNode.setPosition(value);

	}



	/**

	 * 获取/设置惯性空间的四元数。

	 * @type {Array<number>}

	 * @example

	 * let object = new THING.Object3D();

	 * let ret = object.body.localQuaternion[1] == 0;

	 * // @expect(ret == true)

	 * 	object.body.localQuaternion = [0, 10, 0, 1];

	 * ret = object.body.localQuaternion[1] == 10;

	 * // @expect(ret == true)

	 * 	object.body.localQuaternion = [0, 10, 0, 1];

	 * @public

	 */

	get localQuaternion() {

		let _private = this[__.private];



		let target = [0, 0, 0, 1]

		return _private.renderableNode.getQuaternion(target);

	}

	set localQuaternion(value) {

		let _private = this[__.private];



		this.createRootNode();



		_private.renderableNode.setQuaternion(value);

	}



	get quaternion() {

		let _private = this[__.private];



		let target = [0, 0, 0, 1]

		return _private.renderableNode.getWorldQuaternion(target);

	}

	set quaternion(value) {

		let _private = this[__.private];



		this.createRootNode();



		_private.renderableNode.setWorldQuaternion(value);

	}



	/**

	 * 获取/设置惯性空间的旋转。

	 * 欧拉角('XYZ'),单位为 degree(度)。

	 * @type {Array<number>}

	 * @example

	 * let object = new THING.Object3D();

	 * let ret = object.body.localRotation[1] == 0;

	 * // @expect(ret == true)

	 * 	object.body.localRotation = [0, 10, 0];

	 * ret = object.body.localRotation[1] == 10;

	 * // @expect(ret == true)

	 * @public

	 */

	get localRotation() {

		return this.localAngles;

	}

	set localRotation(value) {

		this.localAngles = value;

	}



	/**

	 * 获取/设置本地角度。

	 * 欧拉角('XYZ'),单位为 degree(度)。

	 * @type {Array<number>}

	 * @public

	 */

	get localAngles() {

		let _private = this[__.private];



		let target = [0, 0, 0, 1]

		return MathUtils.getAnglesFromQuat(_private.renderableNode.getQuaternion(target));

	}

	set localAngles(value) {

		let _private = this[__.private];



		this.createRootNode();



		_private.renderableNode.setQuaternion(MathUtils.getQuatFromAngles(value));

	}



	/**

	 * 获取/设置自身坐标系的缩放。

	 * @type {Array<number>}

	 * @example

	 * let object = new THING.Object3D();

	 * let ret = object.body.localScale[1] == 1;

	 * // @expect(ret == true)

	 * 	object.body.localScale = [10, 10, 10];

	 * ret = object.body.localScale[1] == 10;

	 * // @expect(ret == true)

	 * @public

	 */

	get localScale() {

		let _private = this[__.private];



		let target = [1, 1, 1];

		return _private.renderableNode.getScale(target);

	}

	set localScale(value) {

		let _private = this[__.private];



		this.createRootNode();



		_private.renderableNode.setScale(value);



		// Update privot

		if (_private.pivotNode) {

			_private.object.setPivot(_private.object.pivot)

		}

	}



	/**

	 * 获取/设置世界坐标系的位置。

	 * @type {Array<number>}

	 * @example

	 * let object = new THING.Object3D();

	 * let ret = object.body.position[1] == 0;

	 * // @expect(ret == true)

	 * 	object.body.position = [10, 10, 10];

	 * ret = object.body.position[1] == 10;

	 * // @expect(ret == true)

	 * @public

	 */

	get position() {

		let _private = this[__.private];



		let target = [0, 0, 0];

		return _private.renderableNode.getWorldPosition(target);

	}

	set position(value) {

		let _private = this[__.private];



		this.createRootNode();



		_private.renderableNode.setWorldPosition(value);

	}



	/**

	 * 获取/设置世界空间的旋转。

	 * 欧拉角('XYZ'),单位为 degree(度)。

	 * @type {Array<number>}

	 * @example

	 * let object = new THING.Object3D();

	 * let ret = object.body.rotation[1] == 0;

	 * // @expect(ret == true)

	 * 	object.body.rotation = [10, 10, 10];

	 * ret = object.body.rotation[1] == 10;

	 * // @expect(ret == true)

	 * @public

	 */

	get rotation() {

		return this.angles;

	}

	set rotation(value) {

		this.angles = value;

	}



	/**

	 * 获取/设置世界角度。

	 * 欧拉角('XYZ'),单位为 degree(度)。

	 * @type {Array<number>}

	 * @public

	 */

	get angles() {

		let _private = this[__.private];



		let target = [0, 0, 0, 1]

		return MathUtils.getAnglesFromQuat(_private.renderableNode.getWorldQuaternion(target));

	}

	set angles(value) {

		let _private = this[__.private];



		this.createRootNode();



		_private.renderableNode.setWorldQuaternion(MathUtils.getQuatFromAngles(value));

	}



	/**

	 * 获取/设置世界坐标系的缩放。

	 * @type {Array<number>}

	 * @example

	 * let object = new THING.Object3D();

	 * let ret = object.body.scale[1] == 1;

	 * // @expect(ret == true)

	 * 	object.body.scale = [10, 10, 10];

	 * ret = object.body.scale[1] == 10;

	 * // @expect(ret == true)

	 * @public

	 */

	get scale() {

		let _private = this[__.private];



		let target = [1, 1, 1];

		return _private.renderableNode.getWorldScale(target);

	}

	set scale(value) {

		let _private = this[__.private];



		this.createRootNode();



		_private.renderableNode.setWorldScale(value);



		// Update privot

		if (_private.pivotNode) {

			_private.object.setPivot(_private.object.pivot)

		}

	}



	/**

	 * 获取/设置变换矩阵。

	 * @type {Array<number>}

	 * @example

	 * let object = new THING.Object3D();

	 * let ret = object.body.matrix[12] == 0;

	 * // @expect(ret == true)

	 * 	object.body.matrix = THING.Math.mat4.fromTranslation([], [10, 10, 10]);

	 * ret = object.body.matrix[12] == 10;

	 * // @expect(ret == true)

	 * @public

	 */

	get matrix() {

		let _private = this[__.private];



		return _private.renderableNode.getMatrix();

	}

	set matrix(value) {

		let _private = this[__.private];



		this.createRootNode();



		_private.renderableNode.setMatrix(value);

	}



	/**

	 * 获取/设置世界变换矩阵。

	 * @type {Array<number>}

	 * @example

	 * let object = new THING.Object3D();

	 * let ret = object.body.matrixWorld[12] == 0;

	 * // @expect(ret == true)

	 * 	object.body.matrixWorld = THING.Math.mat4.fromTranslation([], [10, 10, 10]);

	 * ret = object.body.matrixWorld[12] == 10;

	 * // @expect(ret == true)

	 * @public

	 */

	get matrixWorld() {

		let _private = this[__.private];



		return _private.renderableNode.getMatrixWorld();

	}

	set matrixWorld(value) {

		let _private = this[__.private];



		this.createRootNode();



		_private.renderableNode.setMatrixWorld(value);

	}



	// #endregion



	// #region BoundingBox



	/**

	 * BoundingBox对象

	 * @typedef {object} BoundingBoxResult

	 * @property {Array<number>} center 中心点坐标

	 * @property {Array<number>} halfSize 包围盒三个轴向尺寸的一半

	 * @public

	 */



	/**

	 * 获取自身的包围盒

	 * @returns {BoundingBoxResult} 返回包围盒结果

	 * @example

	 * var object = new THING.Object3D();

	 * var boundingBox = object.body.getLocalBoundingBox();

	 * console.log(boundingBox);

	 * @public

	 */

	getLocalBoundingBox() {

		const _private = this[__.private];



		const boundingComponent = _private.object.bounding;

		if (!boundingComponent) {

			return {

				center: [0, 0, 0],

				halfSize: [Number.EPSILON, Number.EPSILON, Number.EPSILON]

			};

		}



		const result = boundingComponent.getSelfBoundingBox();

		return result;

	}

	// #endregion



	// #region Resource



	/**

	 * Create the root node.

	 * @private

	 */

	createRootNode() {

		let _private = this[__.private];



		if (!_private.rootNode) {

			let node = _private.renderableNode;

			if (node.isCamera || node.isLight || node.isDecal) {

				_private.rootNode = node;

			}

			else {

				_private.rootNode = this._createParentNode('root', node);

			}



			this._updateNode();

		}

	}



	/**

	 * Clear pivot node.

	 * @private

	 */

	clearPivotNode() {

		let _private = this[__.private];



		let pivotNode = _private.pivotNode;

		if (!pivotNode) {

			return;

		}



		let parentNode = pivotNode.getParent();

		if (parentNode) {

			let children = pivotNode.getChildren().slice(0);

			children.forEach(child => {

				child.setPosition([0, 0, 0]);



				parentNode.add(child);

			});



			parentNode.remove(pivotNode);

		}



		pivotNode.dispose();



		_private.pivotNode = null;



		this._updateNode();

	}



	/**

	 * Create pivot node.

	 * @private

	 */

	createPivotNode() {

		let _private = this[__.private];



		if (!_private.pivotNode) {

			let node = _private.renderableNode;



			_private.pivotNode = this._createParentNode('pivot', node, false);



			this._updateNode();

		}



		return _private.pivotNode;

	}



	/**

	 * Get the pivot node.

	 * @type {object}

	 * @private

	 */

	getPivotNode() {

		return this[__.private].pivotNode;

	}



	/**

	 * Get the root node.

	 * @type {object}

	 * @private

	 */

	getRootNode() {

		return this[__.private].rootNode;

	}



	/**

	 * Get the renderable node.

	 * @type {object}

	 * @private

	 */

	getRenderableNode() {

		return this[__.private].renderableNode;

	}



	/**

	 * Get the node.

	 * @type {object}

	 * @private

	 */

	getNode() {

		return this[__.private].node;

	}



	/**

	 * Set node.

	 * @param {object} node The node.

	 * @private

	 */

	setNode(node) {

		let _private = this[__.private];



		// Get the renderable node

		let renderableNode = _private.renderableNode;



		// Get the previous node's matrix to sync

		// Becareful: do not use matrix here, due to local angles would be changed by negative scale or some angels value

		renderableNode.getPosition(_vec3);

		renderableNode.getScale(_scale);

		renderableNode.getQuaternion(_quat);



		// Get the current states

		let curStates = {

			parentNode: renderableNode.getParent(),

			name: renderableNode.getName(),

			userData: renderableNode.getUserData()

		};



		// Get the current render node stats

		if (renderableNode.isRenderableNode) {

			if (renderableNode.hasStyle()) {

				curStates.style = renderableNode.getStyle();

			}



			curStates.visible = renderableNode.getVisible();

			curStates.renderOrder = renderableNode.getRenderOrder();

			curStates.castShadow = renderableNode.getCastShadow();

			curStates.receiveShadow = renderableNode.getReceiveShadow();

			curStates.layerMask = renderableNode.getLayerMask();

		}



		let app = _private.object.app;



		// Dispose current node

		let nodePool = app.resourceManager.getNodePool();

		if (nodePool) {

			nodePool.free(renderableNode);

		}

		else {

			renderableNode.dispose();

		}



		// Update node

		renderableNode = node;

		_private.renderableNode = node;



		// Resume matrix

		renderableNode.setPosition(_vec3);

		renderableNode.setScale(_scale);

		renderableNode.setQuaternion(_quat);



		// Relink to parent node

		if (curStates.parentNode) {

			curStates.parentNode.add(renderableNode);

		}



		// Resume the states

		renderableNode.setName(curStates.name);

		renderableNode.setUserData(curStates.userData);

		if (renderableNode.isRenderableNode && this.style.resource != node.getStyle()) {

			if (curStates.style) {

				if (_private.sharedStyle) {

					renderableNode.setStyle(_private.sharedStyle.resource);

				}

				else {

					if (this.style.isChanged) {

						let cloneStyle = curStates.style.clone();

						let oriStyle = renderableNode.getStyle();

						this.style.initResource(oriStyle);

						this.style.updateResource(cloneStyle);

					}

					else {

						let oriStyle = renderableNode.getStyle();

						this.style.initResource(oriStyle);

						this.style.updateResource(oriStyle);

					}

				}

			}



			renderableNode.setVisible(curStates.visible);



			if (renderableNode.getRenderOrder() != curStates.renderOrder) {

				renderableNode.setRenderOrder(curStates.renderOrder);

			}



			if (renderableNode.getCastShadow() != curStates.castShadow) {

				renderableNode.setCastShadow(curStates.castShadow);

			}



			if (renderableNode.getReceiveShadow() != curStates.receiveShadow) {

				renderableNode.setReceiveShadow(curStates.receiveShadow);

			}



			if (renderableNode.getLayerMask() != curStates.layerMask) {

				renderableNode.setLayerMask(curStates.layerMask);

			}

		}



		this._updateNode();

	}



	/**

	 * Just update node.

	 * @param {object} node The node.

	 * @private

	 */

	updateNode(node) {

		let _private = this[__.private];



		_private.renderableNode = node;



		this._updateNode();

	}



	/**

	 * Unload resource.

	 * @param {Function} onCreateBodyNode When create body node callback function.

	 * @private

	 */

	unloadResource(onCreateBodyNode) {

		let _private = this[__.private];



		let renderableNode = _private.renderableNode;

		if (!renderableNode) {

			return;

		}



		// Get the user data and prepare to resume

		let userData = renderableNode.getUserData();



		// Get some useful info and wait to resume

		let parentNode = renderableNode.getParent();

		let visible = renderableNode.getVisible();

		let type = renderableNode.getType();

		let name = renderableNode.getName();

		let matrix = renderableNode.getMatrix(_mat4);



		// Get style

		let style;

		if (renderableNode.isRenderableNode) {

			style = renderableNode.getStyle();

		}



		// Destroy renderable node

		renderableNode.dispose();



		// Recreate renderable node

		renderableNode = onCreateBodyNode(type);

		renderableNode.setName(name);



		// Resume info

		parentNode.add(renderableNode);

		renderableNode.setMatrix(matrix);

		renderableNode.setUserData(userData);

		renderableNode.setVisible(visible);



		if (style) {

			renderableNode.setStyle(style);

		}



		// Update renderable node

		_private.renderableNode = renderableNode;



		// Update node

		this._updateNode();

	}



	getGeometryInfo() {

		return this.getRenderableNode().getGeometryInfo();

	}



	// #endregion



	// #region SubNodes



	/**

	 * @callback TraverseNodesCallback

	 * @param {NodeObject} nodeObject 子节点对象

	 * @public

	 */



	/**

	 * 遍历所有子节点

	 * @param {TraverseNodesCallback} callback 遍历回调函数

	 * @example

	 * object.traverseNodes(node => {

	 * 	console.log(node.name);

	 *  let pos = node.position;

	 *  let angles = node.angles;

	 *  let scale = node.scale;

	 *  console.log(pos, angles, scale);

	 * });

	 */

	traverseNodes(callback) {

		let _private = this[__.private];



		let target = this._getSubNodes(_private.renderableNode, _private.node);



		target.forEach(node => {

			let nodeObject = new NodeObject({

				renderableNode: _private.renderableNode,

				node

			});

			callback(nodeObject);

		});

	}



	/**

	 * 获取所有子节点, 并按节点深度排序

	 * @type {Array<NodeObject>}

	 * @public

	 * @example

	 * let nodes = object.nodes;

	 * nodes.forEach(node => {

	 * 	console.log(node.name);

	 *  let pos = node.position;

	 *  let angles = node.angles;

	 *  let scale = node.scale;

	 *  console.log(pos, angles, scale);

	 * });

	 */

	get nodes() {

		let _private = this[__.private];



		let target = this._getSubNodes(_private.renderableNode, _private.node);



		let subNodes = target.sort(function (a, b) {

			return b.depth - a.depth;

		}).map(node => {

			return new NodeObject({

				renderableNode: _private.renderableNode,

				node

			});

		});



		return subNodes;

	}



	/**

	 * Get the renderable nodes sorted by node depth.

	 * @type {Array<NodeObject>}

	 * @private

	 */

	get renderableNodes() {

		let _private = this[__.private];



		let target = this._getSubNodes(_private.renderableNode, _private.node);



		let subNodes = target.filter(node => {

			return node.isRenderable;

		}).sort(function (a, b) {

			return b.depth - a.depth;

		}).map(node => {

			return new NodeObject({

				renderableNode: _private.renderableNode,

				node

			});

		});



		return subNodes;

	}



	_getSubNodes(renderableNode, rootNode) {

		if (!renderableNode.isRenderableNode) {

			return [];

		}

		let subNodes = [];

		let name = renderableNode.getName();



		renderableNode.getSubNodes(subNodes, rootNode, true);



		return subNodes.filter(subNode => {

			return subNode.name !== name;

		})

	}



	/**

	 * 获取子节点名称的集合,根据节点的层级深度排列

	 * @type {Array<string>}

	 * @public

	 */

	get nodeNames() {

		let _private = this[__.private];



		// Use 'null' to skip matrix calculation

		let subNodes = this._getSubNodes(_private.renderableNode, null);



		let names = subNodes.sort(function (a, b) {

			return b.depth - a.depth;

		}).map(node => {

			return node.name;

		});



		return names;

	}



	/**

	 * 检查是否有子节点。

	 * @param {string} name 节点名称。

	 * @returns {boolean} 如果有子节点返回true,否则返回false。

	 * @public

	 * @example

	 * let hasSubNode = bodyObject.hasNode('subNodeName');

	 * if (hasSubNode) {

	 *     console.log('存在子节点。');

	 * } else {

	 *     console.log('不存在子节点。');

	 * }

	 */

	hasNode(name) {

		let node = this._getNodeByName(name, null);

		if (!node) {

			return false;

		}

		return true;

	}



	/**

	 * 根据名称获取子节点结果。

	 * @param {string} name 节点名称。

	 * @returns {NodeObject} 返回一个NodeObject对象。

	 * @public

	 * @example

	 * let node = bodyObject.getNodeByName('subNodeName');

	 * if (node) {

	 *     console.log('找到子节点。');

	 * } else {

	 *     console.log('未找到子节点。');

	 * }

	 */

	getNodeByName(name) {

		let _private = this[__.private];



		return this._getNodeByName(name, _private.node, null,

			(node) => {

				return new NodeObject({

					renderableNode: _private.renderableNode,

					node

				});

			}

		);

	}



	/**

	 * Promote node to object.

	 * @param {string} name The node name.

	 * @param {object} parentNode The parent node.

	 * @param {UStyle} nodeUStyle The sub node style.

	 * @returns {object}

	 * @private

	 */

	promoteNode(name, parentNode, nodeUStyle) {

		let _private = this[__.private];



		// Find sub node by name

		let node = this._getNodeByName(name, null);

		if (!node) {

			return null;

		}



		// clear subnode materal

		_private.renderableNode.clearAllSubNodeMaterials();



		// clear subnode effect

		_private.renderableNode.clearAllSubNodeEffects();



		// Attach sub node to parent

		const unode = parentNode.attachSubNode(node, nodeUStyle, (ustyle) => {

			_private.object.body.style.updateResource(ustyle)

		});



		return unode

	}



	// #endregion



	// #region Style



	hasStyle() {

		let _private = this[__.private];



		return !!_private.style;

	}



	getStyle() {

		let _private = this[__.private];



		if (!_private.style) {

			let bodyNode = this.getRenderableNode();

			if (!bodyNode.isRenderableNode) {

				return null;

			}



			_private.style = new Style(new StyleModifier(_private.object));

		}



		return _private.style;

	}



	setStyle(value) {

		if (!value) {

			return;

		}



		let style = this.getStyle();

		if (style == value) {

			return;

		}





		style.copy(value);

	}



	/**

	 * Get/Set style of body.

	 * @type {THING.Style}

	 * @example

	 *  let object = new THING.Object3D();

	 * 	let style = object.body.style;

	 * 	style.color = 'red';

	 * 	style.opacity = 0.1;

	 *  let ret = object.body.style.color[0] == 1;

	 *  // @expect(ret == true)

	 *  ret = object.body.style.opacity == 0.1;

	 *  // @expect(ret == true)

	 */

	get style() {

		return this.getStyle();

	}

	set style(value) {

		this.setStyle(value);

	}



	clearAllSubNodeMaterials() {

		let _private = this[__.private];

		if (_private.renderableNode) {

			_private.renderableNode.clearAllSubNodeMaterials();

		}

	}



	/**

	 * Get/Set sharedStyle of body.

	 * @type {THING.SharedStyle}

	 * @example

	 *  let obj1 = new THING.Box({

	 *		position: [0, 0, 0],

	 *	});

	 *	  let obj2 = new THING.Box({

	 *		position: [2, 0, 0],

	 *	});

	 *		let sharedStyle = new THING.SharedStyle({

	 *		color: 'red',

	 *		opacity:  0.5,

	 *		wireframe: true,

	 *	})

	 *	obj1.body.sharedStyle = sharedStyle;

	 *	obj2.body.sharedStyle = sharedStyle;

	 */

	get sharedStyle() {

		const _private = this[__.private];



		return _private.sharedStyle

	}

	set sharedStyle(style) {

		const _private = this[__.private];

		const oldSharedStyle = this.sharedStyle;



		if (style === oldSharedStyle) {

			return;

		}



		if (oldSharedStyle && (!style || style !== oldSharedStyle)) {

			oldSharedStyle.removeObject(_private.object);

			oldSharedStyle.release();

		}



		if (style) {

			style.addRef();

			style.addObject(_private.object);

			_private.renderableNode.setStyle(style.resource);



			_private.sharedStyle = style;

		}

		else {

			_private.sharedStyle && _private.renderableNode.setStyle(this.style.resource);



			_private.sharedStyle = null;

		}

	}



	// #endregion



	// #region Attach

	// A.Attach(B)  Means B attch to A, B as a child Node of A

	// 目前这个接口产生的对象变化不支持保存,只能在运行时用

	attach(body, name) {

		let _private = this[__.private];



		// 创建rootNode-renderableRoot-renderableNode结构

		this.createRootNode();

		let node = _private.renderableNode

		if (node.isRenderableNode && node.getName() != 'renderableRoot') {

			_private.renderableNode = this._createParentNode('renderableRoot', node);

		}



		// 将body的renderableNode挂在renderableRoot下

		let oriRenderableNode = body.getRenderableNode()

		let oriParent = oriRenderableNode.getParent();

		let oriMatrixWorld = oriRenderableNode.getMatrixWorld();

		_private.renderableNode.add(oriRenderableNode);

		oriRenderableNode.setMatrixWorld(oriMatrixWorld);

		if (name) {

			// temp: 因为setName的接口修改,此处直接修改_node.name

			oriRenderableNode._node.name = name;

		}



		// 特殊处理

		// saveToDefault:保证被attach对象的style不被覆盖

		// 清空baseObject_orderID:避免鼠标点击拾取到原对象

		oriRenderableNode.$traverse(item => {

			item.getStyle().saveToDefault();

		})

		let userData = oriRenderableNode.getUserData();

		if (userData && userData['baseObject_orderID']) {

			delete userData['baseObject_orderID'];

		}

		oriRenderableNode.setUserData(userData);



		// 将body的renderableNode设置为新Node

		let newRenderableNode = Utils.createObject('Node', { app: this.app });

		oriParent.add(newRenderableNode);

		body.updateNode(newRenderableNode);

		body.style.replaceResource(newRenderableNode.getStyle())

	}

	// #endregion



}



export {

	BodyObject

}