exports.ChannelClient = ChannelClient;
var util = require("util");
var events = require("events");
var coder = require("../coder");
var translate = require("../translate");
var chan_msg = require("./channel_message");
const STATE_INITED = 0;
const STATE_OPENING = 1;
const STATE_OPENED = 2;
const STATE_REOPENING = 3;
const STATE_CLOSED = 4;

const CHANNEL_CODE_SUCCESS  = 0
const CHANNEL_CODE_AUTH_FAIL  = 1
const CHANNEL_CODE_CHANNEL_CLOSED = 2
const CHANNEL_CODE_CHANNEL_OPEND = 3
const CHANNEL_CODE_CHANNEL_PROTOCOL_ERROR = 4

function ChannelClient(id,ip,port,pwd,version){
	this.id_ = id;
	this.ip_ = ip;
	this.pwd_ = pwd;
	this.port_ = port;
	this.session_ = 0;
	this.tcp_client_ = null;
	this.state_ = STATE_INITED;
	this.recv_term_ = 0;

	this.start_index_ = 0;
	this.send_list_ = [];
	this.send_index_ = 0;

	this.timer_ = null;
	this.flush_timer_ = null;

	this.open_times_ = 0;
	this.reopen_times_ = 0;
	this.timeout_index_ = 0;
	this.timeout_timer_ = null;

	this.bflushed_ = false;

	this.need_back_ = false;

	this.ping_ = 0;
	this.version_ = version;

	this.start_emit_msg_ = false;
	this.cache_recv_msg_ = [];
}

util.inherits(ChannelClient, events.EventEmitter);


