"use strict";

var _ = require("lodash");
var Condicio = require("condicio");

var View3D = require("./View3D/View3D.js"),
	ViewPID = require("./ViewPID/ViewPID.js"),
    Session = require("./Session.js"),
    Viewpoint = require("./View3D/Viewpoint.js"),
	Utility = require("./Utility.js"),
	NetworkProtocol = require("./NetworkProtocol.js"),
	semver = require("semver"),
	ParamValidation = require("./ParamValidation.js"),
	RoamerFactory = require("./View3D/RoamerFactory.js"),
	Error = require("./Error.js"),
	jQuery = require("jquery"),
	Debug = require("./Debug.js");
var NetworkError = Error.NetworkError;
var InvalidArgumentError = Error.InvalidArgumentError;
var VersioningError = Error.VersioningError;


/**
 * @class Factory
 *
 * 创建PID和三维显示窗口
 *
 * 应用不应该构建,应该通过createFactory创建得到
 *
 */
function Factory(session, enableSSL, proxyUri) {
    this.session = session;
    this.objectID = "Factory";
	this.enableSSL = enableSSL;
	this.proxyUri = proxyUri;
};

/**
 * 释放窗口资源
 *
 * @param {View3D\ViewPID[]} views 待释放的窗口资源集合
 */
Factory.prototype.releaseView = function (views) {
	var objectIDs = [];
    for (var i = 0; i < views.length; ++i) {
		if(Condicio.isType(views[i], View3D) || Condicio.isType(views[i], ViewPID)){
			views[i].release();
			objectIDs.push(views[i].objectID);
		}
		else{
			throw "The type of views awaiting release must be 'View3D' or 'ViewPID'";
		}
    }

    this.session.request(this.objectID, "releaseView",  { objectIDs: objectIDs });
};

/**
 * <a href="https://fulongtech.atlassian.net/wiki/spaces/RenderingTech/pages/1148354569/5.8.0">参数具体信息说明</a>
 * 创建3D显示窗口
 * @param {HTMLElement} 	elem 		  						父元素
 * @param {Function} 		callback 	  						创建完成后回调
 * @param {Error} 			callback.err  						创建失败返回错误
 * @param {View3D.View3D} 	callback.view3D 					创建成功后返回显示窗口
 * @param {Number} 			mode								三维传输模式
 * 																0: Compatible表示兼容模式(兼容IE,chrome) 
 *																1: DataSaver表示省流模式,带宽占用低(只支持chrome)
 * @param {Number} 			[option]							3D显示窗口配置参数
 * @param {Number} 			[option.maxFPS = 20]				最大帧率
 * @param {Number} 			[option.LODScale = 1] 				可控制场景中模型的加载数量
 * @param {Number} 			[option.maxPagedLODNumber = 500] 	最大加载PagedLOD数量
 * @param {Number} 			[option.enableTransparency = false] 模型颜色带透明位的是否显示为不透明
 * @param {Number} 			[option.loadDurMoving = true] 		场景移动过程中是否允许加载模型
 * @param {Number} 			[option.maxFrameTime = 0.1] 		渲染一帧允许的最大时间
 * @param {Number} 			[option.computeNearFarMode = 0] 	计算远近平面的方式
 * 																0: PrecisionMode表示精准模式
 *																1: FastMode表示快速模式
 *                                                              FastMode可以解决某些视角下漫游会卡的问题,但可能导致模型闪面
 */
Factory.prototype.createView3D = function (elem, callback, mode, option) {
	Condicio.checkNotNull(elem,"HTMLElement parameter cannot be null");
    Condicio.checkNotUndefined(elem, "HTMLElement parameter cannot be undefined");
    Condicio.checkIsFunction(callback, "The type of callback must be 'Function'!");

	mode = mode || 0;
	Condicio.checkIsNumber(mode, "The type of mode must be 'Number'!");
	Condicio.checkArgument(mode == 1 || mode == 0, "The mode must: 0 or 1");

	var callbackID = Utility.genGUID();
	var _this = this;
	var callbackWrapper = function (error, result) {
		if (!error){
			var callbackResult = function (err, view3D) {
				if(err)
					callback(err, null);
				else{
					var roamer1 = view3D.RoamerFactory.createFreeModeRoamer();
					view3D.swapRoamer(roamer1);
					callback(null, view3D);
				}
			}
			new View3D(_this.session, elem, result, mode, _this.enableSSL, _this.proxyUri, callbackResult);
		}
		else
			callback(error, null);
	};
	this.session.registerCallback(callbackID, callbackWrapper, "createView3D");

	var defaultConfig = {
		maxFPS : 20,
		LODScale : 1,
		maxPagedLODNumber : 500,
		enableTransparency : false,
		loadDurMoving : true,
		maxFrameTime : 0.1,
 		computeNearFarMode : 0,
	};
	var newConfig = jQuery.extend({}, defaultConfig, option);
	ParamValidation.checkIsView3DConfig(newConfig);

	var params ={
		callbackID: callbackID,
		mode : mode,
		config : newConfig
	};
	
    this.session.request(this.objectID, "createView3D", params);
};



/**
 * 创建PID显示窗口
 *
 * @param {HTMLElement} elem 		        父元素
 * @param {Function} 	callback 	        创建完成后回调
 * @param {Error} 		callback.err        创建失败返回错误
 * @param {ViewPID.ViewPID} 	callback.viewPID    创建成功后返回显示窗口
 */
