#include "empire.h"
#include "netlobby.h"

void refresh_maplist();
void clear_gamelist();

string MOTD;
string connectmsg;
vector<listedgame *> gamelist;
vector<string> playerlist;

string connectaddr = "";  //the address of the host to connect to

int net_lobby()
{
	static short since_update;
	static bool first = true, waiting_for_update;
	static _button hostgame, back, up, down, games[6];
	static unsigned int gamelist_offset;
	static _listbox playerbox, chatbox;
	static _textline chattext;
	string chatstring;
	DyBytes indata;
	
	if (first)
	{
		char c[256];
		
		back.init(10, 575, 100, 20, "Back");
		hostgame.init(680, 565, 110, 30, "Host Game", 1);
		up.init(570, 50, 50, 84, -1);  //just using the button class' mouse functions, don't draw anything
		down.init(570, 300, 50, 84, -1);
		games[0].init(20,                             50,                     listedgame::w, listedgame::h, -1);
		games[1].init(20 + listedgame::w + 5,         50,                     listedgame::w, listedgame::h, -1);
		games[2].init(20 + (2 * (listedgame::w + 5)), 50,                     listedgame::w, listedgame::h, -1);
		games[3].init(20,                             50 + listedgame::h + 5, listedgame::w, listedgame::h, -1);
		games[4].init(20 + listedgame::w + 5,         50 + listedgame::h + 5, listedgame::w, listedgame::h, -1);
		games[5].init(20 + (2 * (listedgame::w + 5)), 50 + listedgame::h + 5, listedgame::w, listedgame::h, -1);
		
		playerbox.init(635, 40, 160, 360);
		chatbox.init(10, 420, 620, 130);
		chattext.init(10, 552, 620, 13, true, 250);
		
		for (unsigned int i = 0; i < playerlist.size(); i++)
			playerbox.additem(playerlist[i].c_str());
		playerbox.sort();
		
		sprintf(c, "Your version of Trichromic is %s", TRICHROMIC_VERSION);
		chatbox.additem(c);
		chatbox.additem("");
		
		chatbox.additem(MOTD.c_str());
		
		if (global.orignetname == "Default")
		{
			chatbox.additem("You still have the default multiplayer name.  Might want to change that.");
		}
		
		if (connectmsg != "")
		{  //if we have a message from the networking init
			chatbox.additem(connectmsg.c_str());
			connectmsg = "";
		}
		
		chatbox.additem("");
		chatbox.scroll_to_end();
		
		gamelist_offset = 0;
		since_update = 0;
		waiting_for_update = false;
		
		first = false;
	}
	
	buffer_textout_ex(big, "Available games", 10, 10, WHITE);
	buffer_textout_right_ex(big, "Players in Lobby", 800, 10, WHITE);
	buffer_rectrounded(10, 40, 630, 400, LGREY);
	buffer_rectrounded(15, 45, 625, 395, BLACK);
	
	back.check();
	if (key[KEY_ESC] || back.clicked())
	{
		if (net.lobbyengine->isConnected(net.lobbythread))
			net.lobbyengine->disconnectHost(net.lobbythread);
		
		net.unload();
		clear_gamelist();
		
		first = true;
		return NET_QUIT;
	}
	
	if (!net.lobbyengine->isConnected(net.lobbythread))
	{  //if the lobby connection is lost, show this error message, the "back" button, the mouse, and nothing else
		buffer_textout_centre_ex(big, "Lost connection to server", 320, 200, RED);
		clear_gamelist();
		chatbox.check(false);
		draw_mouse();
		return NET_LOBBY;
	}

	hostgame.check();
	playerbox.check(false);  //don't make lines in the player box selectable

	buffer_textout_ex(normal, "Lobby Chat", 15, 405, WHITE);
	chatbox.check(false);  //don't make lines in the chatbox selectable either
	chatstring = chattext.check();
	if ((chatstring != "/NOTHING YET/") && (chatstring.size() > 0))
	{
		DyBytes data;
		addID(data, CHAT);
		data.appendString(global.netname);
		data.appendString(": ");
		data.appendString(chatstring);
		net.lobbyengine->sendTCP(net.lobbythread, data);
	}
	
	if (net.lobbyengine->getMessage(net.lobbythread, indata))
	{
		string id = getID(indata);
		if (id == SERVER_CHAT)
		{
			chatbox.additem(indata.getAsString().c_str());
			chatbox.scroll_to_end();
		}
		else if (id == SERVER_LOBBY_DATA)
		{
			if (indata.decompress())
				get_lobby_data(indata);
			
			playerbox.clear();
			for (unsigned int i = 0; i < playerlist.size(); i++)
				playerbox.additem(playerlist[i].c_str());
			playerbox.sort();
			
			waiting_for_update = false;
		}
		else if (id == SERVER_NEWNAME)
		{
			string s = "There is already someone on the server with your name.  You are now "; 
			global.netname = indata.getAsString();
			s += global.netname + ".";
			chatbox.additem(s.c_str());
			chatbox.scroll_to_end();
		}
		else if (id == SERVER_WANTS_NAME)
		{
			DyBytes data;
			addID(data, CLIENT_NAME);
			data.appendString(global.orignetname);
			net.lobbyengine->sendTCP(net.lobbythread, data);
		}
		else if (id == SERVER_WANTS_VERSION)
		{
			DyBytes data;
			addID(data, CLIENT_VERSION);
			data.appendString(TRICHROMIC_VERSION);
			net.lobbyengine->sendTCP(net.lobbythread, data);
		}
	}
	
	for (unsigned int i = gamelist_offset * 6; ((i < (gamelist_offset + 1) * 6) && (i < gamelist.size())); i++)
	{
		int n, row, col;
		
		n = i % 6;
		row = n / 3;
		col = n % 3;
		
		gamelist[i]->draw(20 + (col * (listedgame::w + 5)), 50 + (row * (listedgame::h + 5)));
		
		if (games[n].mouseover())
			buffer_rect(games[n].x, games[n].y, games[n].x + games[n].w, games[n].y + games[n].h, WHITE);
		
		if (games[n].clicked())
		{
			DyBytes data;
			addID(data, JOINING_GAME);
			net.lobbyengine->sendTCP(net.lobbythread, data);
			
			net.server = false;
			connectaddr = gamelist[i]->get_address();
			clear_gamelist();
			first = true;
			return NET_HOST_CONNECT;
		}
	}
	
	if (gamelist.size() > 6)
	{  //more than 6 games, draw and check the up/down arrows
		if (gamelist_offset > 0)
		{
			glb.draw(56, up.x, up.y);
			if (up.mouseover())
				buffer_rect(up.x, up.y, up.x + up.w, up.y + up.h, WHITE, 0.8);

			if (up.clicked())
				gamelist_offset--;
		}
		else glb.draw(56, up.x, up.y, 0.5);
		
		if (gamelist.size() > (gamelist_offset * 6) + 6)
		{
			glb.draw(57, down.x, down.y);
			if (down.mouseover())
				buffer_rect(down.x, down.y, down.x + down.w, down.y + down.h, WHITE, 0.8);

			if (down.clicked())
				gamelist_offset++;
		}
		else glb.draw(57, down.x, down.y, 0.5);
	}
	
	if (hostgame.clicked())
	{
		refresh_maplist();
		if (maplist.size() < 1)
		{
			chatbox.additem("Can't host game.  You don't have any maps!");
			chatbox.scroll_to_end();
		}
		else if ((!net.engine->enableTCPListen(41136)) || (!net.chatengine->enableTCPListen(41137)))
		{
			chatbox.additem("Can't host game.  Couldn't start listening on ports 41136/41137");
			chatbox.scroll_to_end();
			empdebug("Error listening on port 41136/41137.  DaSyne message: " + net.engine->getErrMsg());
		}
		else
		{
			net.server = true;
			clear_gamelist();
			first = true;
			return NET_HOST;
		}
	}
	
	since_update++;
	if (((since_update > 100) && !waiting_for_update) || (since_update > 750))
	{  //more than 2 seconds since the last update and we're not waiting for an update we asked for already OR we've been waiting for 15 seconds  
		DyBytes data;
		uint16_t size;
		uint8_t namelen;
		uint32_t crc;
		addID(data, QUERY_LOBBY_DATA);
		
		size = gamelist.size();
		data.appendVariable(size);
		
		for (unsigned int i = 0; i < gamelist.size(); i++)
		{
			DyBytes gamedata;
			
			namelen = gamelist[i]->get_name().length();
			data.appendVariable(namelen);
			data.appendString(gamelist[i]->get_name());
			
			gamelist[i]->append_to(gamedata);
			crc = gamedata.crc32();
			data.appendVariable(crc);
		}
		
		net.lobbyengine->sendTCP(net.lobbythread, data);
		
		since_update = 0;
		waiting_for_update = true;
	}

	draw_mouse();
	
	return NET_LOBBY;
}

