// Copyright (C) 2003-2009 Dolphin Project.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License 2.0 for more details.

// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/

// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/

#include "NetWindow.h"

//--------------------------------
//			GUI EVENTS
//--------------------------------

void NetEvent::AppendText(const wxString text)
{
	// I have the feeling SendEvent may be a bit safer/better...
#if 1
	SendEvent(ADD_TEXT, std::string(text.mb_str()));
#else
	wxMutexGuiEnter();
		m_netptr->AppendText(text);
	wxMutexGuiLeave();
#endif
}

void NetEvent::SendEvent(int EventType, const std::string text, int integer)
{
	wxCommandEvent event(wxEVT_HOST_COMMAND, wxID_ANY);

	event.SetId( EventType );
	event.SetInt( integer );
	event.SetString( wxString::FromAscii(text.c_str()) );

	m_netptr->AddPendingEvent(event);
}

//--------------------------------
//		SERVER SIDE THREAD
//--------------------------------

ServerSide::ServerSide(NetPlay* netptr, sf::SocketTCP socket, sf::SocketUDP socketUDP, int netmodel, std::string nick)
	: wxThread()
{
	m_numplayers = 0;
	m_data_received = false;
	m_netmodel = netmodel;
	m_socket = socket;
	m_socketUDP = socketUDP;
    m_netptr = netptr;
	m_nick = nick;
	Event = new NetEvent(m_netptr);
}

char ServerSide::GetSocket(sf::SocketTCP Socket)
{
	for (int i=0; i < m_numplayers; i++)
	{
		if(m_client[i].socket == Socket)
			return i;
	}

	return 0xE;
}

bool ServerSide::RecvT(sf::SocketUDP Socket, char * Data, size_t Max, size_t& Recvd, float Time)
{
	sf::SelectorUDP Selector;
	sf::IPAddress Addr;
	Selector.Add(Socket);

	if (Selector.Wait(Time) > 0)
	{
		Socket.Receive(Data, Max, Recvd, Addr);
		return true;
	}
	else
	{
		return false;
	}
}

void *ServerSide::Entry()
{
	// Add listening socket
	m_selector.Add(m_socket);

	while (1)
	{
		char nbSocketReady = m_selector.Wait(0.5);

		for (char i = 0; i < nbSocketReady; ++i)
		{
			m_CriticalSection.Enter();

			sf::SocketTCP Socket = m_selector.GetSocketReady(i);

			if (Socket == m_socket)
			{
				// Incoming connection
				Event->AppendText(_("*Connection Request... "));

				sf::SocketTCP Incoming;
				sf::IPAddress Address;
				m_socket.Accept(Incoming, &Address);

				unsigned char sent = 0x12;
				if ((m_netmodel == 0 && m_numplayers > 0) || m_numplayers == 3)
				{
					Incoming.Send((const char *)&sent, 1);	// Tell it the server is full...
					Incoming.Close();						// Then close the connection

					Event->AppendText(_(" Server is Full !\n"));
				}
				else
				{
					Event->AppendText(_(" Connection accepted\n"));
					m_client[m_numplayers].socket = Incoming;
					m_client[m_numplayers].address = Address;
					
					if (SyncValues(m_numplayers, Address))
					{
						// Add it to the selector
						m_selector.Add(Incoming);
						Event->SendEvent(HOST_NEWPLAYER);

						m_numplayers++;
					}
					else
					{
						Event->AppendText(_("ERROR : Unable to establish UDP connection !\n"));
						Incoming.Close();
					}
				}
			}
			else
			{
				unsigned char recv;
				int socket_nb;
				size_t recv_size;
				sf::Socket::Status recv_status;
				socket_nb = GetSocket(Socket);

				if ((recv_status = Socket.Receive((char *)&recv, 1, recv_size)) == sf::Socket::Done)
				{
#ifdef NET_DEBUG
					char recv_str[32];
					sprintf(recv_str, "received: 0x%02x | %c\n", recv, recv);
					Event->AppendText(wxString::FromAscii(recv_str));
#endif 
					OnServerData(socket_nb, recv);
				}
				else
				{
					if (recv_status == sf::Socket::Disconnected)
					{
						Event->SendEvent(HOST_PLAYERLEFT);
						m_numplayers--;

						std::string player_left = m_client[socket_nb].nick;
						Event->AppendText( wxString::Format(wxT("*Player : %s left the game.\n\n"),
							player_left.c_str()) );

						// We need to adjust the struct...
						for (int j = socket_nb; j < m_numplayers; j++)
						{
							m_client[j].socket = m_client[j+1].socket;
							m_client[j].nick   = m_client[j+1].nick;
							m_client[j].ready  = m_client[j+1].ready;
						}

						// Send disconnected message to all
						unsigned char send = 0x11;
						unsigned int str_size = (int)player_left.size();

						for (int j=0; j < m_numplayers ; j++)
						{
							m_client[j].socket.Send((const char*)&send, 1);
							m_client[j].socket.Send((const char*)&str_size, 4);
							m_client[j].socket.Send(player_left.c_str(), (int)str_size + 1);
						}
					}
					else
					{
						// Hopefully this should never happen, the client is not
						// Even warned that he is being dropped...
						Event->SendEvent(HOST_ERROR, m_client[socket_nb].nick);
					}

					m_selector.Remove(Socket);
					Socket.Close();
				}
			}

			m_CriticalSection.Leave();
		}

		if(TestDestroy())
		{
			// Stop listening
			m_socket.Close();

			// Delete the Thread and close clients sockets
			for (int i=0; i < m_numplayers ; i++)
				m_client[i].socket.Close();

			break;
		}
	}

	return NULL;
}

