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

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



let _position = MathUtils.createVec3();

let _localPosition = MathUtils.createVec3();

let _tempPosition = MathUtils.createVec3();

let _scale = MathUtils.createVec3();

let _localScale = MathUtils.createVec3();

let _tempScale = MathUtils.createVec3();

let _quat = MathUtils.createQuat();

let _localQuat = MathUtils.createQuat();

let _quatForAngles = MathUtils.createQuat();

let _quatForRotation = MathUtils.createQuat();

let _tempQuat = MathUtils.createQuat();



let _angles = MathUtils.createVec3();

let _mat4_0 = MathUtils.createMat4();

let _mat4_1 = MathUtils.createMat4();

let _mat4_2 = MathUtils.createMat4();



/**
 * NodeObject - 节点对象
 * 
 * 从场景中的可渲染对象获取网格的子节点对象。
 * 
 * @class NodeObject
 * @memberof THING
 * @public
 * 
 */
class NodeObject {



	/**

	 * 从场景中的可渲染对象获取网格的子节点对象。

	 * @param {object} param 初始参数。

	 */

	constructor(param = {}) {

		let node = param['node'];

		let renderableNode = param['renderableNode'];

		this._name = node.name;

		this._renderableNode = renderableNode;

		this._node = node;

	}



	// #region Private

	_updateMatrix(position, quaternion, scale) {

		this.matrix = MathUtils.composeToMat4(position, quaternion, scale, _mat4_0);

	}

	// #endregion



	// #region Coordinates Transform

	/**

	 * 将本地坐标转换为自身坐标。

	 * @param {Array<number>} position 本地坐标。

	 * @param {boolean} [ignoreScale=false] 是否忽略缩放因素。

	 * @returns {Array<number>} 目标的引用。

	 * @public

	 * @example

	 * // 将本地坐标转换为自身坐标,忽略缩放因素

	 * let localPosition = [1, 2, 3];

	 * let selfPosition = nodeObject.localToSelf(localPosition, true);

	 */

	localToSelf(position, ignoreScale = false) {

		let worldPosition = this.localToWorld(position, ignoreScale);



		return this.worldToSelf(worldPosition, ignoreScale);

	}



	/**

	 * 将自身位置转换为本地位置。

	 * @param {Array<number>} position 自身位置。

	 * @param {boolean} [ignoreScale=false] 是否忽略缩放因素。

	 * @returns {Array<number>} 目标的引用。

	 * @public

	 * @example

	 * // 将自身位置转换为本地位置,忽略缩放因素

	 * let selfPosition = [1, 2, 3];

	 * let localPosition = nodeObject.selfToLocal(selfPosition, true);

	 */

	selfToLocal(position, ignoreScale = false) {

		let worldPosition = this.selfToWorld(position, ignoreScale);



		return this.worldToLocal(worldPosition, ignoreScale);

	}



	/**

	 * 将世界位置转换为自身位置。

	 * @param {Array<number>} position 世界位置。

	 * @param {boolean} [ignoreScale=false] 是否忽略缩放因素。

	 * @returns {Array<number>} 目标的引用。

	 * @public

	 * @example

	 * // 将世界位置转换为自身位置,忽略缩放因素

	 * let worldPosition = [1, 2, 3];

	 * let selfPosition = nodeObject.worldToSelf(worldPosition, true);

	 */

	worldToSelf(position, ignoreScale = false) {

		return MathUtils.worldToSelf(this.matrixWorld, position, ignoreScale);

	}



	/**

	 * 将自身位置转换为世界位置。

	 * @param {Array<number>} position 自身位置。

	 * @param {boolean} [ignoreScale=false] 是否忽略缩放因素。

	 * @returns {Array<number>} 目标的引用。

	 * @public

	 * @example

	 * // 将自身位置转换为世界位置,忽略缩放因素

	 * let selfPosition = [1, 2, 3];

	 * let worldPosition = nodeObject.selfToWorld(selfPosition, true);

	 */

	selfToWorld(position, ignoreScale = false) {

		return MathUtils.selfToWorld(this.matrixWorld, position, ignoreScale);

	}