void clear_gamelist()
{
	for (unsigned int i = 0; i < gamelist.size(); i++)
	{
		delete gamelist[i];
		gamelist[i] = NULL;
	}
	gamelist.clear();
}

void get_lobby_data(DyBytes &indata)
{
	string s;
	uint16_t num;
	uint8_t len, changed;
	
	vector<listedgame*> newlist;
	
	if (indata.size() < 2)
		return;
	
	indata.extractVariable(0, num);
	indata.removeFront(2);
	for (unsigned int i = 0; i < num; i++)
	{
		if (indata.size() < 1)
			break;
		
		indata.extractVariable(0, changed);
		indata.removeFront(1);
		
		if (changed)
		{
			listedgame *l = new listedgame;
			
			if (l->get_from(indata))
				newlist.push_back(l);
		}
		else
		{  //server said the game was unchanged since last time
			DyBytes subdata;
			string name;
			
			if (indata.size() < 1)
				break;
			
			indata.extractVariable(0, len);
			indata.removeFront(1);
			
			if (indata.size() < len)
				break;
			
			for (unsigned int j = 0; j < len; j++)
				subdata.appendByte(indata.getByte(j));
			indata.removeFront(len);
			
			name = subdata.getAsString();
			
			vector<listedgame*>::iterator it = gamelist.begin();
			while (it != gamelist.end())
			{
				if ((*it)->get_name() == name)
				{  //if this is the listedgame that hasn't changed, move it to newlist
					newlist.push_back(*it);
					it = gamelist.erase(it);  //and remove it from gamelist (without deleting the memory of that listedgame)
					break;
				} else it++;
			}
		}
	}
	
	clear_gamelist();  //delete and free the memory of any games left in gamelist
	gamelist = newlist;
	
	//-------
	
	if (indata.size() < 2) return;
	
	playerlist.clear();
	indata.extractVariable(0, num);
	indata.removeFront(2);
	
	for (unsigned int i = 0; i < num; i++)
	{
		DyBytes subdata;
		
		if (indata.size() < 1)
			break;
		indata.extractVariable(0, len);
		indata.removeFront(1);
		
		if (indata.size() < len)
			break;
		
		for (unsigned int j = 0; j < len; j++)
			subdata.appendByte(indata.getByte(j));
		indata.removeFront(len);
		
		s = subdata.getAsString();
		playerlist.push_back(s);
	}
}

