#!/usr/bin/env node

var ws_module = require('ws');
var net = require('net');

var argv = require('optimist')
			.usage('Proxies TCP connections for Minecraft Servers to WebSockets.')
			.demand('websocket')
			.describe('websocket','port to host websocket server on, this will be the port that web clients use to join the server.')
			.demand('minecraft')
			.describe('minecraft','address to your Minecraft Server (e.g. localhost:25565)').argv;

function initSocketCallbacks(state,ws,s) {

	function flushSocketBuffer() {
		if(state.sBuffer.length > 0) {
			s.write(Buffer.concat(state.sBuffer));
		}
		state.sBuffer = null;
	};

	function flushWebsocketBuffer() {
		if(state.wsBuffer.length > 0) {
			ws.send(Buffer.concat(state.wsBuffer),{binary: true,mask: false});
		}
		state.wsBuffer = null;
	};

	s.on('close', function(had_error) {
		ws.removeAllListeners('close');		
		ws.close();
	});

	ws.on('close', function() {
		s.removeAllListeners('close');	
		s.destroy();
	});

	ws.on('error', function (e) {
		console.log('websocket error');
		console.log(e);
		ws.removeAllListeners('close');
		s.removeAllListeners('close');
		ws.close();
		s.end();
	});

	s.on('error', function (e) {
		console.log('socket error');
		console.log(e);
		ws.removeAllListeners('close');
		s.removeAllListeners('close');
		ws.close();
		s.end();
	});

	s.on('connect', function() {
		state.sReady = true;
		flushSocketBuffer();
	});

	ws.on('open', function () {
		state.wsReady = true;
		flushWebsocketBuffer();
	});

	s.on('data', function(data) {

		if(! state.wsReady) {
			state.wsBuffer.push(data);
		} else {
			ws.send(data,{binary: true,mask: false});
		}
	});

	ws.on('message', function(m,flags) {
		if(!state.sReady) {
			state.sBuffer.push(m);
		} else {
			s.write(m);			
		}
	});
}

function ws2tcp() {

	console.log('proxy mode ws -> tcp');
	console.log('forwarding port ' + argv.websocket + ' to ' + argv.minecraft);

	wss = new ws_module.Server({port: argv.websocket});
	wss.on('connection', function(ws) {
		var addr_port = argv.minecraft.split(':');
		var s = net.connect(addr_port[1],addr_port[0]);
 		s.setNoDelay(true);
		var state = {
			sReady : false,
			wsReady : true,
			wsBuffer: [],
			sBuffer : []
		};
		initSocketCallbacks(state,ws,s);
	});

}

ws2tcp();