	/**

	 * 将相对位置转移给世界位置。

	 * @param {Array<number>} position 相对位置。

	 * @param {boolean} [ignoreScale=false] 是否忽略缩放因素。

	 * @returns {Array<number>} 目标的引用。

	 * @public

	 * @example

	 * // 将自身位置转换为世界位置,忽略缩放因素

	 * let selfPosition = [1, 2, 3];

	 * let worldPosition = nodeObject.localToWorld(selfPosition, true);

	 */

	localToWorld(position, ignoreScale = false) {

		let root = this._node.root;

		if (root) {

			return root.selfToWorld(_position, position, ignoreScale);

		}

		else {

			return position;

		}

	}



	/**

	 * 将世界位置转换为本地位置。

	 * @param {Array<number>} position 世界位置。

	 * @param {boolean} [ignoreScale=false] 是否忽略缩放因素。

	 * @returns {Array<number>} 目标的引用。

	 * @public

	 * @example

	 * // 将世界位置转换为本地位置,忽略缩放因素

	 * let worldPosition = [1, 2, 3];

	 * let localPosition = nodeObject.worldToLocal(worldPosition, true);

	 */

	worldToLocal(position, ignoreScale = false) {

		let root = this._node.root;

		if (root) {

			return root.worldToSelf(_localPosition, position, ignoreScale);

		}

		else {

			return position;

		}

	}



	// #endregion



	// #region Common



	get name() {

		return this._name;

	}



	get node() {

		return this._node;

	}



	get isRenderable() {

		return !!this._node.isRenderable;

	}



	// #endregion



	// #region Transform



	/**

	 * 绕X轴旋转节点。

	 * @param {number} angle 旋转角度(以角度为单位)。

	 * @public

	 * @example

	 * // 将节点绕X轴旋转45度

	 * nodeObject.rotateX(45);

	 */

	rotateX(angle) {

		let quat = MathUtils.getQuatFromAngles([angle, 0, 0]);

		MathUtils.quat.multiply(quat, this.localQuaternion, quat);

		this.localQuaternion = quat;

	}



	/**

	 * 绕Y轴旋转节点。

	 * @param {number} angle 旋转角度(以角度为单位)。

	 * @public

	 * @example

	 * // 将节点绕Y轴旋转45度

	 * nodeObject.rotateY(45);

	 */

	rotateY(angle) {

		let quat = MathUtils.getQuatFromAngles([0, angle, 0]);

		MathUtils.quat.multiply(quat, this.localQuaternion, quat);

		this.localQuaternion = quat;

	}



	/**

	 * 绕Z轴旋转节点。

	 * @param {number} angle 旋转角度(以角度为单位)。

	 * @public

	 * @example

	 * // 将节点绕Z轴旋转45度

	 * nodeObject.rotateZ(45);

	 */

	rotateZ(angle) {

		let quat = MathUtils.getQuatFromAngles([0, 0, angle]);

		MathUtils.quat.multiply(quat, this.localQuaternion, quat);

		this.localQuaternion = quat;

	}



	/**

	 * 获取世界空间中的上方向。

	 * @type {Array<number>}

	 * @public

	 * @example

	 * // 获取节点在世界空间中的上方向

	 * let upDirection = nodeObject.up;

	 */

	get up() {

		const matrixWorld = this.matrixWorld;

		return MathUtils.normalizeVector([matrixWorld[4], matrixWorld[5], matrixWorld[6]]);

	}



	/**

	 * 获取世界空间中的前方向。

	 * @type {Array<number>}

	 * @public

	 * @example

	 * // 获取节点在世界空间中的前方向

	 * let forwardDirection = nodeObject.forward;

	 */

	get forward() {

		const matrixWorld = this.matrixWorld;

		return MathUtils.normalizeVector([matrixWorld[8], matrixWorld[9], matrixWorld[10]]);

	}



	/**

	 * 获取世界空间中的右方向。

	 * @type {Array<number>}

	 * @public

	 * @example

	 * // 获取节点在世界空间中的右方向

	 * let rightDirection = nodeObject.cross;

	 */

	get cross() {

		let up = this.up;

		let forward = this.forward;



		let target = [0, 0, 0];

		MathUtils.vec3.cross(target, up, forward);

		MathUtils.vec3.normalize(target, target);



		return target;

	}



