"use strict";

var _ = require("lodash"),
	COLOR = require("color"),
	Condicio = require("condicio"),
	Matrix = require("gl-matrix"),
	jQuery = require("jquery"),
	Node = require("./Node.js"),
	DrawingRootNode = require("./DrawingRootNode.js"),
	AnimationNode = require("./AnimationNode.js"),
    Error = require("../Error.js"),
	Utility = require("../Utility.js"),
	CvtForJson = require("../CvtForJson.js"),
	ParamValidation = require("../ParamValidation.js"),
	Style = require("./Style.js");

var NetworkError = Error.NetworkError;
var DataError = Error.DataError;	
	
	
/**
 * @class View3D.NodeFactory
 * 三维节点工厂
 *
 * 提供可创建、加载三维场景中{@link Node}的接口
 *
 * 应用不应该构建,应该通过{@link View3D}直接获取
 */
function NodeFactory(session, objectID) {
    this.objectID = objectID;
    this.session = session;
};

/**
 * 创建Group节点
 *
 * @param {Node[]} [nodes] 	子节点集合
 * @param {String} [name]	节点名称
 *
 * @return {View3D.Node}
 */
NodeFactory.prototype.createGroup = function (nodes, name) {
	name = name || "";
	nodes = nodes || [];
	
	ParamValidation.checkIsTypeArray(nodes, Node, "The type of nodes must be 'Node Array' and valid");
	Condicio.checkIsString(name, "The type of names must be 'String'!");

    var childNodeIDs = CvtForJson.cvtNodeArray(nodes);
    var nodeID = Utility.genGUID();
	var params = {
		nodeID: nodeID,
		childNodeIDs: childNodeIDs,
		name: name
	};
    this.session.request(this.objectID, "createGroup", params);

    return new Node(this.session, nodeID, name);
};

/**
 * 创建MatrixTransform节点
 *
 * 用于将其子节点进行位置的变换,如平移、旋转等
 *
 * @param {mat4}   mat		位置变换的矩阵表示
 * @param {Node[]} [nodes] 	子节点的集合,集合中节点不能为空
 * @param {String} [name]	创建节点的名称
 *
 * @return {View3D.Node}
 */
NodeFactory.prototype.createTransformation = function (mat, nodes, name) {
	name = name || "";
	nodes = nodes || [];
	
    ParamValidation.checkIsMat4(mat, "The type of mat must be 'Matrix-mat4' and valid");
	ParamValidation.checkIsTypeArray(nodes, Node, "The type of nodes must be 'Node Array' and valid");
	Condicio.checkIsString(name, "The type of name must be 'String'!");

    var childNodeIDs = CvtForJson.cvtNodeArray(nodes);;
	
	// 注意:不能直接传递mat给服务端
	var matArray = CvtForJson.cvtMat4(mat);

    var nodeID = Utility.genGUID();
	var params = {
		nodeID: nodeID,
		mat: matArray,
		childNodeIDs: childNodeIDs,
		name: name
	};
    this.session.request(this.objectID, "createTransformation", params);

    return new Node(this.session, nodeID, name);
};

/**
 * 创建天空盒
 *
 * @return {View3D.Node}	
 */
NodeFactory.prototype.createSkybox = function () {
    var nodeID = Utility.genGUID();
    this.session.request(this.objectID, "createSkybox", { nodeID:nodeID });

    return new Node(this.session, nodeID, "");
};

/**
 * 创建用户平面
 *
 * @param {Number} height  	平面高度(毫米)
 * @param {String} [name]	平面的名称
 *
 * @return {View3D.Node}
 */
NodeFactory.prototype.createUserplane = function (height, name) {
	name = name || "";
	
    Condicio.checkIsNumber(height, "The type of height must be 'Number'!");
	Condicio.checkIsString(name, "The type of name must be 'String'!");
	
	var nodeID = Utility.genGUID();
	var params = {
		nodeID: nodeID,
		name: name,
		height: height
	};
    this.session.request(this.objectID, "createUserplane", params);

    return new Node(this.session, nodeID, name);
};

/**
 * 加载OGF模型
 *
 * @param {Object} 						options                           		模型加载参数
 * @param {String}  					options.url				          		模型URL地址 支持“http://”地址和“file:///”格式的下载地址
 * @param {Function}					options.callback		          		加载结束回调
 * @param {NetworkError/DataError}		options.callback.err  					加载出错时候返回错误
 *               						err {NetworkError}               		模型下载失败
 *                						err {DataError}                  		模型数据出错
 * @param {DrawingRootNode} 			options.callback.node  	          		加载成功返回加载结果
 * @param {Function} 					[options.progress]		          		加载进度回调
 * @param {Number} 						options.progress.current          		当前加载进度 范围从0到1 0表示加载模型开始 1表示模型加载完成
 */
NodeFactory.prototype.loadOGFModel = function (options) {
    Condicio.checkIsObject(options, "The type of options must be 'Object'!");
    Condicio.checkIsString(options.url, "The type of url must be 'String'!");
    Condicio.checkIsFunction(options.callback, "The type of callback must be 'Function'!");
	if (!Condicio.isUndefined(options.progress))
		Condicio.checkIsFunction(options.progress, "The type of progress must be 'Function'!");
	
	var _this = this;
	var callbackWrapper = function (error, result) {
		if (error) {
			options.callback(error, null, options.data);
		}
		else {
			//options.callback(null, new DrawingRootNode(_this.session, result.nodeID, result.name));
			options.callback(null, new DrawingRootNode(_this.session, result.nodeID, result.name), options.data);
		}
			
		if (!Condicio.isUndefined(options.progress))
			_this.session.unregisterCallback(progressID);
	 };
	 
	var callbackID = Utility.genGUID();
	this.session.registerCallback(callbackID, callbackWrapper, "loadOGFModel");
	
	var params = {
		url: options.url,
		callbackID: callbackID
	};
	var progressID = Utility.genGUID();
	if (!Condicio.isUndefined(options.progress)) {
		params.progressID = progressID;
		
		this.session.registerCallback(progressID, function (error, result) {
			options.progress(result.current);
		}, "loadOGFModel", true);
	}
	
    this.session.request(this.objectID, "loadOGFModel", params);
};