ChannelClient.prototype.Start = function(){
	if(this.state_ != STATE_INITED){
		return false;
	}
	this.state_ = STATE_OPENING;
	this._Connect();
	this.timeout_timer_ = setInterval(this._Timer.bind(this),1000);
}
ChannelClient.prototype._Timer = function(){
	//this._SendMessageBack();
	this.timeout_index_++;
	if(this.timeout_index_ > 5){
		console.log("timeout.re.open");
		this._ReOpen("timeout");
	}
	if(this.timeout_index_ >= 15){
		this._EmitCLose("timeout");
	}

}
ChannelClient.prototype.GetPing = function(){
	return this.ping_;
}
ChannelClient.prototype.StartEmitMessage = function(){
	if(!this.start_emit_msg_){
		this.start_emit_msg_ = true;
		for(var i in this.cache_recv_msg_){
			this.emit("message",this.cache_recv_msg_[i]);
		}
		this.cache_recv_msg_ = null;
	}
}
ChannelClient.prototype.Stop = function(){
	if(this.state_ == STATE_INITED || this.state_ == STATE_CLOSED){
		return false;
	}
	this.state_ = STATE_CLOSED;
	clearTimeout(this.flush_timer_);
	clearTimeout(this.timer_ );
	clearInterval(this.timeout_timer_);
	
	if(this.tcp_client_){
		var stop = new chan_msg.ChannelMessageClose();
		this.tcp_client_.Send(stop);
		this.tcp_client_.Close();
		this.tcp_client_ = null;
	}
	
}
ChannelClient.prototype._Connect = function(){
	var self = this;
	this.tcp_client_ = new translate.TcpClient();
	this.tcp_client_.Connect(this.ip_,this.port_);
	this.tcp_client_.on("openfail",function(err){
		self._OnClose(err);
	})
	this.tcp_client_.on("open",function(){
		self._OnOpen();
	})
	this.tcp_client_.on("message",function(msg){
		self._OnMessage(msg);
	})
	this.tcp_client_.on("close",function(err){
		self._OnClose(err);
	})
}
ChannelClient.prototype._OnOpen = function (){
	if(this.state_ == STATE_OPENING){
		var msg = new chan_msg.ChannelMessageOpen(this.id_,this.pwd_,this.version_);
		this.tcp_client_.Send(msg);	
	}else if(this.state_ == STATE_REOPENING){
		var msg = new chan_msg.ChannelMessageReOpen(this.session_,this.recv_term_,this.id_);
		this.tcp_client_.Send(msg);
	}else{
		this.tcp_client_.Close();
	}
}
ChannelClient.prototype._OnClose = function (err){
	clearTimeout(this.flush_timer_);
	clearTimeout(this.timer_ );
	this.tcp_client_ = null;
	if(this.state_ == STATE_OPENING){
		this.open_times_ ++;
		if(this.open_times_ >= 5){
			this._EmitCLose(err);
			return;
		}
		this.timer_ = setTimeout(this._Connect.bind(this),500);
	}else if(this.state_ == STATE_OPENED){
		this.state_ = STATE_REOPENING;
		this.timer_ = process.nextTick(this._Connect.bind(this));
	}else if(this.state_ == STATE_REOPENING){
		this.timer_ = setTimeout(this._Connect.bind(this),500);
	}
}
ChannelClient.prototype._SendMessageBack = function(){
	if(this.need_back_) {
		this.need_back_ = false;
		if(this.tcp_client_){
			var data = new chan_msg.ChannelMessageDataBack(this.recv_term_);
			this.tcp_client_.Send(data);
		}
	}
	

}
ChannelClient.prototype._ReOpen = function(err){
	if(this.tcp_client_){
		this.tcp_client_.removeAllListeners();
		this.tcp_client_.Close();
		this.tcp_client_ = null;
	}
	
	this._OnClose(err);
}
ChannelClient.prototype._OnMessage = function (msg){
	this.timeout_index_ = 0;
	var tag = msg.GetTag();
	if(tag == coder.TAG.ADCLOUD_MESSAGE_TAG_CHANNEL_OPENBACK){
		if(this.state_ == STATE_OPENING){
			if(msg.code == CHANNEL_CODE_SUCCESS){
				this.state_ = STATE_OPENED;
				this.session_ = msg.session;
				this.emit("open");
				this.StartFlush();
				return;
			}
			this._EmitCLose("open.fail.code="+msg.code,+" desc:"+msg.desc);
		}else{
			console.log("state.fail.at.open.back",this.state_);
		}
	}else if(tag == coder.TAG.ADCLOUD_MESSAGE_TAG_CHANNEL_REOPEN_BACK){
		if(this.state_ == STATE_REOPENING){
			if(msg.code == CHANNEL_CODE_SUCCESS){
				this.state_ = STATE_OPENED;
				this.ClearCache(msg.term);
				this.send_index_ = this.start_index_ = 0;
				this.recv_term_ = 0;
				this.StartFlush();
				console.log("reopen.success.at ",msg.term);
				return;
			}else{
				console.log("reopen.fail",msg.code);
				this._EmitCLose("reopen.fail.code="+msg.code);
			}
		}else{
			console.log("state.fail.at.reopen.back",this.state_);
		}
	}else if(tag == coder.TAG.ADCLOUD_MESSAGE_TAG_CHANNEL_MESSAGE){
		if(this.state_ == STATE_OPENED){
			this.recv_term_ = msg.term;
			this.need_back_ = true;
			this._SendMessageBack();
			if(this.start_emit_msg_){
				for(var i in msg.arr){
					this.emit("message",msg.arr[i]);
				}
			}else{
				this.cache_recv_msg_ = this.cache_recv_msg_.concat(msg.arr);
			}
			
		}else{
			console.log("state.fail.at.message",this.state_);
		}
	}else if(tag == coder.TAG.ADCLOUD_MESSAGE_TAG_CHANNEL_MESSAGE_BACK){
		if(this.state_ == STATE_OPENED){
			this.ClearCache(msg.term);
		}
	}else if(tag == coder.TAG.ADCLOUD_MESSAGE_TAG_CHANNEL_KEEP_ALIVE){
		this.ping_ = msg.ping;
		if( this.tcp_client_ )this.tcp_client_.Send(msg);
	}
}
ChannelClient.prototype.StartFlush = function(){
	this.bflushed_ = true;
	process.nextTick(this._Flush.bind(this));
}
ChannelClient.prototype.StopFlush = function(){
	this.bflushed_ = false;
	clearTimeout(this.flush_timer_);
}
ChannelClient.prototype._EmitCLose = function(err){
	clearTimeout(this.flush_timer_);
	clearTimeout(this.timer_ );
	clearInterval(this.timeout_timer_);
	if(this.tcp_client_)
		this.tcp_client_.Close();
	this.tcp_client_ = null;
	if(this.state_ != STATE_CLOSED){
		this.state_ == STATE_CLOSED;
		this.emit("close",err);	
	}

}
ChannelClient.prototype._Flush = function(){
	if(!this.tcp_client_) return;
	if(!this.bflushed_) return;
	var arr = [];
	var begin = this.send_index_ - this.start_index_;
	var num = 0;
	for(var i=begin;i<this.send_list_.length && num <= 2048;i++){
		arr.push(this.send_list_[i]);
		num++;
	}
	this.send_index_+=num;
	if(num > 0){
		var msg = new chan_msg.ChannelMessageData(this.send_index_,arr);
		this.tcp_client_.Send(msg);
		if(num >= 200){
			process.nextTick(this._Flush.bind(this));
		}else{
			this.flush_timer_ = setTimeout(this._Flush.bind(this),2);
		}
	}else{
		this.flush_timer_ = setTimeout(this._Flush.bind(this),2);
	}
}
ChannelClient.prototype.ClearCache = function(term){
	if(term > this.send_index_){
		term = this.send_index_;
	}
	this.send_list_.splice(0,term - this.start_index_);
	this.start_index_ = term
}

ChannelClient.prototype.PushMessage = function(msg){
	if(this.state_ == STATE_OPENED || this.state_ == STATE_REOPENING){
			this.send_list_.push(msg);
			return true;
	}
	return false;
}