	/**

	 * 获取/设置本地位置。

	 * @type {Array<number>}

	 * @example

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

	 * @public

	 */

	get localPosition() {

		MathUtils.decomposeFromMat4(this.matrix, _localPosition, _tempQuat, _tempScale);

		return _localPosition;

	}

	set localPosition(value) {

		let position = value.slice(0);

		this._updateMatrix(position, this.localQuaternion, this.localScale);

	}



	/**

	 * 获取/设置本地四元数。

	 * @type {Array<number>}

	 * @example

	 * 	object.body.nodes[0].localQuaternion = THING.Math.getQuatFromAngles([45, 45, 45]);

	 * @public

	 */

	get localQuaternion() {

		MathUtils.decomposeFromMat4(this.matrix, _tempPosition, _localQuat, _tempScale);

		return _localQuat

	}

	set localQuaternion(value) {

		let quaternion = value.slice(0);

		this._updateMatrix(this.localPosition, quaternion, this.localScale);

	}



	/**

	 * 获取/设置本地角度。

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

	 * @type {Array<number>}

	 * @example

	 * 	object.body.nodes[0].localAngles = [45, 45, 45];

	 * @public

	 */

	get localAngles() {

		return MathUtils.getAnglesFromQuat(this.localQuaternion, _angles);

	}

	set localAngles(value) {

		this.localQuaternion = MathUtils.getQuatFromAngles(value, _localQuat);

	}



	/**

	 * 获取/设置本地缩放。

	 * @type {Array<number>}

	 * @example

	 * 	object.body.nodes[0].localScale = [3, 3, 3];

	 * @public

	 */

	get localScale() {

		MathUtils.decomposeFromMat4(this.matrix, _tempPosition, _tempQuat, _localScale);

		return _localScale

	}

	set localScale(value) {

		let scale = value.slice(0);

		this._updateMatrix(this.localPosition, this.localQuaternion, scale);

	}



	/**

	 * 获取/设置世界坐标

	 * @type {Array<number>}

	 * @example

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

	 * @public

	 */

	get position() {

		MathUtils.decomposeFromMat4(this.matrixWorld, _position, _tempQuat, _tempScale);

		return _position;

	}

	set position(value) {

		MathUtils.decomposeFromMat4(this.matrixWorld, _position, _quat, _scale);

		let matrixWorld = MathUtils.composeToMat4(value, _quat, _scale, _mat4_2);

		this.matrixWorld = matrixWorld;

	}



	/**

	 * 获取/设置世界四元数。

	 * @type {Array<number>}

	 * @example

	 * 	object.body.nodes[0].quaternion = THING.Math.getQuatFromAngles([45, 45, 45]);

	 * @public

	 */

	get quaternion() {

		MathUtils.decomposeFromMat4(this.matrixWorld, _position, _quat, _scale);

		return _quat;

	}

	set quaternion(value) {

		MathUtils.decomposeFromMat4(this.matrixWorld, _position, _quat, _scale);

		let matrixWorld = MathUtils.composeToMat4(_position, value, _scale, _mat4_2);

		this.matrixWorld = matrixWorld;

	}



	/**

	 * 获取/设置世界角度。

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

	 * @type {Array<number>}

	 * @example

	 * 	object.body.nodes[0].angles = [45, 45, 45];

	 * @public

	 */

	get angles() {

		return MathUtils.getAnglesFromQuat(this.quaternion, _angles);

	}

	set angles(value) {

		this.quaternion = MathUtils.getQuatFromAngles(value, _quatForAngles);

	}



	/**

	 * 获取/设置世界旋转。

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

	 * @type {Array<number>}

	 * @example

	 * 	object.body.nodes[0].rotation = [45, 45, 45];

	 * @public

	 */

	get rotation() {

		return MathUtils.getAnglesFromQuat(this.quaternion, _angles);

	}

	set rotation(value) {

		this.quaternion = MathUtils.getQuatFromAngles(value, _quatForRotation);

	}



	/**

	 * 获取/设置缩放

	 * @type {Array<number>}

	 * @example

	 * 	object.body.nodes[0].scale = [3, 3, 3];

	 * @public

	 */

