function Client (opts) {
	this.id = opts.id;
	this.opts = opts;
	this.send = send; 
	this.isConnected = false;
	let client = this;

	let queue = [];
	let callbacks = {};
	let isBusy = true;
	let socket;

	connect(opts);

	function send (event, data, callback) {
		if (!client.isConnected && callback)
			return callback(new Error('CONNECTION'));

		queue.push({event, data, callback});
		next();
	}

	function next() {
		if (isBusy)
			return;
		
		isBusy = true;

		if (queue.length == 0) 
			return (isBusy = false);

		if (socket) {
			let msg = {
				id: genId(),
				event: queue[0].event, 
				data: queue[0].data
			}

			socket.write((!!opts.cipher ? encode(JSON.stringify(msg), opts.cipher, opts.password) : JSON.stringify(msg)) + '|', function (err) {
				if (err)
					return socket.emit('error', err);

				if (queue[0].callback)		
					callbacks[msg.id] = queue[0].callback;

				client.emit('send', queue[0]);
				queue.shift(); 
				isBusy = false;
				next();
			});
		}	
	}
	
	function connect (opts) {
		socket = net.connect({host: opts.host, port: opts.port});
		queue = [];
		callbacks = {};
		isBusy = true;

		socket.on('connect', function () {
			isBusy = false;
			client.isConnected = true;
			client.emit('connect', opts.id);
		});

		let buffer = '';
		socket.on('data', function(chunk) {
			buffer += chunk;
			let msgs = buffer.split('|');
			buffer = msgs.pop();

			msgs.forEach(function(packet){

				let msg;
				try { 
					msg = JSON.parse(!!opts.cipher ? decode(packet, opts.cipher, opts.password) : packet);
				} catch(err) { 
					return socket.emit('error', err);
				}

				client.emit('receive', msg);
	
				if (msg.id in callbacks) {	
					callbacks[msg.id](msg.data);
					delete callbacks[msg.id];			
					return;
				}
				
				if (msg.event != 'ACK') {
					client.emit(msg.event, msg.data);
					send('ACK', {id: msg.id, event: msg.event});
					return;
				}
			});
		});	

		socket.on('close', () => {
			setTimeout(connect, opts['reconnect-delay'] * 1000 || 30000, opts);
			if (client.isConnected) {
				client.isConnected = false;
				client.emit('disconnect', opts.id);	
			}
			client.isConnected = false;
		});

		socket.on('error', (err) => client.emit('error', err));		
	}	
}
function Client (opts) {
	this.id = opts.id;
	this.opts = opts;
	this.send = send; 
	this.isConnected = false;
	let client = this;

	let queue = [];
	let callbacks = {};
	let isBusy = true;
	let socket;

	connect(opts);

	function send (event, data, callback) {
		if (!client.isConnected && callback)
			return callback(new Error('CONNECTION'));

		queue.push({event, data, callback});
		next();
	}

	function next() {
		if (isBusy)
			return;
		
		isBusy = true;

		if (queue.length == 0) 
			return (isBusy = false);

		if (socket) {
			let msg = {
				id: genId(),
				event: queue[0].event, 
				data: queue[0].data
			}

			socket.write((!!opts.cipher ? encode(JSON.stringify(msg), opts.cipher, opts.password) : JSON.stringify(msg)) + '|', function (err) {
				if (err)
					return socket.emit('error', err);

				if (queue[0].callback)		
					callbacks[msg.id] = queue[0].callback;

				client.emit('send', queue[0]);
				queue.shift(); 
				isBusy = false;
				next();
			});
		}	
	}
	
	function connect (opts) {
		socket = net.connect({host: opts.host, port: opts.port});
		queue = [];
		callbacks = {};
		isBusy = true;

		socket.on('connect', function () {
			isBusy = false;
			client.isConnected = true;
			client.emit('connect', opts.id);
		});

		let buffer = '';
		socket.on('data', function(chunk) {
			buffer += chunk;
			let msgs = buffer.split('|');
			buffer = msgs.pop();

			msgs.forEach(function(packet){

				let msg;
				try { 
					msg = JSON.parse(!!opts.cipher ? decode(packet, opts.cipher, opts.password) : packet);
				} catch(err) { 
					return socket.emit('error', err);
				}

				client.emit('receive', msg);
	
				if (msg.id in callbacks) {	
					callbacks[msg.id](msg.data);
					delete callbacks[msg.id];			
					return;
				}
				
				if (msg.event != 'ACK') {
					client.emit(msg.event, msg.data);
					send('ACK', {id: msg.id, event: msg.event});
					return;
				}
			});
		});	

		socket.on('close', () => {
			setTimeout(connect, opts['reconnect-delay'] * 1000 || 30000, opts);
			if (client.isConnected) {
				client.isConnected = false;
				client.emit('disconnect', opts.id);	
			}
			client.isConnected = false;
		});

		socket.on('error', (err) => client.emit('error', err));		
	}	
}
function Client ( opts ) { this . id = opts . id ; this . opts = opts ; this . send = send ; this . isConnected = false ; let client = this ; let queue = [ ] ; let callbacks = { } ; let isBusy = true ; let socket ; connect ( opts ) ; function send ( event , data , callback ) { if ( ! client . isConnected && callback ) return callback ( new Error ( 'CONNECTION' ) ) ; queue . push ( { event , data , callback } ) ; next ( ) ; } function next ( ) { if ( isBusy ) return ; isBusy = true ; if ( queue . length == 0 ) return ( isBusy = false ) ; if ( socket ) { let msg = { id : genId ( ) , event : queue [ 0 ] . event , data : queue [ 0 ] . data } socket . write ( ( ! ! opts . cipher ? encode ( JSON . stringify ( msg ) , opts . cipher , opts . password ) : JSON . stringify ( msg ) ) + '|' , function ( err ) { if ( err ) return socket . emit ( 'error' , err ) ; if ( queue [ 0 ] . callback ) callbacks [ msg . id ] = queue [ 0 ] . callback ; client . emit ( 'send' , queue [ 0 ] ) ; queue . shift ( ) ; isBusy = false ; next ( ) ; } ) ; } } function connect ( opts ) { socket = net . connect ( { host : opts . host , port : opts . port } ) ; queue = [ ] ; callbacks = { } ; isBusy = true ; socket . on ( 'connect' , function ( ) { isBusy = false ; client . isConnected = true ; client . emit ( 'connect' , opts . id ) ; } ) ; let buffer = '' ; socket . on ( 'data' , function ( chunk ) { buffer += chunk ; let msgs = buffer . split ( '|' ) ; buffer = msgs . pop ( ) ; msgs . forEach ( function ( packet ) { let msg ; try { msg = JSON . parse ( ! ! opts . cipher ? decode ( packet , opts . cipher , opts . password ) : packet ) ; } catch ( err ) { return socket . emit ( 'error' , err ) ; } client . emit ( 'receive' , msg ) ; if ( msg . id in callbacks ) { callbacks [ msg . id ] ( msg . data ) ; delete callbacks [ msg . id ] ; return ; } if ( msg . event != 'ACK' ) { client . emit ( msg . event , msg . data ) ; send ( 'ACK' , { id : msg . id , event : msg . event } ) ; return ; } } ) ; } ) ; socket . on ( 'close' , ( ) => { setTimeout ( connect , opts [ 'reconnect-delay' ] * 1000 || 30000 , opts ) ; if ( client . isConnected ) { client . isConnected = false ; client . emit ( 'disconnect' , opts . id ) ; } client . isConnected = false ; } ) ; socket . on ( 'error' , ( err ) => client . emit ( 'error' , err ) ) ; } }
opts = {host, port, cipher, password}
opts = { host port cipher password }