int net_connect_to_host()
{
	static bool first = true, dataerr, chaterr, dataconnected, chatconnected;
	static _button cancel;
	
	if (first)
	{
		dataerr = false;
		chaterr = false;
		dataconnected = false;
		chatconnected = false;
		
		cancel.init(360, 300, 80, 20, "Cancel");
		
		net.engine->connectToHost(connectaddr, 41136);
		net.chatengine->connectToHost(connectaddr, 41137);
		
		first = false;
	}
	
	glb.draw(54, 0, 0);
	cancel.check();
	
	if (cancel.clicked())
	{
		if (net.engine->isConnected(net.thread))
			net.engine->disconnectHost(net.thread);
		else if (!dataerr)
			net.engine->cancelConnection();
		
		if (net.chatengine->isConnected(net.chatthread))
			net.chatengine->disconnectHost(net.chatthread);
		else if (!chaterr)
			net.chatengine->cancelConnection();
		
		first = true;
		
		if (net.lobbyengine->isConnected(net.lobbythread))
		{
			DyBytes data;
			
			addID(data, BACK_IN_LOBBY);
			net.lobbyengine->sendTCP(net.lobbythread, data);
			
			data.clear();
			addID(data, QUERY_LOBBY_DATA);
			net.lobbyengine->sendTCP(net.lobbythread, data);
			
			return NET_LOBBY;
		}
		
		return NET_FIRST;
	}
	
	if (!dataerr && !dataconnected)
	{
		int temp;
		int status = net.engine->checkConnection(temp);
		
		buffer_textout_centre_ex(big, "Connecting to host...", 400, 200, WHITE);
		
		if (status == DY_NOTOPEN) dataerr = true;
		else if (status >= 0) dataconnected = true;
	}
	else if (dataerr)
	{
		buffer_rectrounded(200, 190, 600, 260, RED, 0.7, 5);
		buffer_textout_centre_ex(big, "Error connecting to host", 400, 200, WHITE);
		buffer_textout_centre_ex(normal, net.engine->getErrMsg(), 400, 230, WHITE);
	}
	
	if (!chaterr && !chatconnected)
	{
		int temp;
		int status = net.chatengine->checkConnection(temp);
		
		if (status == DY_NOTOPEN) chaterr = true;
		else if (status >= 0) chatconnected = true;
	}
	else if (chaterr && !dataerr)  //don't display two error messages, just one will do
	{
		buffer_rectrounded(200, 190, 600, 260, RED, 0.7, 5);
		buffer_textout_centre_ex(big, "Error connecting to host", 400, 200, WHITE);
		buffer_textout_centre_ex(normal, net.chatengine->getErrMsg(), 400, 230, WHITE);
	}
	
	if (dataconnected && chatconnected)
	{
		connectaddr = "";
		first = true;
		return NET_CLIENT;
	}
	
	draw_mouse();
	
	return NET_HOST_CONNECT;
}