/**
 * 加载PGF优化结果
 * 需要注意的是这个接口需要配合ROS使用,当与ROS的连接断开时,此接口是不能用的。
 *
 * @param {Object} 						options                           		模型加载参数
 * @param {String}  					options.fileName				        模型名字(PGF名字,带后缀,例:uuid.pgf)
 * @param {Function}					options.callback		          		加载结束回调
 * @param {NetworkError/DataError}		options.callback.err  					加载出错时候返回错误
 *               						err {NetworkError}               		模型下载失败
 *                						err {DataError}                  		模型数据出错
 * @param {DrawingRootNode} 			options.callback.node  	          		加载成功返回加载结果
 * @param {Function} 					[options.progress]		          		加载进度回调
 * @param {Number} 						options.progress.current          		当前加载进度 范围从0到1 0表示加载模型开始 1表示模型加载完成
 */
NodeFactory.prototype.loadPGFOptimizeResult = function (options) {
    Condicio.checkIsObject(options, "The type of options must be 'Object'!");
    Condicio.checkIsString(options.fileName, "The type of fileName must be 'String'!");
    Condicio.checkIsFunction(options.callback, "The type of callback must be 'Function'!");
	if (!Condicio.isUndefined(options.progress))
		Condicio.checkIsFunction(options.progress, "The type of progress must be 'Function'!");
	
	var _this = this;
	var callbackWrapper = function (error, result) {
		if (error) {
			options.callback(error, null);
		}
		else 
			options.callback(null, new DrawingRootNode(_this.session, result.nodeID, result.name));
			
		if (!Condicio.isUndefined(options.progress))
			_this.session.unregisterCallback(progressID);
	 };
	 
	var callbackID = Utility.genGUID();
	this.session.registerCallback(callbackID, callbackWrapper, "loadPGFOptimizeResult");
	
	var params = {
		fileName: options.fileName,
		callbackID: callbackID
	};
	var progressID = Utility.genGUID();
	if (!Condicio.isUndefined(options.progress)) {
		params.progressID = progressID;
		
		this.session.registerCallback(progressID, function (error, result) {
			options.progress(result.current);
		}, "loadPGFOptimizeResult", true);
	}
	
    this.session.request(this.objectID, "loadPGFOptimizeResult", params);
};


/**
 * 加载PGF模型
 *
 * @param {Object} 						options                           		模型加载参数
 * @param {String}  					options.url				          		模型URL地址 支持“http://”地址和“file:///”格式的下载地址
 * @param {Function}					options.callback		          		加载结束回调
 * @param {NetworkError/DataError}		options.callback.err  					加载出错时候返回错误
 *               						err {NetworkError}               		模型下载失败
 *                						err {DataError}                  		模型数据出错
 * @param {DrawingRootNode} 			options.callback.node  	          		加载成功返回加载结果
 * @param {Function} 					[options.progress]		          		加载进度回调
 * @param {Number} 						options.progress.current          		当前加载进度 范围从0到1 0表示加载模型开始 1表示模型加载完成
 */
NodeFactory.prototype.loadPGFModel = function (options) {
    Condicio.checkIsObject(options, "The type of options must be 'Object'!");
    Condicio.checkIsString(options.url, "The type of url must be 'String'!");
    Condicio.checkIsFunction(options.callback, "The type of callback must be 'Function'!");
	if (!Condicio.isUndefined(options.progress))
		Condicio.checkIsFunction(options.progress, "The type of progress must be 'Function'!");
	
	var _this = this;
	var callbackWrapper = function (error, result) {
		if (error) {
			options.callback(error, null);
		}
		else 
			options.callback(null, new DrawingRootNode(_this.session, result.nodeID, result.name));
			
		if (!Condicio.isUndefined(options.progress))
			_this.session.unregisterCallback(progressID);
	 };
	 
	var callbackID = Utility.genGUID();
	this.session.registerCallback(callbackID, callbackWrapper, "loadPGFModel");
	
	var params = {
		url: options.url,
		callbackID: callbackID
	};
	var progressID = Utility.genGUID();
	if (!Condicio.isUndefined(options.progress)) {
		params.progressID = progressID;
		
		this.session.registerCallback(progressID, function (error, result) {
			options.progress(result.current);
		}, "loadPGFModel", true);
	}
	
    this.session.request(this.objectID, "loadPGFModel", params);
};



/**
 * 创建导航立方体
 * <a href="https://wiki.fulongtech.cn/pages/viewpage.action?pageId=108036913">详细请参考</a>
 * @param {Object} 		  option   							创建导航立方体参数
 * @param {vec2} 		  [option.offset = vec2(-90, 80)]  导航立方体的位置参数,第一个值为正(负)表示模型中央与窗口左侧(右侧)的像素偏移量,第二个值为正(负)表示模型中央与窗口上(下)侧的像素偏移量
 * @param {double} 	      [option.scale = 150]				自定义导航立方体的大小,默认值为立方体边长为150像素
 * @return {View3D.Node} 
 */
