#include "empire.h"
#include "logic.h"

extern void get_battledata(DyBytes *data);
extern int unit_price(int type, int army);
bool valid_client_movepath(_unit *u, vector<int> movelist);
string netcommand(unsigned char byte);

void netplayer()
{
	static bool scroll = false;
	static int scrollx, scrolly;
	
	bool err = false;
	int i, k, x, y, z;
	DyBytes data;
	_unit *v, *w;
	vector<int> movelist;
	
	if (scroll)
	{
		if (scroll_to_location(scrollx, scrolly) == 1) scroll = false;
		else return;
	}  //if it's in the process of scrolling somewhere, don't read in any new data yet
	
	if (net.server)
	{
		for (int j = 0; j < 3; j++)
		{
			if (clientconnected(j))
			{
				DyBytes indata_copy;
				data.clear();
				bool newdata = get_client_message(j, data);
				indata_copy = data;
				
				//The server gets data from all clients, but only acts on the ones
				//from the client who's turn it is.  This prevents data from other
				//clients (who shouldn't be sending anything when it's not their
				//turn anyway) from building up a backlog of old data.
				
				if (newdata && j != game->pturn - 1)
				{  //if we received new data from this client, but it's not this client's turn
					char c[200];
					sprintf(c, "Received command %s from client %d, but it isn't that client's turn!", netcommand(data.getByte(0)).c_str(), j);
					empdebug(c);
					
					data.clear();
					data.appendByte(NET_OHNOYOUDONT);
					net.engine->sendTCP(j, data);
					continue;
				}
				else if (newdata)
				{  //if we received new data from this client, and it's the client's turn
					i = data.getByte(0);
					data.removeFront(1);
					empdebug("Host received data");
					empdebug(i);
					empdebug("from client");
					empdebug(j);
					switch(i)
					{
						case NET_CLIENT_ENDTURN:
							game->next_turn();
							
							data.clear();
							data.appendByte(NET_NEWTURN);
							data.appendByte(game->pturn);
							net.engine->broadcastTCP(data);
							break;
						case NET_CLIENT_NEWUNIT:
							if (player[game->pturn].units_in_play() < 50)
							{
								_player *p = &player[game->pturn];
								k = data.getByte(0);
								x = data.getByte(1);
								y = data.getByte(2);
								data.removeFront(3);
								
								{
									char c[200];
									sprintf(c, "Client %d asked to create a %s at (%d, %d)", j, unit_name(k).c_str(), x, y);
									empdebug(c);
								}
								
								if (p->cash >= unit_price(k, p->number))
								{
									if ((k >= 0) && (k < NUM_UNITS) && (moves_needed(unitstats[k].movetype, x, y, game->pturn) < 99))
									{
										if ((x >= 0) && (x < worldmap.l) && (y >= 0) && (y < worldmap.h))
										{
											if (any_unit_here(x, y) == -1)
											{
												p->cash -= unit_price(k, p->number);
												p->create_unit(k, x, y, game->pturn);
												
												p->stats.spent += unit_price(k, p->number);
												p->stats.produced++;
												
												update_cities();
												
												data.clear();
												data.appendByte(NET_NEWUNIT);
												data.appendByte(game->pturn);
												data.appendByte(k);
												data.appendByte(x);
												data.appendByte(y);
												data.appendVariable(p->cash);
												net.engine->broadcastTCP(data);
											}
											else
											{
												empdebug("ERROR: Client asked to create a unit, but there's a unit in that spot!");
												err = true;
											}
										}
										else
										{
											empdebug("ERROR: Client asked to create a unit, but gave off-map coordinates!");
											err = true;
										}
									}
									else
									{
										empdebug("ERROR: Client asked to create a unit, but the unit couldn't be built!");
										err = true;
									}
								}
								else
								{
									empdebug("ERROR: Client asked to create a unit, but didn't have enough cash!");
									err = true;
								}
							}
							else
							{
								empdebug("ERROR: Client asked to create a unit, but it has too many units in play!");
								err = true;
							}
							break;
						case NET_CLIENT_MOVEUNIT:
							x = data.getByte(0);
							y = data.getByte(1);
							data.removeFront(2);
							i = any_unit_here(x, y);
							if ((i != -1) && (i / 100 == game->pturn))
							{
								v = &player[i / 100].unit[i % 100];
								if ((v->exists == 1) && (v->canmove))
								{
									worldmap.create_limited_pathmap(game->pturn, unitstats[v->type].movetype, v->tilex, v->tiley, v->move + 1);
									k = data.getByte(0);
									data.removeFront(1);
									movelist.clear();
									x = 0;
									while (x < k)
									{
										movelist.push_back(data.getByte(x));
										x++;
									}
									if (valid_client_movepath(v, movelist))
									{
										game->unitselected = v;  //necessary for the unit_moving function
										play_moving_sound(v->type);
										v->set_moves(movelist);
										v->canmove = false;
										game->state = UNIT_MOVING;
										
										scroll = true;
										scrollx = v->tilex;
										scrolly = v->tiley;
										
										data.clear();
										data.appendByte(NET_MOVEUNIT);
										data.appendByte(v->tilex);
										data.appendByte(v->tiley);
										data.appendByte(movelist.size());
										x = 0;
										while ((unsigned)x < movelist.size())
										{
											data.appendByte(movelist[x]);
											x++;
										}
										net.engine->broadcastTCP(data);
									}
									else
									{
										empdebug("ERROR: Client wanted to move a unit, but the movelist was invalid!");
										err = true;
									}
								}
								else
								{
									empdebug("ERROR: Client wanted to move a unit, but the unit can't move or doesn't exist!");
									err = true;
								}
							}
							else
							{
								empdebug("ERROR: Client wanted to move a unit, but the unit wasn't there or belongs to someone else!");
								err = true;
							}
							break;
						case NET_CLIENT_STANDBY:
							i = data.getByte(1);
							if ((i >= 0) && (i < 50))
							{
								if (player[game->pturn].unit[i].exists == 1)
								{
									player[game->pturn].unit[i].ready = 0;
									
									data.clear();
									data.appendByte(NET_STANDBY);
									data.appendByte(game->pturn);
									data.appendByte(i);
									net.engine->broadcastTCP(data);
								}
							}  //standby doesn't really do anything, so even if the unit can't go into standby, don't bother with the errors
							break;
						case NET_CLIENT_DEPLOY:
							i = data.getByte(0);
							if ((i >= 0) && (i < 50))
							{
								v = &player[game->pturn].unit[i];
								if (unitstats[v->type].name == "Deployable Portal")
								{
									if (worldmap.tile[v->tilex][v->tiley].get_set() == 0)
									{  //if it's a land tile
										k = get_ltype(v->tilex, v->tiley);
										if ((k == 1) || (k == 6))
										{
											v->exists = 0;
											v->health = 0;
											worldmap.tile[v->tilex][v->tiley].set_unit(-1, -1);
											worldmap.tile[v->tilex][v->tiley].change_type(6, 0);
											game->unitselected = NULL;
											game->state = NOTHING_HAPPENING;
											create_particle(v->tilex * MAP_TILE_SIZE + MAP_TILE_SIZE / 2, v->tiley * MAP_TILE_SIZE + MAP_TILE_SIZE / 2, P_SHOCKWAVE);
											
											scroll = true;
											scrollx = v->tilex;
											scrolly = v->tiley;
											
											data.clear();
											data.appendByte(NET_DEPLOYPORTAL);
											data.appendByte(game->pturn);
											data.appendByte(i);
											net.engine->broadcastTCP(data);
										}
									}
								}
								else
								{
									empdebug("ERROR: Client wanted to deploy a portal, but the unit wasn't a Deployable Portal!");
									err = true;
								}
							}
							else
							{
								empdebug("ERROR: Client wanted to deploy a portal, but gave an out-of-range index!");
								err = true;
							}
							break;
						case NET_CLIENT_MECHCAP:
							i = data.getByte(0);
							if ((i >= 0) && (i < 50))
							{
								v = &player[game->pturn].unit[i];
								x = data.getByte(1);
								y = data.getByte(2);
								if ((x > 0) && (x < worldmap.l) && (y > 0) && (y < worldmap.h))
								{
									k = any_unit_here(x, y);
									if ((k != -1) && (player[k / 100].team != player[game->pturn].team))
									{
										i = player[k / 100].unit[k % 100].type;
										if ((unitstats[i].movetype != M_INFANTRY) && (unitstats[i].movetype != M_MECH))
										{
											if (unitstats[i].basetype != AIR)
											{
												mechanic_capture(v, x, y);
												
												scroll = true;
												scrollx = v->tilex;
												scrolly = v->tiley;
												
												data.clear();
												data.appendByte(NET_MECHANIC_CAPTURE);
												data.appendByte(game->pturn);
												data.appendByte(v->number);
												data.appendByte(x);
												data.appendByte(y);
												net.engine->broadcastTCP(data);
											}
											else
											{
												empdebug("ERROR: Client wanted to do a mechanic capture, but the enemy unit was an air unit!");
												err = true;
											}
										}
										else
										{
											empdebug("ERROR: Client wanted to do a mechanic capture, but the enemy unit was an infantry type!");
											err = true;
										}
									}
									else
									{
										empdebug("ERROR: Client wanted to do a mechanic capture, but there wasn't an enemy unit there!");
										err = true;
									}
								}
								else
								{
									empdebug("ERROR: Client wanted to do a mechanic capture, but gave off-map coordinates!");
									err = true;
								}
							}
							else
							{
								empdebug("ERROR: Client wanted to do a mechanic capture, but gave an out-of-range index!");
								err = true;
							}
							break;
						case NET_CLIENT_UNLOAD:
							z = any_unit_here(data.getByte(0), data.getByte(1));
							if ((z != -1) || (z / 100 != game->pturn))
							{
								v = &player[z / 100].unit[z % 100];
								k = data.getByte(2);
								if ((k == 0) || (k == 1))
								{
									x = data.getByte(3);
									y = data.getByte(4);
									if (unit_unloadable_here(v->tilex, v->tiley, x, y, v->load[k].type))
									{
										v->unload_unit(k, x, y);
										
										scroll = true;
										scrollx = v->tilex;
										scrolly = v->tiley;
										
										data.clear();
										data.appendByte(NET_UNLOAD);
										data.appendByte(v->tilex);
										data.appendByte(v->tiley);
										data.appendByte(k);
										data.appendByte(x);
										data.appendByte(y);
										net.engine->broadcastTCP(data);
									}
									else
									{
										empdebug("ERROR: Client wanted to unload a unit, but the unit wasn't unloadable here!");
										err = true;
									}
								}
								else
								{
									empdebug("ERROR: Client wanted to unload a unit, but gave a bad unload number!");
									err = true;
								}
							}
							else
							{
								empdebug("ERROR: Client wanted to unload a unit, but gave a tile with no unit or a unit it doesn't control!");
								err = true;
							}
							break;
						case NET_CLIENT_TELEPORT:
							i = data.getByte(0);
							if ((i >= 0) && (i < 50))
							{
								v = &player[game->pturn].unit[i];
								x = data.getByte(1);
								y = data.getByte(2);
								if ((x >= 0) && (x < worldmap.l) && (y >= 0) && (y < worldmap.h))
								{
									if ((worldmap.tile[x][y].same_type(6, 0)) && (worldmap.tile[v->tilex][v->tiley].same_type(6, 0)))
									{
										if (unitstats[v->type].basetype == LAND)
										{
											start_teleport_animation(v, x, y);
											
											data.clear();
											data.appendByte(NET_TELEPORT);
											data.appendByte(game->pturn);
											data.appendByte(i);
											data.appendByte(x);
											data.appendByte(y);
											net.engine->broadcastTCP(data);
										}
										else
										{
											empdebug("ERROR: Client wanted to teleport a unit, but it wasn't a land unit!");
											err = true;
										}
									}
									else
									{
										empdebug("ERROR: Client wanted to teleport a unit, but the tile isn't a teleporter!");
										err = true;
									}
								}
								else
								{
									empdebug("ERROR: Client wanted to teleport a unit, but gave off-map coordinates!");
									err = true;
								}
							}
							else
							{
								empdebug("ERROR: Client wanted to teleport a unit, but gave an out-of-range index!");
								err = true;
							}
							break;
						case NET_CLIENT_ATTACK:
							x = data.getByte(0);
							y = data.getByte(1);
							data.removeFront(2);
							z = any_unit_here(x, y);
							if ((z != -1) && (z / 100 == game->pturn))
							{
								v = &player[z / 100].unit[z % 100];
								if ((v->exists == 1) && (v->ready == 1))
								{
									x = data.getByte(0);
									y = data.getByte(1);
									data.removeFront(2);
									z = any_unit_here(x, y);
									if ((z != -1) && (z / 100 != game->pturn))
									{
										w = &player[z / 100].unit[z % 100];
										if (w->exists == 1)
										{
											if (player[v->owner].team != player[w->owner].team)
											{
												k = attackable_unit_here(v, w->tilex, w->tiley, true);
												if (k != -1)
												{  //scrolling to the battle is done in the battle function
													game->state = IN_BATTLE;
													game->attacker = v;
													game->defender = w;
													
													//sending battle data is done in the actual battle function
												}
												else
												{
													empdebug("ERROR: Client wanted to attack, but the attacker can't hit the defender!");
													err = true;
												}
											}
											else
											{
												empdebug("ERROR: Client wanted to attack, but the defender is on the same team!");
												err = true;
											}
										}
										else
										{
											empdebug("ERROR: Client wanted to attack, but the defender doesn't exist!");
											err = true;
										}
									}
									else
									{
										empdebug("ERROR: Client wanted to attack, but gave a tile with no defender!");
										err = true;
									}
								}
								else
								{
									empdebug("ERROR: Client wanted to attack, but the attacker isn't ready or doesn't exist!");
									err = true;
								}
							}
							else
							{
								empdebug("ERROR: Client wanted to attack, but gave a tile with no unit or a unit it doesn't control!");
								err = true;
							}
							break;
						case NET_CLIENT_SURRENDER:
							x = (signed char)data.getByte(0);
							if (player[game->pturn].playing == 1)
							{
								if ((x >= -1) && (x < 4))
								{
									if ((x == -1) || (player[x].playing == 1))
									{
										data.clear();
										data.appendByte(NET_PLAYER_SURRENDER);
										data.appendByte(game->pturn);
										data.appendByte(x);
										net.engine->broadcastTCP(data);

										do_surrender(game->pturn, x);
									}
									else
									{
										empdebug("Error: Client wanted to surrender to a player that isn't playing!");
										err = true;
									}
								}
								else
								{
									empdebug("ERROR: The client wanted to surrender, but gave an out-of-range index!");
									err = true;
								}
							}
							else
							{
								empdebug("ERROR: The client wanted to surrender, but is no longer playing!");
								err = true;
							}
							break;
					}
					if (err)
					{  //the client will be waiting for a response after sending the request
						data.clear();
						data.appendByte(NET_OHNOYOUDONT);  //so make sure the client knows its request has been DENIED
						data.append(indata_copy);
						net.engine->sendTCP(net.clientthread[j], data);
					}
				}  //end of "if j == pturn - 1 && data.size > 0"
			}  //end of "if clientconnected(j)"
		}
	}
	else
	{
		int j;
		
		if (get_host_message(data))
		{
			i = data.getByte(0);
			data.removeFront(1);
			empdebug("Received data:");
			empdebug(i);
			
			if (game->state == NET_WAITING_FOR_RESPONSE)
			{  //if this client was waiting for a response from the server, here it is
				game->state = NOTHING_HAPPENING;
			}
			
			switch(i)
			{
				case NET_PLAYERGONE:
					j = data.getByte(0);
					if (player[j].playing == 1)
					{
						player[j].playing = 0;
						transform_buildings(j, -1);
						game->state = PLAYER_DEFEATED;
						destroy_player_units(j);
					}
					break;
				case NET_NEWTURN:
					do_sidebar(0, NULL, false, true);  //reset sidebar
										
					game->pturn = data.getByte(0);
					game->new_turn = true;
					game->unitselected = NULL;
					game->state = NOTHING_HAPPENING;
					break;
				case NET_PLAYERCASH:
					data.extractVariable(1, player[data.getByte(0)].cash);
					break;
				case NET_NEWUNIT:
					j = data.getByte(0);
					k = data.getByte(1);
					x = data.getByte(2);
					y = data.getByte(3);
					
					z = player[j].create_unit(k, x, y, j);
					
					{
						char c[200];
						sprintf(c, "Server says: Create unit %s for player %d at (%d, %d)", unit_name(k).c_str(), j, x, y);
						empdebug(c);
					}
					
					if (z != -1)
					{
						player[j].stats.produced++;
						player[j].stats.spent += player[j].unit[z].price;
						player[j].cash -= player[j].unit[z].price;
					}
					else
					{
						empdebug("ERROR! The server said to create a new unit, but I couldn't!");
					}
					
					data.removeFront(4);
					data.extractVariable(0, player[j].cash);
					
					update_cities();
					break;
				case NET_MOVEUNIT:
					movelist.clear();
					x = data.getByte(0);
					y = data.getByte(1);
					data.removeFront(2);
					k = any_unit_here(x, y);
					v = &player[k / 100].unit[k % 100];
					k = data.getByte(0);
					data.removeFront(1);
					j = 0;
					while (j < k)
					{
						movelist.push_back(data.getByte(j));
						j++;
					}
					
					scroll = true;
					scrollx = v->tilex;
					scrolly = v->tiley;
					
					game->unitselected = v;  //necessary for the unit_moving function
					v->set_moves(movelist);
					v->canmove = false;
					game->state = UNIT_MOVING;
					play_moving_sound(v->type);
					break;
				case NET_STANDBY:
					j = data.getByte(0);
					k = data.getByte(1);
					player[j].unit[k].ready = 0;
					break;
				case NET_DEPLOYPORTAL:
					j = data.getByte(0);
					k = data.getByte(1);
					v = &player[j].unit[k];
					
					scroll = true;
					scrollx = v->tilex;
					scrolly = v->tiley;
					
					v->exists = 0;
					v->health = 0;
					worldmap.tile[v->tilex][v->tiley].set_unit(-1, -1);
					worldmap.tile[v->tilex][v->tiley].change_type(6, 0);
					create_particle(v->tilex * MAP_TILE_SIZE + MAP_TILE_SIZE / 2, v->tiley * MAP_TILE_SIZE + MAP_TILE_SIZE / 2, P_SHOCKWAVE);
					break;
				case NET_MECHANIC_CAPTURE:
					j = data.getByte(0);
					k = data.getByte(1);
					x = data.getByte(2);
					y = data.getByte(3);
					
					scroll = true;
					scrollx = player[j].unit[k].tilex;
					scrolly = player[j].unit[k].tiley;
					
					mechanic_capture(&player[j].unit[k], x, y);
					break;
				case NET_UNLOAD:
					j = data.getByte(0);
					k = data.getByte(1);
					z = any_unit_here(j, k);
					v = &player[z / 100].unit[z % 100];
					j = data.getByte(2);
					x = data.getByte(3);
					y = data.getByte(4);
					v->unload_unit(j, x, y);
					
					scroll = true;
					scrollx = v->tilex;
					scrolly = v->tiley;
					
					if (player[j].controller == HUMAN)
					{  //if it was this client that asked to unload a unit
						game->state = UNIT_SELECTED;
					}
					break;
				case NET_TELEPORT:
					j = data.getByte(0);
					k = data.getByte(1);
					x = data.getByte(2);
					y = data.getByte(3);
					start_teleport_animation(&player[j].unit[k], x, y);
					break;
				case NET_BATTLEDATA:
					game->state = IN_BATTLE;
					get_battledata(&data);
					empdebug("Entering battle");
					break;
				case NET_PLAYER_SURRENDER:
					x = (signed char)data.getByte(0);
					y = (signed char)data.getByte(1);
					do_surrender(x, y);
					break;
				case NET_OHNOYOUDONT:
					empdebug("ERROR!  Tried to do something, but the server wouldn't allow it!");
					{
						bool debugfirst = true;
						while (data.size() > 0)
						{
							if (debugfirst)
							{
								empdebug(netcommand(data.getByte(0)));
								debugfirst = false;
							}
							else
								empdebug(int(data.getByte(0)));
							
							data.removeFront(1);
						}
					}
					break;
				case NET_NEWCONTROLLER:
					empdebug("Received new controller message for player");
					x = data.getByte(0);
					y = data.getByte(1);
					empdebug(x);
					empdebug(y);
					player[x].controller = y;
					break;
			}
		}
	}
}