bool ServerSide::SyncValues(unsigned char socketnb, sf::IPAddress Address)
{
	sf::SocketTCP Socket = m_client[socketnb].socket;

	std::string buffer_str = m_netptr->GetSelectedGame();
	char *buffer = NULL;
	unsigned char init_number;
	u32 buffer_size = (u32)buffer_str.size();
	size_t received;
	bool errorUDP = false;

	// First, Send the number of connected clients & netmodel
	Socket.Send((const char *)&m_numplayers, 1);
	Socket.Send((const char *)&m_netmodel, 4);

	// Send the Game String
	Socket.Send((const char *)&buffer_size, 4);
	Socket.Send(buffer_str.c_str(), buffer_size + 1);

	// Send the host Nickname
	buffer_size = (u32)m_nick.size();
	Socket.Send((const char *)&buffer_size, 4);
	Socket.Send(m_nick.c_str(), buffer_size + 1);

	// Read client's UDP Port
	Socket.Receive((char *)&m_client[m_numplayers].port, sizeof(short), received);

	// Read returned nickname
	Socket.Receive((char *)&buffer_size, 4, received);
		buffer = new char[buffer_size + 1];
	Socket.Receive(buffer, buffer_size + 1, received);

	m_client[socketnb].nick = std::string(buffer);
	m_client[socketnb].ready = false;

	///////////////////
	// Test UDP Socket 
	///////////////////
	if (m_socketUDP.Send((const char*)&m_numplayers, 1, Address, m_client[m_numplayers].port) == sf::Socket::Done)
	{
		// Test UDP Socket Receive, 2s timeout
		if (!RecvT(m_socketUDP, (char*)&init_number, 1, received, 2))
			errorUDP = true;
	}
	else
		errorUDP = true;

	// Check if the client has the game
	Socket.Receive((char *)&init_number, 1, received);

	delete[] buffer;

	if (!errorUDP)
	{
		// Send to all connected clients
		if (m_numplayers > 0)
		{
			unsigned char send = 0x10;
			buffer_size = (int)m_client[socketnb].nick.size();
			for (int i=0; i < m_numplayers ; i++)
			{
				// Do not send to connecting player
				if (i == socketnb)
					continue;

				m_client[i].socket.Send((const char *)&send, 1);		// Init new connection
				m_client[i].socket.Send((const char *)&init_number, 1);	// Send Game found ?
				m_client[i].socket.Send((const char *)&buffer_size, 4);	// Send client nickname
				m_client[i].socket.Send(m_client[socketnb].nick.c_str(), buffer_size + 1);
			}
		}

		Event->AppendText(  wxString::Format(wxT("*Connection established to %s (%s:%d)\n"),
			m_client[socketnb].nick.c_str(), Address.ToString().c_str(), m_client[m_numplayers].port)  );

		if (init_number != 0x1F) // Not Found
			for (int i = 0; i < 4; i++)
				Event->AppendText(_("WARNING : Game Not Found on Client Side !\n"));

		// UDP connecton successful
		init_number = 0x16;
		Socket.Send((const char *)&init_number, 1);
	}
	else	// UDP Error, disconnect client
	{
		// UDP connecton failed
		init_number = 0x17;
		Socket.Send((const char *)&init_number, 1);
		return false;
	}

	return true;
}