NodeFactory.prototype.createNavigator = function (option) {
	
   if (Condicio.isUndefined(option))  {
	   option = {
		   offset : Matrix.vec2.fromValues(-90, 80),
		   scale : 150
	   };
   }
   if (Condicio.isUndefined(option.offset))  {
		option.offset = Matrix.vec2.fromValues(-90, 80)
   }
   if (Condicio.isUndefined(option.scale))  {
		option.scale = 150
   }
   
	Condicio.checkIsObject(option, "param option must be a Object");
	ParamValidation.checkIsVec2(option.offset, "The type of option-offset must be 'vec2' ");
	Condicio.checkIsNumber(option.scale, "The type of opyion-scale must be 'Number'!");

    var nodeID = Utility.genGUID();
	var posArray = CvtForJson.cvtVec2(option.offset);
	var params = {
		nodeID: nodeID,
		posArray: posArray,
		scale: option.scale
	};
	this.session.request(this.objectID, "createNavigator", params);

    return new Node(this.session, nodeID, "");
};

/**
 * 释放节点资源
 *
 * @param {Node[]} nodes 待释放的节点资源集合
 * 
 */
NodeFactory.prototype.releaseNode = function (nodes) {
    ParamValidation.checkIsTypeArray(nodes, Node, "The type of nodes must be 'Node Array' and valid");

    var objectIDs = CvtForJson.cvtNodeArray(nodes);
    this.session.request(this.objectID, "releaseNode", { objectIDs: objectIDs });

};

/**
 * 创建文本批注
 *
 * @param {Object}      option							
 * @param {String} 		[option.text = ""]   	批注文字,需自带换行(插入'\n')
 * @param {vec2[]}		[option.pointerPoints]	连线点集合,为空或者未定义,则不能连线,给个点是一个vec2,表示屏幕坐标(单位:像素)
 * @param {Object} 	    [option.style] 		    绘制属性,参照View3D.Style.TextLabelStyle的定义
 *
 * @return {View3D.Node}
 */
NodeFactory.prototype.createTextLabel = function (option) {
	var defaultOption = {
		text: "",
		pointerPoints:[],
		style: Style.TextLabelStyle
	};
	var newOption = jQuery.extend(true, {}, defaultOption, option);
	
	// 参数检查
	Condicio.checkIsObject(newOption, "param option must be a Object");
    Condicio.checkIsString(newOption.text, "param text must be a string");
	Condicio.checkArgument(newOption.style.fixSizeRange.min >= 0, "param range min must greater than or equal to 0!");
	Condicio.checkArgument(newOption.style.fixSizeRange.max > 0, "param range max must greater than 0!");
	ParamValidation.checkIsTypeArray(newOption.pointerPoints, Matrix.glMatrix.ARRAY_TYPE, "param pointerPoints must be a 'vec2 Array'");
	ParamValidation.checkIsTextLabelStyle(newOption.style);
	
	// 参数与BRS端适配
	newOption.style.pointer.line.color = newOption.style.pointer.line.color.rgbaArray();
	newOption.style.pointer.identifyLocationPoint.color = newOption.style.pointer.identifyLocationPoint.color.rgbaArray();
	newOption.style.textFrame.border.color = newOption.style.textFrame.border.color.rgbaArray();
	newOption.style.textFrame.text.color = newOption.style.textFrame.text.color.rgbaArray();
	newOption.style.textFrame.text.backdropColor = newOption.style.textFrame.text.backdropColor.rgbaArray();
	newOption.style.textFrame.fillColor = newOption.style.textFrame.fillColor.rgbaArray();
	
	var pointerPointsArray = [];
	for (var i =0; i<newOption.pointerPoints.length; ++i) {
		var pointerPoint = newOption.pointerPoints[i];
		pointerPointsArray.push([pointerPoint[0], pointerPoint[1]]);
	}
	newOption.pointerPoints = pointerPointsArray;
	
	var nodeID = Utility.genGUID();
    var params = {
        nodeID: nodeID,
		option: newOption
    };
	
    this.session.request(this.objectID, "createTextLabel", params);

    return new Node(this.session, nodeID, "");
};

/**
 * 创建图片批注
 *
 * @param {Object} 		            option                           		    创建图片标签参数
 * @param {String} 		            option.pictureUrl                           图片地址(支持“http://”地址和“file:///”格式)支持jpg、jpeg、bmp、tga、png图片格式
 * @param {Function} 	            option.callback                             创建标签结束回调
 * @param {NetworkError/DataError} 	option.callback.err                         创建过程出现错误时返回错误
 *                                  err {NetworkError}                		    图片下载失败
 *                					err {DataError}                   			图片数据出错
 * @param {Node} 		            option.callback.node  	          		    创建成功返回加载结果
 * 
 */
NodeFactory.prototype.createPictureLabel = function (option) {
    Condicio.checkIsString(option.pictureUrl, "param pictureUrl must be a string");

	var _this = this;
    var callbackWrapper = function (error, result) {
        if (error) {
            option.callback(error, null);
        }
        else
            option.callback(null, new Node(_this.session, result.nodeID, result.name));
    };
	
	var callbackID = Utility.genGUID();
	this.session.registerCallback(callbackID, callbackWrapper, "createPictureLabel");
	
    var params = {
        pictureUrl: option.pictureUrl,
		callbackID: callbackID
    };

    this.session.request(this.objectID, "createPictureLabel", params);

};

/**
 * 创建线段
 *
 * @param {vec3} 	startPt   	线段起点
 * @param {vec3} 	endPt     	线段终点
 * @param {Object} 	[lineStyle] 线段属性,参照View3D.Style.LineStyle的定义
 * @param {String} 	[name]      节点名称
 *
 * @return {View3D.Node}
 */