Factory.prototype.createViewPID = function (elem, callback) {
	Condicio.checkNotNull(elem,"HTMLElement parameter cannot be null");
    Condicio.checkNotUndefined(elem, "HTMLElement parameter cannot be undefined");
    Condicio.checkIsFunction(callback, "The type of callback must be 'Function'!");

	var callbackID = Utility.genGUID();
	var _this = this;
	var callbackWrapper = function (error, result) {
		if (!error)
			callback(null, new ViewPID(_this.session, elem, result.viewID, _this.enableSSL, _this.proxyUri));
		else
			callback(error, null);
	};
	
	this.session.registerCallback(callbackID, callbackWrapper, "createViewPID");
    this.session.request(this.objectID, "createViewPID", {callbackID: callbackID });
};

var hasProxy = function(options, recvPack){
	if(options.server.ip !== "localhost"
		&&(options.server.ip !== recvPack.body.result.ip  
		|| options.server.port !== recvPack.body.result.port)){
		return true;
	}
	
	return false;
};

/**
 * @member Global.createFactory
 * 创建服务器组件工厂
 *
 * @param {Object} 		options							创建工厂参数
 * @param {Object} 		options.server					服务器连接信息
 * @param {Boolean}		[options.server.useSSL=false]	与服务器通信是否使用加密协议(HTTPS),默认为HTTP
 * @param {String}		options.server.ip				服务器IP地址
 * @param {String}		options.server.port				服务器连接端口号
 * @param {String}		[options.server.userName]		当前登录的用户名(BRS下载文件需要用户验证时需填)
 * @param {String}		[options.server.language]		当前系统语言,默认中文
 *														(中文:zh_CN,英文:en_US,输入非中文默认都是英文)
 * @param {Function} 	options.errorHandler			创建完成以后,服务器运行过程发生错误或者连接意外断开回调
 * @param {Error} 		options.errorHandler.err 	  	发生错误时候传出错误
 *						err{NetworkError}				网络断开
 * 						err{InvalidArgumentError} 		调用方法时候,请求的某些对象不存在、请求的方法没有定义
 * @param {Function} 	callback 						创建完成回调
 * @param {Error}		callback.err 					创建失败返回错误
 *						err{NetworkError}				网络连接失败
 *						err{VersioningError} 			版本协商失败
 *						err{SystemError}				启动渲染服务失败
 * @param {Factory} 	callback.factory				创建成功返回工厂
 */
function createFactory(options, callback) {
    Condicio.checkIsObject(options, "The type of options must be 'Object'!");
    Condicio.checkIsObject(options.server, "The type of server must be 'Object'!");
    Condicio.checkIsString(options.server.ip, "The type of server ip must be 'String'!");
    Condicio.checkIsString(options.server.port, "The type of server port must be 'String'!");
    Condicio.checkIsFunction(options.errorHandler, "The type of error-callbck must be 'Function'!");
    Condicio.checkIsFunction(callback, "The type of complete-callback must be 'Function'!");
	
	var defaultProtocol = "ws://";
	options.server.useSSL = options.server.useSSL || false;
	if(options.server.useSSL){
		defaultProtocol = "wss://";
	}
	
	options.server.userName = options.server.userName || "";
	Condicio.checkIsString(options.server.userName, "The type of server userName must be 'String'!");
	
	options.server.language = options.server.language || "zh_CN";
	Condicio.checkIsString(options.server.language, "The type of server language must be 'String'!");
	
	var listenerUrl = defaultProtocol + options.server.ip + ":" + options.server.port + "/brs" 
						+ "?vicVersion=" + NetworkProtocol.VersionNumber.CurrentVIC 
						+ "&supportBRSMinimumVersion=" + NetworkProtocol.VersionNumber.SupportBRSMinimum
						+ "&userName=" + options.server.userName
						+ "&language=" + options.server.language;
	var ws = new WebSocket(listenerUrl);
	
	ws.onmessage = function(ev){
		var recvPack = new NetworkProtocol.Package;
		recvPack.fromString(ev.data);
		
		switch (recvPack.type) {
			case NetworkProtocol.PackageType.SessionReady: {
				var proxyUri = "";	//用于之后图片请求是否使用代理服务器
				if(hasProxy(options, recvPack)){
					proxyUri = recvPack.body.result.ip + ":" + recvPack.body.result.port;
				}
				
				var rendererID = recvPack.body.result.rendererID;
				var session = new Session(ws, options.server.ip, options.server.port, options.errorHandler, rendererID);
				callback(null, new Factory(session, options.server.useSSL, proxyUri));
				break;
			}
			case NetworkProtocol.PackageType.Error: {
				callback(Error.genError(recvPack.body.error));	
				break;
			}
		}
	};
	
	ws.onclose = function(){
		callback(new NetworkError("Can't connect server, please check if the server address is correct!"));	
	};
};

/**
 * @member Global.releaseFactory
 * 释放服务器组件工厂
 * 
 * @param {Factory[]} factorys 需要释放的工厂集合
 */
function releaseFactory(factorys) {
	for (var i = 0; i < factorys.length; ++i) {
		if(Condicio.isType(factorys[i], Factory)){
			factorys[i].session.close();
		}
		else{
			throw "The type of factorys awaiting release must be 'Factory'";
		}
    }
};

exports.createFactory = createFactory;
exports.releaseFactory = releaseFactory;
exports.Viewpoint = Viewpoint;
exports.Debug = Debug;