bool valid_client_movepath(_unit *u, vector<int> movelist)
{
	unsigned int i = 0;
	int x = u->tilex, y = u->tiley, z;
	
	while (i < movelist.size())
	{
		switch(movelist[i])
		{
			case UP:
				y--;
				break;
			case DOWN:
				y++;
				break;
			case LEFT:
				x--;
				break;
			case RIGHT:
				x++;
				break;
		}
		i++;
	}
	
	z = any_unit_here(x, y);
	if (z == -1) return true;
	if (can_merge(u, &player[z / 100].unit[z % 100])) return true;
	if (player[z / 100].unit[z % 100].can_load_unit(u)) return true;
	
	return false;
}

void logic_netchat(string message)
{
	static bool first = true, holdingenter;
	static int chattimer;
	static float chatalpha;
	static _listbox chatbox;
	static _textline chattext;
	string chatstr;
	
	if (first)
	{
		chatbox.init(0, YTILES * MAP_TILE_SIZE - 135, 620, 121);
		chattext.init(0, YTILES * MAP_TILE_SIZE - 13, 620, 13, true);
		chatalpha = 0.0;
		chattimer = 0;
		holdingenter = false;
		first = false;
	}
	
	if (message != "")
	{
		chatbox.additem(message.c_str());
		chatbox.scroll_to_end();
		chatalpha = 1.0;
		return;
	}
	
	if (game->typingchat)
	{
		chatbox.draw(0.8, true, true);
		chatstr = chattext.check(0.8);
	}
	else
	{
		if (holdingenter)
		{
			if (!key[KEY_ENTER])
			{
				holdingenter = false;
			}
		}
		else
		{
			if (key[KEY_ENTER]) game->typingchat = true;
		}
		chattext.draw(0.2);
		clear_keybuf();
		chatstr = "/NOTHING YET/";
	}
	
	if (chatstr != "/NOTHING YET/")
	{
		chatalpha = 1.0;
		chattimer = 0;
		game->typingchat = false;
		holdingenter = true;
	}
	else
	{
		if (chattimer > 250)
		{
			chatalpha -= 0.004;
			if (chatalpha < 0) chatalpha = 0;
		}
		else chattimer++;
	}
	
	chatbox.draw(chatalpha, true, true);
	if (net.server)
	{
		if (do_hostchat(&chatbox, chatstr))
		{
			chatalpha = 1.0;
			chattimer = 0;
		}
	}
	else
	{
		if (do_clientchat(&chatbox, chatstr))
		{
			chatalpha = 1.0;
			chattimer = 0;
		}
	}
}

