# client.js

    /**
     * @module Client
     * @author roymond.wang
     * @since 2018-08-30
     * @desc Client模块, 信令系统的入口。
     *
     *
     * @example
     * var Clinet = ModuleBase.use(ModulesEnum.client);
     */

    if(!log){
       var log = log4javascript.getLogger();
    }

    if(!bugout){
    	var bugout = null;
    }

    var signalClient = null;
    var Task_Id = 0; //每次自增长

    var rtmVersion = "1.0.0.0";

    ModuleBase.define("Client", ["RestServer","RtmManager","RtmServer","ClientUser","InviteManager","RemoteControlManager","ChatManager","RpcManager","Error"], function(RestServer,RtmManager,RtmServer,ClientUser,InviteManager,RemoteControlManager,ChatManager,RpcManager,Error) {

    	var Client = function(serverURI,accessToken) {
    		/**
             * @desc Client级别回调的枚举
             * @readonly
             * @enum {string}
             */
    		this.ClientCallbackEnum = {
    			NATIVE_PAYLOAD_NOTIFY: 'native.payload',
    		};

    		this.serverURI = serverURI;
    		this.accessToken = accessToken;

    		this.mqttWSAddr = null;

    		this.restServer =  new RestServer();
    		this.rtmManager = null;
    		this.rtmServer = null;
    		this.inviteManager = null;
    		this.remoteControlManager = null;
    		this.chatManager = null;
    		this.rpcManager = null;
    		this.selfUser = null;

    		this.eventEmitter = new EventEmitter();

    		this.topicReservedWords = ['/u/','/g/','/s'];

    		signalClient = this;
    	};

    	/**
    	 * @desc 获取rtm client的版本号
    	 * @returns {String} - 版本号
    	 *
    	 * @example
    	 * var Version = avdEngine.getVersion();
    	 */
    	Client.prototype.getVersion = function() {
    		return "rtm-" + rtmVersion;
    	};

    	/**
    	 * @desc 设置日志显示方式和日志级别
    	 * @param {Appender} appenderModel - 日志显示方式(枚举型), 默认值为Appender.browserConsole
    	 * @param {LogLevel} logLevel - 日志级别(枚举型),默认值为LogLevel.error
    	 *
    	 * @example
    	 * avdEngine.setLog(Appender.browserConsole, LogLevel.debug);
    	 */
    	Client.prototype.setLog = function(appenderModel, logLevel) {
    		var appender;
    		if(appenderModel == Appender.alert) {
    			appender = new log4javascript.AlertAppender();
    		} else if(appenderModel == Appender.inpage) {
    			appender = new log4javascript.InPageAppender();
    		} else if(appenderModel == Appender.popup) {
    			appender = new log4javascript.PopUpAppender();
    		} else if(appenderModel == Appender.browserConsole) {
    			appender = new log4javascript.BrowserConsoleAppender();
    		}

    		if(logLevel == LogLevel.all) {
    			appender.setThreshold(log4javascript.Level.ALL);
    		} else if(logLevel == LogLevel.trace) {
    			appender.setThreshold(log4javascript.Level.TRACE);
    		} else if(logLevel == LogLevel.debug) {
    			appender.setThreshold(log4javascript.Level.DEBUG);
    		} else if(logLevel == LogLevel.info) {
    			appender.setThreshold(log4javascript.Level.INFO);
    		} else if(logLevel == LogLevel.warn) {
    			appender.setThreshold(log4javascript.Level.WARN);
    		} else if(logLevel == LogLevel.error) {
    			appender.setThreshold(log4javascript.Level.ERROR);
    		} else if(logLevel == LogLevel.fatal) {
    			appender.setThreshold(log4javascript.Level.FATAL);
    		} else if(logLevel == LogLevel.off) {
    			appender.setThreshold(log4javascript.Level.OFF);
    		}
    		appenderLayout(appender);
    	};

    	function appenderLayout(appender) {
    		var popUpLayout = new log4javascript.PatternLayout("%d{yyyy-MM-dd HH:mm:ss SSS} %-5p - %m{1}%n");
    		appender.setLayout(popUpLayout);
    		log.addAppender(appender);
    	};

    	/**
    	 * @desc 用户登陆
    	 * @async
    	 * @param {String} account  -登陆帐号
    	 * @param {String} password -登陆密码 - 可以不填
    	 */
       Client.prototype.login = function(account,password) {
           	  var deferred = when.defer();
    		  var self = this;
    		  self.restServer.getMqttWSAddr(self.serverURI,self.accessToken).then(function(mqttWSAddr){
    				log.info("===client.login(),restServer.getMqttWSAddr(), mqttWSAddr:",mqttWSAddr);
    				self.mqttWSAddr = mqttWSAddr;
    				self.rtmManager = new RtmManager(self.mqttWSAddr,account,password);
    				self.rtmManager.connect().then(function(rtmClient){
    					   signalClient.rtmServer = new RtmServer(rtmClient);
    					   signalClient.rtmServer.subscribe("/u/" + account);

    					   var clientUser = new ClientUser(account,password);
    					   self.selfUser = clientUser;

    					   deferred.resolve();
    				}).otherwise(function(error){
    					   deferred.reject(error);
    				});
    	 	   }).otherwise(function(error){
    	 	   	    log.error("===client.login(),error:",error);
    	 	   	    deferred.reject(error);
    	 	   });

            return deferred.promise;
    	};

    	/**
    	 * @desc 用户登出
    	 * @async
    	 * @param {int} reson -登出原因
    	 */
    	Client.prototype.logout = function(reson) {
    		  var deferred = when.defer();
    		  var self = this;
    		  try{
    		  	  this.rtmServer.end();
    		      signalClient = null;
    		      self.selfUser = null;
    		  	  deferred.resolve();
    		  }catch(error){
    		  	  deferred.reject(error);
    		  }
              return deferred.promise;
    	};

    	/**
    	 * @desc 获取邀请管理类
    	 * @returns {InviteManager} - inviteManager 邀请管理类
    	 */
    	Client.prototype.getInviteManager = function(){
    		if(! this.inviteManager){
    		    this.inviteManager = new InviteManager();
    		}
    	    return this.inviteManager;
    	};

    	/**
    	 * @desc 获取远程管理类
    	 * @returns {RemoteControlManager} - remoteControlManager 远程管理类
    	 */
    	Client.prototype.getRemoteControlManager = function(){
    		if(! this.remoteControlManager){
    		    this.remoteControlManager = new RemoteControlManager();
    		}
    	    return this.remoteControlManager;
    	};

    	/**
    	 * @desc 获取聊天管理类
    	 * @returns {ChatManager} - chatManager 聊天管理类
    	 */
    	Client.prototype.getChatManager = function(){
    		if(! this.chatManager){
    		    this.chatManager = new ChatManager();
    		}
    	    return this.chatManager;
    	};

    	/**
    	 * @desc 获取RPC管理类
    	 * @returns {RpcManager} - rpcManager RPC管理类
    	 */
    	Client.prototype.getRpcManager = function(){
    		if(! this.rpcManager){
    		    this.rpcManager = new RpcManager();
    		}
    	    return this.rpcManager;
    	};

    	Client.prototype.getServeTime = function(){
    		var dt = new Date;
    		dt.setMinutes(dt.getMinutes() + dt.getTimezoneOffset()); // 当前时间(分钟) + 时区偏移(分钟)
    		var timestamp = dt.getTime(); //格林尼治时间戳(毫秒数)
    		timestamp = Math.round(timestamp / 1000); //精确到秒
    		return timestamp;
    	};

    	/**
    	 * @desc 没有封闭，原生的MQTT消息发布
    	 * @async
    	 * @param {Object} topic -主题
    	 * @param {Object} payload -内容
    	 */
        Client.prototype.nativePublish = function(topic,payload){
        	 var deferred = when.defer();
    		 var isExit = arrayUtil.contains(this.topicReservedWords, topic);
    		 if(isExit){
    		 	   var nativePublishError = new Error(ErrorConstant.mqtt_topic_reservedword_error);
    		 	   deferred.reject(nativePublishError);
    		 }else{
    		 	  this.rtmServer.publish(topic,payload);
    		 	   deferred.resolve();
    		 }
             return deferred.promise;
        };

         /**
          * @desc 没有封闭，原生的MQTT消息订阅
    	  * @async
          * @param {Object} topic-主题
          */
         Client.prototype.nativeSubscribe = function(topic){
         	 var deferred = when.defer();
    		 var isExit = arrayUtil.contains(this.topicReservedWords, topic);
    		 if(isExit){
    		 	   var nativePublishError = new Error(ErrorConstant.mqtt_topic_reservedword_error);
    		 	   deferred.reject(nativePublishError);
    		 }else{
         	      this.rtmServer.subscribe(topic);
         	       deferred.resolve();
    		 }
             return deferred.promise;
         };

         /**
          * @desc 获取主题的保留字。如果原生的MQTT消息发布，主题中含有保留字会抛错
          */
         Client.prototype.getTopicReservedWords = function(){
         	 return this.topicReservedWords;
         };

    	/**
         * @desc Client级别的回调
         * @param {ClientCallback} type - 回调枚举标识
         * @param {Object} callback -回调方法名,可以自定义
         *
         */
    	Client.prototype.addCallback = function(type, callback) {
    	    this.eventEmitter.on(type, callback);
    	};

    	Client.prototype.offCallback = function(types) {
    	   for(var key in types) {
               var type = types[key];
    	   	   this.eventEmitter.off(type);
    	   }
    	};

    	return Client;
    });