void ServerSide::Write(int socknb, const char *data, size_t size, long *ping)
{
	wxCriticalSectionLocker lock(m_CriticalSection);

		if (ping != NULL)
		{
			// Ask for ping
			unsigned char value = 0x15;
			size_t recv_size;
			u32 four_bytes = 0x101A7FA6;

			Common::Timer timer;
			timer.Start();

			for (int i=0; i < m_numplayers ; i++)
			{
				m_client[i].socket.Send((const char*)&value, 1);

				timer.Update();
				m_client[i].socket.Send((const char*)&four_bytes, 4);
				m_client[i].socket.Receive((char*)&four_bytes, 4, recv_size);
				ping[i] = (long)timer.GetTimeDifference();
			}

			return;
		}

	// Send the data safely, without intefering with another call 
	m_client[socknb].socket.Send(data, size);
}

void ServerSide::WriteUDP(int socknb, const char *data, size_t size)
{
	wxCriticalSectionLocker lock(m_CriticalSection);

	m_socketUDP.Send(data, size, m_client[socknb].address, m_client[socknb].port);
}

//--------------------------------
//		CLIENT SIDE THREAD
//--------------------------------

ClientSide::ClientSide(NetPlay* netptr, sf::SocketTCP socket, sf::SocketUDP socketUDP, std::string addr, std::string nick)
	: wxThread()
{
	m_numplayers = 0;
	m_data_received = false;
	m_netmodel = 0;
	m_socket = socket;
	m_socketUDP = socketUDP;
	m_port = m_socketUDP.GetPort();
    m_netptr = netptr;
	m_nick = nick;
	m_addr = addr;
	Event = new NetEvent(m_netptr);
}

bool ClientSide::RecvT(sf::SocketUDP Socket, char * Data, size_t Max, size_t& Recvd, float Time)
{
	sf::SelectorUDP Selector;
	sf::IPAddress addr;
	Selector.Add(Socket);

	if (Selector.Wait(Time) > 0)
	{
		Socket.Receive(Data, Max, Recvd, addr);
		return true;
	}
	else
	{
		return false;
	}
}

void *ClientSide::Entry()
{
	Event->AppendText(_("*Connection Request... "));

	// If we get here, the connection is already accepted, however the game may be full
	if (SyncValues())
	{
		// Tell the server if the client has the game	
		CheckGameFound();

		// Check UDP connection
		unsigned char value;
		size_t val_sz;
		m_socket.Receive((char *)&value, 1, val_sz);
		
		if (value == 0x16)	// UDP connection successful
		{
			Event->AppendText(_("Connection successful !\n"));
			Event->AppendText( wxString::Format(wxT("*Connection established to %s (%s)\n*Game is : %s\n"), 
				m_hostnick.c_str(), m_addr.c_str(), m_selectedgame.c_str() ) );
		}
		else
		{
			Event->AppendText(_("UDP Connection FAILED !\n"
				"ERROR : Unable to establish UDP Connection, please Check UDP Port forwarding !"));
			m_socket.Close();
			Event->SendEvent(HOST_ERROR);
			return NULL;
		}
	}
	else // Server is Full  
	{
		m_socket.Close();
		Event->SendEvent(HOST_FULL);
		return NULL;
	}

	m_netptr->ChangeSelectedGame(m_selectedgame);
	Event->SendEvent(HOST_NEWPLAYER, "NULL", m_netmodel);
	Event->SendEvent(GUI_UPDATE);

	m_selector.Add(m_socket);

	while (1)
	{
		unsigned char recv;
		size_t recv_size;
		sf::Socket::Status recv_status;

		// we use a selector because of the useful timeout
		if (m_selector.Wait(0.5) > 0)
		{
			m_CriticalSection.Enter();

			if ((recv_status = m_socket.Receive((char *)&recv, 1, recv_size)) == sf::Socket::Done)
			{
#ifdef NET_DEBUG
				char recv_str[32];
				sprintf(recv_str, "received: 0x%02x | %c\n", recv, recv);
				Event->AppendText(wxString::FromAscii(recv_str));
#endif 
				OnClientData(recv);
			}
			else
			{
				if (recv_status == sf::Socket::Disconnected)
				{
					Event->SendEvent(HOST_DISCONNECTED);
				}
				else
				{
					Event->SendEvent(HOST_ERROR);
				}

				m_selector.Remove(m_socket);
				m_socket.Close();

				return NULL;
			}

			m_CriticalSection.Leave();
		}
		
		if(TestDestroy())
		{
			m_socket.Close();

			break;
		}
	}

	return NULL;
}