	get scale() {

		MathUtils.decomposeFromMat4(this.matrixWorld, _tempPosition, _tempQuat, _scale);

		return _scale;

	}

	set scale(value) {

		MathUtils.decomposeFromMat4(this.matrixWorld, _position, _quat, _scale);

		let matrixWorld = MathUtils.composeToMat4(_position, _quat, value, _mat4_2);

		this.matrixWorld = matrixWorld;

	}



	/**

	 * 获取/设置矩阵。

	 * @type {Array<number>}

	 * @example

	 * 	object.body.nodes[0].matrix = THING.Math.composeToMat4([10, 10, 10], [0, 0, 0, 1], [2, 2, 2]);

	 * @public

	 */

	get matrix() {

		let node = this._node;

		return node.matrix;

	}

	set matrix(value) {

		let node = this._node;

		this._renderableNode.setSubNodeMatrix(node, value);

	}



	/**

	 * 获取/设置世界矩阵。

	 * @type {Array<number>}

	 * @example

	 * 	object.body.nodes[0].matrixWorld = THING.Math.composeToMat4([10, 10, 10], [0, 0, 0, 1], [2, 2, 2]);

	 * @public

	 */

	get matrixWorld() {

		let node = this._node;



		let root = node.root;

		root.updateMatrixWorld();

		root.getMatrixWorld(_mat4_0);



		return MathUtils.mat4.multiply(_mat4_0, _mat4_0, node.matrix).slice(0);

	}

	set matrixWorld(value) {

		let node = this._node;



		let root = node.root;

		root.updateMatrixWorld();

		root.getMatrixWorld(_mat4_0);



		MathUtils.mat4.invert(_mat4_1, _mat4_0);

		MathUtils.mat4.multiply(_mat4_1, _mat4_1, value);



		this._renderableNode.setSubNodeMatrix(node, _mat4_1);

	}



	getMatrixWorldIgnoreScale() {

		let node = this._node;



		// scale the rotation part

		const matrix = removeScaleFromMatrix(node.matrix, this.localScale);



		const root = node.root;

		root.updateMatrixWorld();

		root.getMatrixWorld(_mat4_0);



		return MathUtils.mat4.multiply(_mat4_0, _mat4_0, matrix).slice(0);



		function removeScaleFromMatrix(matrix, scale) {

			// 计算每个轴的逆缩放因子

			const invScaleX = 1 / scale[0];

			const invScaleY = 1 / scale[1];

			const invScaleZ = 1 / scale[2];



			// 创建一个新的矩阵来存储结果

			const matrixWithoutScale = [...matrix];



			// 应用逆缩放因子到旋转部分

			matrixWithoutScale[0] *= invScaleX;

			matrixWithoutScale[1] *= invScaleX;

			matrixWithoutScale[2] *= invScaleX;



			matrixWithoutScale[4] *= invScaleY;

			matrixWithoutScale[5] *= invScaleY;

			matrixWithoutScale[6] *= invScaleY;



			matrixWithoutScale[8] *= invScaleZ;

			matrixWithoutScale[9] *= invScaleZ;

			matrixWithoutScale[10] *= invScaleZ;



			return matrixWithoutScale;

		}

	}



	// #endregion



	get isSubNodeObject() {

		Utils.warn(`Please use .isNodeObject`);



		return true;

	}



	get isNodeObject() {

		return true;

	}



	/**

	 * 获取/设置子节点材质的颜色。

	 * @type {string|Array<number>}

	 * @public

	 * @example

	 * // 设置子节点材质的颜色为红色

	 * subNodeObject.color = [1, 0, 0]; // RGB

	 */

	get color() {

		let node = this._node;

		return this._renderableNode.getSubNodeMaterial(node, 'color');

	}

	set color(value) {

		let color = Utils.parseColor(value, null);

		if (this.color != color) {

			let node = this._node;

			this._renderableNode.setSubNodeMaterial(node, 'color', color);

		}

	}



	/**

	 * 获取/设置子节点材质的不透明度。

	 * @type {number}

	 * @public

	 * @example

	 * // 设置子节点材质的不透明度为0.5

	 * subNodeObject.opacity = 0.5;

	 */