NodeFactory.prototype.createLine = function (startPt, endPt, lineStyle, name) {
	var newLineStyle = jQuery.extend({},Style.LineStyle, lineStyle);
	name = name || "";
	
	ParamValidation.checkIsVec3(startPt, "The type of startPt must be 'vec3'");
	ParamValidation.checkIsVec3(endPt, "The type of endPt must be 'vec3'");
	ParamValidation.checkIsLineStyle(newLineStyle);
	Condicio.checkIsString(name, "The type of name must be 'String'!");
		
	newLineStyle.color = newLineStyle.color.rgbaArray();
	var startPtArray = CvtForJson.cvtVec3(startPt);
	var endPtArray = CvtForJson.cvtVec3(endPt);
	
	var nodeID = Utility.genGUID();
    var params = {
		nodeID: nodeID,
		startPoint: startPtArray,
		endPoint: endPtArray,
        lineStyle: newLineStyle,
		nodeName: name
    };
	
	this.session.request(this.objectID, "createLine", params);

    return new Node(this.session, nodeID, name);
};

/**
 * 创建点模型
 *
 * @param {vec3}	point      		要绘制的点
 * @param {Object}  [pointStyle]    点属性,参照View3D.Style.PointStyle的定义
 * @param {String} 	[name]          节点名称
 *
 * @return {View3D.Node}
 */
NodeFactory.prototype.createPoint = function (point, pointStyle, name) {
	name = name || "";
	var newPointStyle = jQuery.extend({}, Style.PointStyle, pointStyle);
	
	ParamValidation.checkIsPointStyle(newPointStyle);
	ParamValidation.checkIsVec3(point, "The type of point must be 'vec3'");
	
	var numberArray = CvtForJson.cvtVec3(point);
	newPointStyle.color = newPointStyle.color.rgbaArray();
		
	var nodeID = Utility.genGUID();
    var params = {
		nodeID: nodeID,
		point: numberArray,
        pointStyle: newPointStyle,
		nodeName: name
    };
	
	this.session.request(this.objectID, "createPoint", params);

    return new Node(this.session, nodeID, name);
};

/**
 * 创建一个空心球体
 * @param {Number}	radius		球体半径(单位:毫米)
 * @param {COLOR}	color		球体颜色(颜色中的alpha值无效)
 *
 * @return {View3D.Node}
 */
NodeFactory.prototype.createSphere = function(radius, color){
	Condicio.checkIsNumber(radius, "The sphere radius must be Number!");
	Condicio.checkArgument(radius > 0, "The sphere radius must greater than 0");
	//Condicio.checkIsType(color, COLOR, "The type of color must be 'Color'");
	
	var nodeID = Utility.genGUID();
	var params = {
		nodeID: nodeID,
		radius: radius,
		color: color.rgbaArray()
	};
	
	this.session.request(this.objectID, "createSphere", params);
	
	return new Node(this.session, nodeID, "");
};

/**
 *	加载自定义模型
 * @param {Object}					option						加载自定义模型参数
 * @param {String}					option.modelUrl				模型文件地址(支持“http://”地址和“file:///”格式),模型文件支持格式:.osg、.ive、.3ds
 * @param {Function} 	            option.callback             加载自定义模型结束回调
 * @param {NetworkError/DataError} 	option.callback.err         加载过程出现错误时返回错误
 *                                  err {NetworkError}          模型文件下载失败
 *                					err {DataError}             模型数据出错
 * @param {Node} 		            option.callback.node  	    加载成功返回模型节点
 *
 */
NodeFactory.prototype.loadModel = function(option){
	Condicio.checkIsString(option.modelUrl, "param modelUrl must be a string");
	
	var _this = this;
    var callbackWrapper = function (error, result) {
        if (error) {
            option.callback(error, null);
        }
        else
            option.callback(null, new Node(_this.session, result.nodeID, ""));
    };
	
	var callbackID = Utility.genGUID();
	this.session.registerCallback(callbackID, callbackWrapper, "loadModel");
	
    var params = {
        modelUrl: option.modelUrl,
		callbackID: callbackID
    };

    this.session.request(this.objectID, "loadModel", params);
	
}; 

/**
 *	加载动画模型
 * @param {Object}					option						加载动画模型参数
 * @param {String}					option.modelUrl				模型文件地址(支持“http://”地址和“file:///”格式),模型文件支持格式:.fbx
 * @param {Function} 	            option.callback             加载动画模型结束回调
 * @param {NetworkError/DataError} 	option.callback.err         加载过程出现错误时返回错误
 *                                  err {NetworkError}          模型文件下载失败
 *                					err {DataError}             模型数据出错
 * @param {View3D.AnimationNode} 		    option.callback.node  	    加载成功返回动画节点
 *
 */
NodeFactory.prototype.loadAnimationModel = function(option){
	Condicio.checkIsString(option.modelUrl, "param modelUrl must be a string");
	
	var _this = this;
    var callbackWrapper = function (error, result) {
        if (error) {
            option.callback(error, null);
        }
        else
            option.callback(null, new AnimationNode(_this.session, result.nodeID));
    };
	
	var callbackID = Utility.genGUID();
	this.session.registerCallback(callbackID, callbackWrapper, "loadAnimationModel");
	
    var params = {
        modelUrl: option.modelUrl,
		callbackID: callbackID
    };

    this.session.request(this.objectID, "loadAnimationModel", params);
}; 

