/**
 * Created by TalhaAkkas on 05/05/15.
 */

var net = require('net');
var os = require('os');
var TIME_OUT = 100 * 1000;

//function getLocalIP(){
//	var ifaces = os.networkInterfaces();
//	var ip = undefined;
//	Object.keys(ifaces).forEach(function (ifname) {
//		var alias = 0;
//		ifaces[ifname].forEach(function (iface) {
//			if ('IPv4' !== iface.family || iface.internal !== false) {
//				// skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses
//				return;
//			}
//			ip = iface.address;
//		});
//	});
//	return ip;
//}
function assert(condition, message) {
	if (!condition) {
		throw message || "Assertion failed";
	}
}
function MessageServer(clusters, listener, options) {
	assert(options && options.ip && options.port && options.host);
	function createKey(ip, port){
		return ip + ":" + port;
	}
	this.isItMe = function(ip, port){
		return createKey(this.ip, this.port) == createKey(ip, port);
	};
	this.connectToServer = function(ip, port){
		if(this.isItMe(ip,port)){
			return;
		}
		var client = net.connect({ip: ip, port: port},
			function() { //'connect' listener
				console.log('connected to server!');
				this.onSocketConnected(client);
			});
	};
	this.onSocketConnected = function(sock){
		sock.messageServer = this;
		sock.on('data', function (data) {
			try {
				this.messageServer.onMessage(data, this);
			}catch (e) {
				console.log(e);
			}
		});
		sock.on('close', function (data) {
			try {
				this.messageServer.onSocketClose(this);
			}catch (e) {
				console.log(e);
			}
		});
		sock.on('error', function (error) {
			try {
				this.messageServer.onSocketClose(this);
			}catch (e) {
				console.log(e);
			}
		});
		sock.setTimeout(TIME_OUT, function (error) {
			try {
				this.messageServer.onSocketClose(this);
			}catch (e) {
				console.log(e);
			}
		});
		sock.sendAck = function(){
			this.write({type:"Ack"});
		};
		sock.write({type:"GetInfo"});
		setInterval(function(){sock.sendAck()}, TIME_OUT - 100);
	};
	this.sendMessage = function(message, ip, port){
		if(this.isItMe(ip, port)) {return;}
		if(!this.messageQueues[createKey(ip, port)]){
			this.messageQueues[createKey(ip, port)] = [];
		}
		this.messageQueues[createKey(ip, port)].push(message);
		this.checkMessages();
	};
	this.checkMessages = function (){
		for(var ip in this.messageQueues){
			var queue = this.messageQueues[ip];
			var sock = this.connections[ip];
			if(queue.length > 0 && sock == undefined){
				this.connectToServer(ip);
			}else if(queue.length > 0){
				while(queue.length){
					var message = queue.shift();
					sock.write(message);
				}
			}
		}
	};
	this.onInfo = function(data, sock){
		sock.info = data;
		this.connections[data.ip] = sock;
		this.checkMessages();
	};
	this.onGetInfo = function(data, sock){
		var info = {type:"Info", ip: getLocalIP()};
		sock.write(info);
		return true;
	};
	this.onAck = function(){
		return true;
	};

	this.onMessage = function(data, sock){
		try{
			//check socket validation;
			//if(sock.info && sock.info.ip &&
			//	this.connections[sock.info.ip] &&
			//	this.connections[sock.info.ip] == sock){
			//
			//}

			// now it is safe to process data;
			var object = data;

			if(this.validateMesage(object)){
				if(this["on" + object.type] &&
					this["on" + object.type](object, sock)){
					return;
				}
				if(this.listener && this.listener['on' + object.type] &&
					this.listener['on' + object.type](object.data, sock)){
					return;
				}
			}


		}catch(e){
			console.log(e);
		}
	};
	this.validateMesage = function(object){
		return object.type != undefined && object.type != null;
	};
	this.options = options;
	this.ip = options.ip;
	this.port =  options.port;
	this.messageQueues = {};
	this.listener = listener;
	this.server = {};
	this.connections = {};
	this.clusters = clusters;
	var connections = {};
	this.listen = function () {
		this.server = net.createServer(this.onSocketConnected);
		this.server.messageServer = this;
		this.server.listen(this.options.port, this.options.host);
	}
}
module.exports = MessageServer;