	get opacity() {

		let node = this._node;

		return this._renderableNode.getSubNodeMaterial(node, 'opacity');

	}

	set opacity(value) {

		if (this.opacity != value) {

			let node = this._node;

			this._renderableNode.setSubNodeMaterial(node, 'opacity', value);

		}

	}



	/**

	 * 获取/设置子节点的可见性。

	 * @type {boolean}

	 * @public

	 * @example

	 * // 设置子节点的可见性为false

	 * subNodeObject.visible = false;

	 */

	get visible() {

		let node = this._node;

		return this._renderableNode.getSubNodeVisible(node);

	}

	set visible(value) {

		if (this.visible != value) {

			let node = this._node;

			this._renderableNode.setSubNodeVisible(node, value);

		}

	}



	// 由于在渲染端只有UNode记录了UserData,subNode(Mesh)并未记录UserData

	// 因此我们将subNode的UserData数据以map的形式存在UNode上

	// subNode的UserData的key为id-name

	get userData() {

		let data = this._renderableNode.getUserData();

		if (data && data.subNodeUserData) {

			let key = this.node.id + '-' + this.node.name;

			return data.subNodeUserData.get(key)

		}

		return null;

	}

	set userData(value) {

		if (this.userData != value) {

			let data = this._renderableNode.getUserData();

			if (!data) {

				data = {};

				this._renderableNode.setUserData(data);

			}

			data.subNodeUserData = data.subNodeUserData || new Map();

			let key = this.node.id + '-' + this.node.name;

			data.subNodeUserData.set(key, value);

		}

	}



	/**

	 * 获取AABB包围盒。

	 * @type {THING.AABBResult}

	 * @example

	 * let subObject = object.body.getNodeByName('MeshName')

	 * if(subObject){

	 * 	 console.log(subObject.AABB)

	 * }

	 * @public

	 */

	get AABB() {

		let node = this._node;

		return this._renderableNode.getSubNodeAABB(node);

	}



	/**

	 * 获取OBB包围盒。

	 * @type {THING.OBBResult}

	 * @example

	 * let subObject = object.body.getNodeByName('MeshName')

	 * if(subObject){

	 * 	 console.log(subObject.OBB)

	 * }

	 * @public

	 */

	get OBB() {

		let node = this._node;

		return this._renderableNode.getSubNodeOBB(node);

	}



	/**

	 * 获取/设置子节点的勾边颜色。

	 * @type {Array<number>}

	 * @public

	 * @example

	 * // 设置子节点的勾边为[0,0,0]

	 * subNodeObject.outlineColor = [0,0,0];

	 * // 取消子节点的勾边

	 * subNodeObject.outlineColor = null;

	 */

	set outlineColor(value) {

		let node = this._node;

		let color = Utils.parseColor(value, null);

		return this._renderableNode.setSubNodeEffect(node, 'outlineColor', color);

	}

	get outlineColor() {

		let node = this._node;

		return this._renderableNode.getSubNodeEffect(node, 'outlineColor');

	}



	/**

	 * 获取/设置子节点的发光强度。

	 * @type {number}

	 * @public

	 * @example

	 * // 设置子节点的发光强度为1

	 * subNodeObject.glow = 1;

	 * // 取消子节点的发光

	 * subNodeObject.glow = null;

	 * subNodeObject.glow = 0;

	 */

	set glow(value) {

		let node = this._node;

		return this._renderableNode.setSubNodeEffect(node, 'glow', value);

	}

	get glow() {

		let node = this._node;

		return this._renderableNode.getSubNodeEffect(node, 'glow');

	}



	/**

	 * 获取父节点

	 * @returns {THING.NodeObject|null} 返回父节点,如果不存在则返回null

	 * @public

	 */

	get parentNode() {

		// 获取所有子节点

		const subNodes = [];

		this._renderableNode.getSubNodes(subNodes, null)



		// 查找匹配父节点名称的子节点

		const parentSubNode = subNodes.find(subNode => subNode.name === this.node.parentName)

		if (!parentSubNode) {

			return null

		}



		// 创建并返回新的节点对象

		return new NodeObject({

			node: parentSubNode,

			renderableNode: this._renderableNode

		});

	}



}



export { NodeObject }