/**
 * 可配置粒子效果
 * <a href="https://fulongtech.atlassian.net/wiki/spaces/RenderingTech/pages/1407025332">参考样例点这里</a>
 * <a href="https://fulongtech.atlassian.net/wiki/spaces/~907676428/pages/1326481426">参数具体信息说明</a>
 * @param {Object}		option													创建粒子
 * @param {Function}	option.callback											创建粒子效果回调
 * @param {Error}		option.callback.error									返回错误信息
 * 						err {DataError}          								图片文件不存在
 * @param {Node}		option.callback.node									返回创建的粒子节点
 * @param {Object}		option.style											粒子参数
 * @param {vec3}		option.style.direction									发射方向
 * @param {vec3}		option.style.acceleration								加速度
 * @param {Number}		option.style.angle										发射角
 * @param {Number}		option.style.size										单个粒子大小
 * @param {Number}		option.style.flowRate									流量
 * @param {Number}		option.style.lifeTime									粒子的存活时间
 * @param {String}		option.style.imageName									图片文件名
 * @param {Object[]}	[option.style.alphaInterpolations]						透明度插值
 * 
 */
NodeFactory.prototype.createParticalSystem = function(option){

	ParamValidation.checkIsParticalSystemStyle(option.style);
	
	var direction = CvtForJson.cvtVec3(option.style.direction);
	option.style.direction = direction;

	var acceleration = CvtForJson.cvtVec3(option.style.acceleration);
	option.style.acceleration = acceleration;

	var callbackID = Utility.genGUID();
	var callbackWrapper = function (error, result) {
        if (error) {
            option.callback(error, null);
        }
        else
            option.callback(null, new Node(this.session, result.nodeID, ""));
    };
	
	this.session.registerCallback(callbackID, callbackWrapper, "createParticalSystem");
	
	var params = {
		style: option.style,
		callbackID : callbackID,
	};

	this.session.request(this.objectID, "createParticalSystem", params);
}; 


/**
 * 创建火
 * @param {Object}	[style]													创建火的参数
 * @param {Number}	[style.size = 1000]										火的大小
 * @param {vec2}	[style.direction = vec2(1, 0)]							风向(火、烟的偏移方向)
 * @param {Number}	[style.angle = 0]										偏移角度(火、烟受风影响而偏移的角度)
 *
 * @return {View3D.Node}
 */
NodeFactory.prototype.createFire = function(style){

	var defaultStyle = {
		size : 1000,
		direction : Matrix.vec2.fromValues(1, 0),
		angle : 0
	};
	var newStyle = jQuery.extend({}, defaultStyle, style);
	
	ParamValidation.checkIsVec2(newStyle.direction,"The type of style-direction must be 'vec2'" );
	Condicio.checkIsNumber(newStyle.size, "The type of style-size must be 'Number'!");
	Condicio.checkArgument(newStyle.size > 0, "The size must greater than 0");
	Condicio.checkIsNumber(newStyle.angle, "The type of style-angle must be 'Number'!");
	
	var numberArray = CvtForJson.cvtVec2(newStyle.direction);
	newStyle.direction = numberArray;
	
	var nodeID = Utility.genGUID();
	var params = {
		nodeID: nodeID,
        style: newStyle
	};
	
	this.session.request(this.objectID, "createFire", params);
	return new Node(this.session, nodeID, "");
}; 

/**
 * 创建消防喷水
 * @param {Object}	[style]									喷水参数
 * @param {Number}	[style.size = 1000]						水柱大小
 * @param {vec3}	[style.direction = vec3(1, 0, 1)]		喷水方向
 *
 * @return {View3D.Node}
 */
NodeFactory.prototype.createSprayingWater = function(style){

	var defaultStyle = {
		size : 1000,
		direction : Matrix.vec3.fromValues(1, 0, 1)
	};
	var newStyle = jQuery.extend({}, defaultStyle, style);
	Condicio.checkIsNumber(newStyle.size, "The type of style-size must be 'Number'!");
	Condicio.checkArgument(newStyle.size > 0, "The size must greater than 0");
	ParamValidation.checkIsVec3(newStyle.direction,"The type of style-direction must be 'vec3'" );
	
	var numberArray = CvtForJson.cvtVec3(newStyle.direction);
	newStyle.direction = numberArray;
	
	var nodeID = Utility.genGUID();
	var params = {
		nodeID: nodeID,
        style: newStyle
	};
	
	this.session.request(this.objectID, "createSprayingWater", params);
	return new Node(this.session, nodeID, "");
}; 

/**
 * 创建多边形
 * @param {Object}						option													创建多边形参数,支持创建凹凸多边形(创建带纹理的多边形必须要设置贴图url、pixelScale)
 * @param {vec3[]}						option.points 											多边形各个点坐标(点序必须是顺时针或者逆时针)
 * @param {COLOR}						[option.color = COLOR('rgb(255,255,255)').alpha(1))] 	多边形颜色(颜色中的alpha值无效)
 * @param {String}						[option.url]											多边形贴图地址(支持“http://”地址和“file:///”格式)支持jpg、jpeg、bmp、tga、png图片格式
 * @param {Number}						[option.pixelScale = 10]								贴图像素点所占比例(贴图的一个像素点占实际图像的边长,eg:输入值为10,标识一个像素点代表实际图像边长10mm)
 * @param {Function}					option.callback											创建多边形结束回调
 * @param {NetworkError/DataError} 		option.callback.err    									加载过程出现错误时返回错误
 *                                  	err {NetworkError}     									贴图文件下载失败
 *										err {DataError}     									贴图数据出错
 *
 * @param {View3D.Node}						option.callback.node									创建成功返回模型节点
 */