int reconnect()
{
	static bool first = true, nohost, pollingerror, isclient, waiting, nobodyleft, allconnected;
	static int timer = 0;
	static _button cancel;
	int i, j, k;
	DyBytes data;
	
	if (first)
	{
		pollingerror = false;
		nohost = false;
		waiting = false;
		first = false;
		nobodyleft = false;
		timer = 0;
		
		net.hostnum++;
		
		while (player[net.hostnum].controller == NETAI) net.hostnum++;
		if (net.hostnum >= 4)
		{
			nohost = true;
		}
		else if ((player[net.hostnum].controller == HUMAN) || (player[net.hostnum].controller == AI))
		{  //if the player designated to be the next host is controlled by this machine
			j = 0;
			i = 0;
			while (i < 4)
			{
				if (player[i].playing == 1)
				{
					j++;
					if ((player[i].controller == NETAI) || (i < net.hostnum)) player[i].controller = AI;  //AIs controlled by people who disconnected are controlled by the new host
				}
				i++;
			}
			if (j == 0)
			{  //no other players besides the new host are left in the game
				nobodyleft = true;
				cancel.init(240, 450, 180, 40, "Return to Netplay Menu");
			}
			else cancel.init(240, 450, 180, 40, "Stop Waiting");
			allconnected = false;
			
			net.server = true;
			net.engine->disconnectHost(net.thread);
			net.chatengine->disconnectHost(net.chatthread);
			net.engine->enableTCPListen(41136);
			net.chatengine->enableTCPListen(41137);
			
			net.clientthread[0] = -1;
			net.clientthread[1] = -1;
			net.clientthread[2] = -1;
		}
		else
		{
			cancel.init(240, 450, 180, 40, "Exit to Netplay Menu");
			if (net.address[net.hostnum] != "")
			{
				net.engine->disconnectHost(net.thread);
				net.chatengine->disconnectHost(net.chatthread);
				net.engine->connectToHost(net.address[net.hostnum], 41136);
				net.chatengine->connectToHost(net.address[net.hostnum], 41137);
				net.thread = -1;
				net.chatthread = -1;
			}
			else
			{  //next designated host doesn't have an address - game over
				nohost = true;
			}
		}
	}
	
	timer++;
	
	buffer_rectfill(0, 0, 800, 600, BLACK, 0.65);
	cancel.check();
	
	buffer_textout_centre_ex(big, "Lost connection to host", 320, 100, WHITE);
	if (net.server)
	{
		buffer_textout_centre_ex(big, "You are the new host.", 320, 200, WHITE);
		
		if (nobodyleft)
			buffer_textout_centre_ex(big, "No other players left.  Can't continue the game.", 320, 250, WHITE);
		else
		{
			buffer_textout_centre_ex(big, "Waiting for the other clients to connect...", 320, 250, WHITE);
			
			allconnected = true;
			i = 0;
			while (i < 4)
			{
				if ((player[i].playing == 1) && (player[i].controller == NET))
				{
					buffer_textout_right_ex(big, (player[i].name + ": ") + net.address[i], 310, 280 + (20 * i), WHITE);
					if (clientconnected(i - 1)) buffer_textout_ex(big, "Connected", 330, 280 + (20 * i), GREEN);
					else
					{
						buffer_textout_ex(big, "Not Connected Yet", 330, 280 + (20 * i), WHITE);
						allconnected = false;
					}
				}
				else if ((player[i].playing == 1) && (player[i].controller != HUMAN))
				{
					buffer_textout_right_ex(big, player[i].name, 310, 280 + (20 * i), WHITE);
					buffer_textout_ex(big, "Controlled by AI", 330, 280 + (20 * i), WHITE);
				}
				i++;
			}
			
			if (allconnected)
			{
				buffer_textout_centre_ex(big, "All Players Connected / Transferred to AI", 320, 500, WHITE);
				if (cancel.get_text() != "Continue Game") cancel.init(240, 450, 180, 40, "Continue Game");
			}
			else buffer_textout_centre_ex(med, "Hitting \"Stop Waiting\" will replace any not-yet-reconnected players with AI", 320, 500, WHITE);
			
			i = 0;
			while ((unsigned)i < net.engine->getMaxConnections())
			{
				if (net.engine->acceptClient(i))
				{
					isclient = false;
					j = net.hostnum + 1;
					while (j < 4)
					{
						if (net.engine->getRemoteAddr(i) == net.address[j])
						{
							isclient = true;
							k = 0;
							while (clientconnected(k)) k++;
							if (k < 3)
								net.clientthread[k] = i;
							else
								net.engine->disconnectHost(i);
						}
						j++;
					}
					if (!isclient)
					{  //if whoever just connected wasn't one of the clients in the game
						net.engine->disconnectHost(i);
					}
				}
				i++;
			}
		}
		
		if (cancel.clicked())
		{
			net.engine->disableTCPListen();
			net.chatengine->disableTCPListen();
			if (nobodyleft)
			{
				net.thread = -1;
				net.chatthread = -1;
				net.clientthread[0] = -1;
				net.clientthread[1] = -1;
				net.clientthread[2] = -1;
				first = true;
				return -1;
			}
			else
			{
				i = net.hostnum + 1;
				while (i < 4)
				{
					if (player[i].playing == 1)
					{  //make sure everyone has the right controller data
						//replace players that haven't connected with AI
						if ((i > 0) && (!clientconnected(i - 1))) player[i].controller = AI;
						
						data.clear();
						data.appendByte(NET_NEWCONTROLLER);
						data.appendByte(i);
						
						if (player[i].controller == HUMAN) data.appendByte(NET);
						else if (player[i].controller == AI) data.appendByte(NETAI);
						else data.appendByte(NET);
						net.engine->broadcastTCP(data);
					}
					i++;
				}
				data.clear();
				data.appendByte(NET_RESUMEGAME);
				net.engine->broadcastTCP(data);
				first = true;
				return 1;
			}
		}
	}
	else  //not the new host
	{
		if (nohost)
		{
			buffer_textout_centre_ex(big, "Error: There is no new host.  Can't continue the game.", 320, 200, WHITE);
			
			if (cancel.clicked())
			{
				first = true;
				return -1;
			}
		}
		else if (pollingerror)
		{
			buffer_textout_centre_ex(big, "Error connecting to the new host.  Can't continue the game.", 320, 200, WHITE);
			if (cancel.clicked())
			{
				first = true;
				return -1;
			}
		}
		else
		{
			int temp;
			waiting = false;
			
			if (net.thread != -1)
			{
				waiting = true;
				i = net.engine->checkConnection(temp);
				if (i >= 0)
				{
					net.thread = i;
				}
				else if (i != DY_BUSYUNREACHABLE)
				{
					pollingerror = true;
				}
			}
			if (net.chatthread != -1)
			{
				waiting = true;
				i = net.chatengine->checkConnection(temp);
				if (i >= 0)
				{
					net.chatthread = i;
				}
				else if (i != DY_BUSYUNREACHABLE)
				{
					pollingerror = true;
				}
			}
			
			if (waiting) buffer_textout_centre_ex(big, "Attempting to connect to the new host...", 320, 200, WHITE);
			else
			{
				buffer_textout_centre_ex(big, "Connected to new host!", 320, 200, WHITE);
				buffer_textout_centre_ex(big, "Game will continue when the host stops waiting for others...", 320, 220, WHITE);
				
				if (get_host_message(data))
				{
					if (data.getByte(0) == NET_NEWCONTROLLER)
					{
						if (data.getByte(1) != net.client_pnum)
						{  //our player's controller is always human, so ignore anything to the contrary
							player[data.getByte(1)].controller = data.getByte(2);
						}
					}
					else if (data.getByte(0) == NET_RESUMEGAME)
					{
						first = true;
						return 1;
					}
				}
			}
			
			if (cancel.clicked())
			{
				if (net.thread != -1)
				{
					net.engine->disconnectHost(net.thread);
					net.thread = -1;
				}
				if (net.chatthread != -1)
				{
					net.engine->disconnectHost(net.chatthread);
					net.chatthread = -1;
				}
				first = true;
				return -1;
			}
			
		}
	}
	
	return 0;
}

string netcommand(unsigned char byte)
{
	switch (byte)
	{
		case NET_CLIENT_ENDTURN:
			return "NET_CLIENT_ENDTURN";
			break;
		case NET_CLIENT_NEWUNIT:
			return "NET_CLIENT_NEWUNIT";
			break;
		case NET_CLIENT_MOVEUNIT:
			return "NET_CLIENT_MOVEUNIT";
			break;
		case NET_CLIENT_STANDBY:
			return "NET_CLIENT_STANDBY";
			break;
		case NET_CLIENT_DEPLOY:
			return "NET_CLIENT_DEPLOY";
			break;
		case NET_CLIENT_MECHCAP:
			return "NET_CLIENT_MECHCAP";
			break;
		case NET_CLIENT_UNLOAD:
			return "NET_CLIENT_UNLOAD";
			break;
		case NET_CLIENT_TELEPORT:
			return "NET_CLIENT_TELEPORT";
			break;
		case NET_CLIENT_ATTACK:
			return "NET_CLIENT_ATTACK";
			break;
		case NET_CLIENT_SURRENDER:
			return "NET_CLIENT_SURRENDER";
			break;
		default:
			break;
	}
	
	char s[5];
	sprintf(s, "%d", byte);
	return s;
}