bool ClientSide::SyncValues()
{
	unsigned int buffer_size = (int)m_nick.size();
	unsigned char byterecv;
	bool errorUDP;
	char *buffer = NULL;
	size_t recv_size;

	unsigned short server_port;
	std::string host = m_addr.substr(0, m_addr.find(':'));
	TryParseInt(m_addr.substr(m_addr.find(':')+1).c_str(), (int *)&server_port);

	// First, Read the init number : nbplayers (0-2) or server full (0x12)
	m_socket.Receive((char *)&m_numplayers, 1, recv_size);
	if (m_numplayers == 0x12)
		return false;
	m_socket.Receive((char *)&m_netmodel, 4, recv_size);

	// Send client's UDP Port
	m_socket.Send((const char *)&m_port, sizeof(short));

	// Send client's nickname
	m_socket.Send((const char *)&buffer_size, 4);
	m_socket.Send(m_nick.c_str(), buffer_size + 1);

	// Read the Game String
	m_socket.Receive((char *)&buffer_size, 4, recv_size);
		buffer = new char[buffer_size + 1];
	m_socket.Receive(buffer, buffer_size + 1, recv_size);
		m_selectedgame = std::string(buffer);

	// Read the host Nickname
	m_socket.Receive((char *)&buffer_size, 4, recv_size); 
		buffer = new char[buffer_size + 1];
	m_socket.Receive(buffer, buffer_size + 1, recv_size);
		m_hostnick = std::string(buffer);

	///////////////////
	// Test UDP Socket
	///////////////////
	if (m_socketUDP.Send((const char*)&m_numplayers, 1, host.c_str(), server_port) == sf::Socket::Done)
	{
		// Test UDP Socket Receive, 2s timeout
		if (!RecvT(m_socketUDP, (char*)&byterecv, 1, recv_size, 2))
			errorUDP = true;
	}
	else
		errorUDP = true;

	delete[] buffer;
	return true;
}

void ClientSide::CheckGameFound()
{
	unsigned char send_value;

	// Check if the game selected by Host is in Client's Game List
	m_netptr->IsGameFound(&send_value, m_selectedgame);

	if (send_value == 0x1F) // Found
	{	
		m_socket.Send((const char *)&send_value, 1);
	}
	else
	{	
		m_socket.Send((const char *)&send_value, 1);

		for (int i = 0; i < 2; i++)
			Event->AppendText(_("WARNING : You do not have the Selected Game !\n"));
	}
}

void ClientSide::Write(const char *data, size_t size, long *ping)
{
	wxCriticalSectionLocker lock(m_CriticalSection);

		if (ping != NULL)
		{
			// Ask for ping
			unsigned char value = 0x15;
			size_t recv_size;
			int four_bytes = 0x101A7FA6;

			Common::Timer timer;
			timer.Start();

			m_socket.Send((const char*)&value, 1);
			m_socket.Send((const char*)&four_bytes, 4);
			m_socket.Receive((char*)&four_bytes, 4, recv_size);

			*ping = (long)timer.GetTimeElapsed();

			return;
		}

	m_socket.Send(data, size);
}

void ClientSide::WriteUDP(const char *data, size_t size)
{
	wxCriticalSectionLocker lock(m_CriticalSection);

	unsigned short server_port;
	std::string host = m_addr.substr(0, m_addr.find(':'));
	TryParseInt(m_addr.substr(m_addr.find(':')+1).c_str(), (int *)&server_port);

	m_socketUDP.Send(data, size, host.c_str(), server_port);
}