NodeFactory.prototype.createPolygon = function(option) {
	Condicio.checkIsObject(option, "The type of option must be 'Object'!");
	ParamValidation.checkIsTypeArray(option.points, Matrix.glMatrix.ARRAY_TYPE, "The type of points must be 'Number Array' and valid!");
	Condicio.checkArgument(option.points.length >= 3, "The points can`t draw polygon! ");
	
	var defaultColor = COLOR('rgb(255,255,255)').alpha(1);
	var color = jQuery.extend({}, defaultColor, option.color);
	
	var url = option.url || "";
	var pixelScale = option.pixelScale || 10;
	
	var _this = this;
    var callbackWrapper = function (error, result) {
        if (error) {
            option.callback(error, null);
        }
        else
            option.callback(null, new Node(_this.session, result.nodeID, ""));
    };
	
	var callbackID = Utility.genGUID();
	this.session.registerCallback(callbackID, callbackWrapper, "createPolygon");
	
	var pointArray = [];
	for(var i = 0; i < option.points.length; ++i){
		var points = CvtForJson.cvtVec3(option.points[i]);
		pointArray.push(points);
	}
	
    var params = {
        points: pointArray,
		color: color.rgbaArray(),
		url: url,
		pixelScale: pixelScale,
		callbackID: callbackID
    };

    this.session.request(this.objectID, "createPolygon", params);
	
};
 
 /**
  * 创建Box
  * @param {Object}	  option												创建Box参数
  * @param {vec3[]}	  option.points									    	Box八个顶点(<a href="https://fulongtech.atlassian.net/wiki/spaces/RenderingTech/pages/83200479">详细请参考</a>)
  * @param {COLOR}	  [option.color = COLOR('rgb(255,255,255)').alpha(1))]	Box颜色(颜色中的alpha值无效)
  *
  * @return {View3D.Node}
  */
NodeFactory.prototype.createBox = function(option) {
	ParamValidation.checkIsTypeArray(option.points, Matrix.glMatrix.ARRAY_TYPE, "The type of points must be 'Number Array' and valid!");
	Condicio.checkArgument(option.points.length === 8, "The points must be 8 point!");
	
	var pointArray = CvtForJson.cvtVec3Array(option.points);
	
	var defaultColor = COLOR('rgb(255,255,255)').alpha(1);
	var color = jQuery.extend({}, defaultColor, option.color);
	
	var nodeID = Utility.genGUID();
	var params = {
		nodeID: nodeID,
		points: pointArray,
		color: color.rgbaArray()
	};
	
	this.session.request(this.objectID, "createBox", params);
	return new Node(this.session, nodeID, "");
};
 
 /**
  * 创建扇形旋转体(球体的一部分或者球)
  * 先创建出一个xz平面的扇形,然后旋转一定角度得到旋转体
  * @param {Object}		option													创建扇形旋转体参数
  * @param {Number}		option.length											扇形边长(毫米)
  * @param {Number}		option.sectorAngle										扇形角度
  * @param {Number}		option.rotateAngle										扇形旋转的角度
  * @param {vec3}		option.direction										扇形旋转体的朝向(圆心到曲面中心点的方向)
  * @param {COLOR}		[option.color = COLOR('rgb(255,255,255)').alpha(1)]		扇形旋转体颜色(颜色中的alpha值无效)
  *
  * @return {View3D.Node}
  */
NodeFactory.prototype.createSectorRevolution = function(option) {
	Condicio.checkIsNumber(option.sectorAngle, "The type of sectorAngle must be Number! ");
	Condicio.checkIsNumber(option.rotateAngle, "The type of rotateAngle must be Number! ");
	Condicio.checkIsNumber(option.length,"The type of length must be Number! ");
	Condicio.checkArgument(option.length > 0, "The length must greater than '0'! ");
	
	var defaultColor = COLOR('rgb(255,255,255)').alpha(1);
	var color = jQuery.extend({}, defaultColor, option.color);
	
	var directionArray = CvtForJson.cvtVec3(option.direction);
	var nodeID = Utility.genGUID();
	var params = {
		nodeID:nodeID,
		length:option.length,
		sectorAngle:option.sectorAngle,
		rotateAngle:option.rotateAngle,
		color:color.rgbaArray(),
		direction:directionArray
	};
	
	this.session.request(this.objectID, "createSectorRevolution", params);
	return new Node(this.session, nodeID, "");
};
 
 /**
  * 创建圆柱体
  * <a href="https://fulongtech.atlassian.net/wiki/spaces/RenderingTech/pages/83200479">详细请参考</a>
  * @param {Object}		option													创建圆柱参数
  * @param {vec3}		option.bottomCenter										圆柱体底面中心
  * @param {Number}		option.bottomRadius										圆柱体底面半径
  * @param {vec3}		option.bottomNormal										圆柱体底面法向
  * @param {vec3}		option.topCenter										圆柱体顶面中心
  * @param {Number}		option.topRadius										圆柱体顶面半径
  * @param {vec3}		option.topNormal										圆柱体顶面法向
  *	@param {color}		[option.color = COLOR('rgb(255,255,255)').alpha(1)]		圆柱体颜色(颜色中的alpha值无效)
  *
  * @return {View3D.Node}		
  */
NodeFactory.prototype.createCylinder = function(option) {
	ParamValidation.checkIsVec3(option.bottomCenter, "The bottomCenter type must be vec3! ");
	ParamValidation.checkIsVec3(option.bottomNormal, "The bottomNormal type must be vec3! ");
	ParamValidation.checkIsVec3(option.topCenter, "The topCenter type must be vec3! ");
	ParamValidation.checkIsVec3(option.topNormal, "The topNormal type must be vec3! ");
	Condicio.checkIsNumber(option.bottomRadius, "The bottomRadius type must be Number! ");
	Condicio.checkIsNumber(option.topRadius, "The topRadius type must be Number! ");
	Condicio.checkArgument(option.bottomRadius >= 0, "bottomRadius must greater than or equal to 0! ");
	Condicio.checkArgument(option.topRadius >= 0, "topRadius must greater than or equal to 0! ");

	var bottomCenter = CvtForJson.cvtVec3(option.bottomCenter);
	var bottomNormal = CvtForJson.cvtVec3(option.bottomNormal);;
	var topCenter = CvtForJson.cvtVec3(option.topCenter);;
	var topNormal = CvtForJson.cvtVec3(option.topNormal);;


	var defaultColor = COLOR('rgb(255,255,255)').alpha(1);
	var color = jQuery.extend({}, defaultColor, option.color);

	var nodeID = Utility.genGUID();
	var params = {
		nodeID:nodeID,
		bottomCenter:bottomCenter,
		bottomRadius:option.bottomRadius,
		bottomNormal:bottomNormal,
		topCenter:topCenter,
		topRadius:option.topRadius,
		topNormal:topNormal,
		color:color.rgbaArray()
	};

	this.session.request(this.objectID, "createCylinder", params);
	return new Node(this.session, nodeID, "");
};

/**
  * 创建圆形断面圆环
  * <a href="https://fulongtech.atlassian.net/wiki/spaces/RenderingTech/pages/83200479">详细请参考</a>
  * @param {Object}		option													创建圆环参数
  * @param {vec3}		option.pt1												起始圆形断面的中心
  * @param {Number}		option.radius1											起始圆形断面的半径(>0)
  * @param {vec3}		option.pt2												截止圆形断面的中心
  * @param {Number}		option.radius2											截止圆形断面的半径(>0)
  * @param {vec3}		option.ptCenter											圆环整体中心
  * @param {Number}		option.direction										圆环的方向
  * 																			Direction=0,表示小圆环,从P1到P2的小角度
  *																				Direction=1,表示大圆环,从P1到P2的大角度
  *																				Direction=2,表示整个圆环,此时,P2表示圆环法向量,此时R2无效,以R1为准
  *																				Direction=3,表示半个圆环,此时,P2表示圆环法向量,此时R2无效,以R1为准
  *	@param {color}		[option.color = COLOR('rgb(255,255,255)').alpha(1)]		圆环颜色(颜色中的alpha值无效)
  *
  * @return {View3D.Node}		
  */
NodeFactory.prototype.createTorus = function(option) {
	ParamValidation.checkIsVec3(option.pt1, "The option.pt1 type must be vec3! ");
	Condicio.checkIsNumber(option.radius1, "The option.radius1 type must be Number! ");
	Condicio.checkArgument(option.radius1 > 0, "The option.radius1 type must be greater than 0! ");
	ParamValidation.checkIsVec3(option.pt2, "The option.pt2 type must be vec3! ");
	var subOut = Matrix.vec3.create();
	Condicio.checkArgument(Matrix.vec3.length(Matrix.vec3.sub(subOut, option.pt1, option.pt2)) > 0
	, "The option.pt2 must not equal to option.pt1! ");
	Condicio.checkIsNumber(option.radius2, "The option.radius2 type must be Number! ");
	Condicio.checkArgument(option.radius2 > 0, "The option.radius2 type must be greater than 0! ");
	ParamValidation.checkIsVec3(option.ptCenter, "The option.ptCenter type must be vec3! ");
	Condicio.checkIsNumber(option.direction, "The option.direction type must be Number! ");
	Condicio.checkArgument(option.direction === 0 || option.direction === 1 || option.direction === 2||option.direction === 3
		, "The  type of option.direction must be valid!");

	var pt1 = CvtForJson.cvtVec3(option.pt1);
	var pt2 = CvtForJson.cvtVec3(option.pt2);
	var ptCenter = CvtForJson.cvtVec3(option.ptCenter);
	
	var defaultColor = COLOR('rgb(255,255,255)').alpha(1);
	var color = jQuery.extend({}, defaultColor, option.color);

	var nodeID = Utility.genGUID();
	var params = {
		nodeID:nodeID,
		pt1: pt1,
		radius1: option.radius1,
		pt2: pt2,
		radius2: option.radius2,
		ptCenter: ptCenter,
		direction: option.direction,
		color: color.rgbaArray()
	};

	this.session.request(this.objectID, "createTorus", params);
	return new Node(this.session, nodeID, "");
};

/**  
 * 创建某个根节点(根节点必须是DrawingRootNode)下排除某一部分节点后的其它节点
 *
 * 返回的node仅用于createColorChangeEffect、createHideEffect、createTransparencyEffect这三种effect中,若用于其他接口中不保证结果;
 *
 * @param {Node}  	root						根节点
 * @param {Node[]} [excludes]					需要从根节点中排除的一组子节点
 *
 * @return {View3D.Node}
 */
NodeFactory.prototype.createExclusionNode = function(root, excludes) {
	Condicio.checkIsType(root, Node, "The type of root must be 'Node'");
	ParamValidation.checkIsTypeArray(excludes, Node, "The type of excludes must be 'Node Array' and valid");
	
	var excludeIDs = CvtForJson.cvtNodeArray(excludes);
	
	var nodeID = Utility.genGUID();
	var params = {
		nodeID: nodeID,
		excludeIDs: excludeIDs,
		rootNodeID: root.objectID
	};
    this.session.request(this.objectID, "createExclusionNode", params);
	
	return new Node(this.session, nodeID, "");
};

/**
  * 创建流动效果
  * <a href="https://fulongtech.atlassian.net/wiki/spaces/RenderingTech/pages/1504084703">详细请参考</a>
  * @param {Object}		 option						 创建流动效果参数
  * @param {vec3[]} 	 option.vertexArray          流动的路线,由一组点组成,流经管道的中心点     	                            							
  * @param {Number} 	 option.radius               水流的半径(要比管道半径小一些,推荐管道半径 * 0.8 即可)
  * @param {Number} 	 option.speed                流动的速度  单位 m/s
  * @param {string} 	 option.textureImageName     纹理图片的name, 放在BRS的Resources文件夹下,支持jpg\png\dds\bmp格式
  * 
  * @param {Function} 	 option.callback             创建节点的回调
  * @param {Error}       option.callback.err	     创建节点的回调参数,内部异常,如果没有异常则为null
  * @param {Node}        option.callback.node	     创建节点的回调参数,返回创建的节点
  * 
  * @param {Function} 	 option.stateCallback        流动状态的回调
  * @param {Error}       option.stateCallback.err	 流动状态回调参数,内部异常,如果没有异常则为null
  * @param {vec3}        option.stateCallback.pos	 流动状态回调参数,当前流到的位置
  * @param {boolean}     option.stateCallback.flowing   流动状态回调参数,是否正在流动,流动为true,流满为false
  */
 NodeFactory.prototype.createFlowingModel = function(option) {

	ParamValidation.checkIsVec3Array(option.vertexArray, "The option.vertexArray type must be vec3Array! ");
	Condicio.checkArgument(option.vertexArray.length > 1, "The option.vertexArray.size must be greater than 1 ! ");
	Condicio.checkIsNumber(option.radius, "The option.radius type must be Number! ");
	Condicio.checkArgument(option.radius > 0, "The option.radius must be greater than 0! ");
	Condicio.checkIsNumber(option.speed, "The option.speed type must be Number! ");
	Condicio.checkArgument(option.speed > 0, "The option.speed must be greater than 0! ");
	Condicio.checkIsString(option.textureImageName, "The option.textureImageName type must be String!");
	Condicio.checkIsFunction(option.stateCallback, "The type of stateCallback must be 'Function'!");
	Condicio.checkIsFunction(option.callback, "The type of callback must be 'Function'!");

    var callbackWrapper = function (error, result) {
        if (error) {
            option.callback(error, null);
        }
        else {
			option.callback(null, new Node(this.session, result.nodeID, ""));
		}
    };

	var callbackID = Utility.genGUID();
	this.session.registerCallback(callbackID, callbackWrapper, "createFlowingModel", false);

	var stateCallbackID = Utility.genGUID();
	this.session.registerCallback(stateCallbackID, option.stateCallback, "createFlowingModel", true);

	var nodeID = Utility.genGUID();
	var vecs = CvtForJson.cvtVec3Array(option.vertexArray);

	var params = {
		nodeID : nodeID,
		vertexArray : vecs, 
		radius : option.radius,
		speed : option.speed,
		textureImageName: option.textureImageName,
		callbackID: callbackID,
		stateCallbackID: stateCallbackID
	};

	this.session.request(this.objectID, "createFlowingModel", params);  
};

/**
 * 创建轴
 * @param {Object}				option										创建轴的参数
 * @param {vec3}				option.position								轴的创建位置
 * @param {vec3}				option.direction							轴的创建方向
 * @param {Function} 			option.pickCallback                			创建轴的回调
 * @param {Error}       		option.pickCallback.err						轴点击回调参数,内部异常,如果没有异常则为null
 * @param {vec3}        		option.pickCallback.direction				轴点击回调参数,方向为当前轴所代表的方向
 * @param {Object} 				[option.axisStyle] 							轴属性,参照View3D.Style.AxisStyle的定义
 *
 * @return {View3D.Node}	
 */
NodeFactory.prototype.createAxis = function (option) {
	var defaultStyle = {
		axisStyle: Style.AxisStyle
	};
	
	var newOption = jQuery.extend(true,{}, defaultStyle, option);

	Condicio.checkIsObject(newOption, "The type of option must be 'Object'!");
	ParamValidation.checkIsVec3(newOption.position, "The position type must be vec3! ");
	ParamValidation.checkIsVec3(newOption.direction, "The direction type must be vec3! ");
	Condicio.checkIsFunction(newOption.pickCallback,"The type of pickCallback must be 'Function'!");
	Condicio.checkIsObject(newOption.axisStyle, "The type of axisStyle must be 'Object'!");
	ParamValidation.checkIsAxisStyle(newOption.axisStyle);
	
	newOption.axisStyle.color = newOption.axisStyle.color.rgbaArray();
	newOption.axisStyle.pickedColor = newOption.axisStyle.pickedColor.rgbaArray();
	var style = newOption.axisStyle;
	var position = CvtForJson.cvtVec3(newOption.position);
	var direction = CvtForJson.cvtVec3(newOption.direction);
	
    var pickCallbackWrapper = function (error, result) {
        if (error) {
            option.pickCallback(error, null);
        }
        else{
			option.pickCallback(null, CvtForJson.cvtToVec3(result.direction));
		}
    };
	
	var pickCallbackID = Utility.genGUID();
	this.session.registerCallback(pickCallbackID, pickCallbackWrapper, "createAxis", true);
    var nodeID = Utility.genGUID();
	
    var params = {
		nodeID: nodeID,
		position: position,
		direction: direction,
		callbackID: pickCallbackID,
		axisStyle: style,
    };

	this.session.request(this.objectID, "createAxis", params);
    return new Node(this.session, nodeID, "");
};

module.exports = NodeFactory;