//////////////////////////////////////////////////////////////////////////////
//
// Asura_NetLinkManager.cpp
//
// Copyright (c) 2007 Rebellion. All rights reserved.
//
// Description:
//
//     class to handle connection, disconnection and link status of internet
//     and LAN connections (including Wifi infrastructure and adhoc)
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#ifdef ASURA_SUPPORT_EXTERNAL_NETWORK

// This file's header.
#include "Network/Asura_NetLinkManager.h"

// Asura
#include "Interface/Asura_CommandConsole.h"
#ifdef ASURA_SUPPORT_GAMESPY
#include "Network/GameSpy/Asura_GameSpy.h"
#endif
#include "Network/Asura_Network.h"

// C O N S T A N T S /////////////////////////////////////////////////////////
static const Asura_UnicodeChar* wszNETLINK_CONNECT = L"NetLink.Connect";
static const Asura_UnicodeChar* wszNETLINK_DISCONNECT = L"NetLink.Disconnect";
static const Asura_UnicodeChar* wszNETLINK_RESETDROPPEDSTATUS = L"NetLink.ResetDroppedStatus";
static const Asura_UnicodeChar* wszNETLINK_STATUS = L"NetLink.Status";
static const Asura_UnicodeChar* wszNETLINK_TYPE = L"NetLink.Type";
static const Asura_UnicodeChar* wszNETLINK_WLANSWITCHOFF = L"NetLink.WLANSwitchOff";
static const Asura_UnicodeChar* wszNETLINK_PROGRESS = L"NetLink.Progress";
#if _DEBUG
static const Asura_UnicodeChar* wszNETLINK_DROPCONNECTION = L"NetLink.DropConnection";
#endif

// I N T E R N A L   F U N C T I O N S ///////////////////////////////////////
#if _DEBUG
static bool ConsoleCmd_DropConnection( bool bHelp )
{
	if (bHelp)
	{
		Asura_CommandConsole::Printf("for testing dropped connection e.g. due to WLAN switch");
	}
	else
	{
		Asura_NetLinkManager::DropConnection();
	}
	return true;
}
#endif

// S T A T I C   C L A S S   D A T A /////////////////////////////////////////
u_int Asura_NetLinkManager::s_uStatus;
u_int Asura_NetLinkManager::s_uType;
bool Asura_NetLinkManager::s_bWLANSwitchedOff;
int Asura_NetLinkManager::s_iStateProgress = 0;
u_int Asura_NetLinkManager::s_uUploadBitsPerSec = 0;
u_int Asura_NetLinkManager::s_uDownloadBitsPerSec = 0;


void Asura_NetLinkManager::Initialise()
{
	static bool g_bInitialised = false;
	if (g_bInitialised)
	{
		Asura_Assert(false, "you can remove project calls to Asura_NetLinkManager::Initialise()/Shutdown() now");
		return;
	}
	g_bInitialised = false;

	Asura_CommandConsole::AddCmd( wszNETLINK_CONNECT, Connect, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::AddCmd( wszNETLINK_DISCONNECT, Disconnect, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
#if _DEBUG
	Asura_CommandConsole::AddCmd( wszNETLINK_DROPCONNECTION, ConsoleCmd_DropConnection, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
#endif
	Asura_CommandConsole::AddCmd( wszNETLINK_RESETDROPPEDSTATUS, ResetDroppedStatus, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );

	Asura_CommandConsole::AddVar( wszNETLINK_STATUS, &s_uStatus, 0, 10, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::SetVarHelpText( wszNETLINK_STATUS, "network link status: 0=discon,1=cncting,2=conn,3=connfail,4=dcncting,5=dropping,6=dropped,7=chk gspy,8=gspy unavail,9=gspy temp unavail,10=DNSfail" );
	Asura_CommandConsole::MakeVarReadOnly( wszNETLINK_STATUS );
	Asura_CommandConsole::AddVar( wszNETLINK_TYPE, &s_uType, 0, 1, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::SetVarHelpText( wszNETLINK_TYPE, "network link type: 0=internet/WiFi infrastructure,1=lan/WiFi AdHoc" );
	Asura_CommandConsole::MakeVarReadOnly( wszNETLINK_TYPE );

	Asura_CommandConsole::AddVar( wszNETLINK_WLANSWITCHOFF, &s_bWLANSwitchedOff, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::MakeVarReadOnly( wszNETLINK_WLANSWITCHOFF );

	Asura_CommandConsole::AddVar( wszNETLINK_PROGRESS, &s_iStateProgress, 0 , 100, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );
	Asura_CommandConsole::SetVarHelpText( wszNETLINK_PROGRESS, "network link progress: progress of the current state, range = 0 -> 100" );
	Asura_CommandConsole::MakeVarReadOnly( wszNETLINK_PROGRESS );
}

void Asura_NetLinkManager::Update()
{
	switch (s_uStatus)
	{
		case ASURA_NETLINK_CONNECTING:
		{
			Platform_UpdateConnecting();
			break;
		}
		case ASURA_NETLINK_CONNECTED:
		{
#if defined(ASURA_SUPPORT_GAMESPY)
			Asura_GameSpy::Update();
#endif
			Platform_UpdateConnected();
			break;
		}
		case ASURA_NETLINK_DISCONNECTING:
		case ASURA_NETLINK_CONNECTION_DROPPING:
		{
			Platform_UpdateDisconnecting();
			break;
		}
#ifdef ASURA_SUPPORT_GAMESPY
		case ASURA_NETLINK_CHECKING_GAMESPY:
		{
			Asura_GameSpy::Update();

			switch (Asura_GameSpy::GetState())
			{
				case ASURA_GAMESPY_STATE_AVAILABLE_CHECK:
				{
					break;
				}
				case ASURA_GAMESPY_STATE_INITIALISED:
				{
					s_uStatus = ASURA_NETLINK_CONNECTED;
					break;
				}
				case ASURA_GAMESPY_STATE_UNAVAILABLE:
				{
					s_uStatus = ASURA_NETLINK_FAILED_GAMESPY_UNAVAILABLE;
					break;
				}
				case ASURA_GAMESPY_STATE_TEMPORARILY_UNAVAILABLE:
				{
					s_uStatus = ASURA_NETLINK_FAILED_GAMESPY_TEMPORARILY_UNAVAILABLE;
					break;
				}
				case ASURA_GAMESPY_STATE_DNS_FAILED:
				{
					s_uStatus = ASURA_NETLINK_FAILED_GAMESPY_DNS_FAILED;
					break;
				}
				default:
				{
					Asura_Assert(false, "unexpected GameSpy state");
					break;
				}
			} // end switch (Asura_GameSpy::GetState())

			s_iStateProgress = (s_uStatus == ASURA_NETLINK_CHECKING_GAMESPY) ?  static_cast<int>(Asura_GameSpy::GetConnectionProgress()) : 0;

			break;
		} // end case ASURA_NETLINK_CHECKING_GAMESPY:
#endif // #ifdef ASURA_SUPPORT_GAMESPY
		default:
		{
			break;
		}
	} // end switch (s_uStatus)
}

void Asura_NetLinkManager::Update_PostRender()
{
	if (s_uStatus == ASURA_NETLINK_CONNECTING)
	{
		Platform_UpdateConnecting_PostRender();
	}
}

void Asura_NetLinkManager::Shutdown()
{
	if (IsConnected())
	{
		Disconnect(false);
		Update();
	}
}

bool Asura_NetLinkManager::Connect( int iType, bool bHelp, bool bInterrogate )
{
	if (bHelp || bInterrogate)
	{
		Asura_CommandConsole::Printf( "initiate network startup with type (0 = internet, 1 = LAN)" );
		return true;
	}

	if (iType < 0 || iType >= ASURA_NETLINK_NUM_TYPES)
	{
		Asura_Assert(false, "invalid netlink type");
		Asura_CommandConsole::Printf( "invalid netlink type");
		return false;
	}

	Asura_Assert(IsDisconnected(), "can only connect when disconnected");
	if (IsDisconnected())
	{
		s_uType = static_cast<u_int>(iType);
		if (Platform_InitiateConnection())
		{
			s_uStatus = ASURA_NETLINK_CONNECTING;
			return true;
		}
		else
		{
			s_uStatus = ASURA_NETLINK_CONNECTION_FAILED;
		}
	}
	return false;
}

bool Asura_NetLinkManager::Disconnect( bool bHelp )
{
	if (bHelp)
	{
		Asura_CommandConsole::Printf( "initiate network shutdown" );
		return true;
	}

	Asura_Assert( IsConnected(), "can only disconnect when connected");
	if (IsConnected())
	{
#ifdef ASURA_SUPPORT_GAMESPY
		if (s_uType == ASURA_NETLINK_INTERNET)
		{
			Asura_GameSpy::Shutdown();
		}
#endif
		Platform_InitiateDisconnection();
		s_uStatus = ASURA_NETLINK_DISCONNECTING;
		return true;
	}
	return false;
}

bool Asura_NetLinkManager::ResetDroppedStatus( bool bHelp )
{
	if (bHelp)
	{
		Asura_CommandConsole::Printf( "if connection state is 'dropped' change it to 'disconnected'" );
	}
	else
	{
		if (s_uStatus != ASURA_NETLINK_CONNECTION_DROPPED)
		{
			Asura_Assert(false, "invalid state");
			return false;
		}
		s_uStatus = ASURA_NETLINK_DISCONNECTED;
	}
	return true;
}

bool Asura_NetLinkManager::IsConnected()
{
	switch (s_uStatus)
	{
		case ASURA_NETLINK_CONNECTED:
#ifdef ASURA_SUPPORT_GAMESPY
		case ASURA_NETLINK_CHECKING_GAMESPY:
		case ASURA_NETLINK_FAILED_GAMESPY_UNAVAILABLE:
		case ASURA_NETLINK_FAILED_GAMESPY_TEMPORARILY_UNAVAILABLE:
		case ASURA_NETLINK_FAILED_GAMESPY_DNS_FAILED:
#endif
			return true;
		default:
			return false;
	}
}

bool Asura_NetLinkManager::IsDisconnected()
{
	switch(s_uStatus)
	{
		case ASURA_NETLINK_DISCONNECTED:
		case ASURA_NETLINK_CONNECTION_FAILED:
		case ASURA_NETLINK_CONNECTION_DROPPED:
			return true;
		default:
			return false;
	}
}

void Asura_NetLinkManager::FinishedPlatformConnecting(bool bSuccess)
{
	Asura_Assert(s_uStatus == ASURA_NETLINK_CONNECTING, "wrong state");
	if (!bSuccess)
	{
		s_uStatus = ASURA_NETLINK_CONNECTION_FAILED;
		return;
	}

#ifdef ASURA_SUPPORT_GAMESPY
	if (s_uType == ASURA_NETLINK_INTERNET)
	{
		s_uStatus = ASURA_NETLINK_CHECKING_GAMESPY;
		Asura_GameSpy::Initialise();
	}
	else
#endif
	{
		s_uStatus = ASURA_NETLINK_CONNECTED;
	}
}

void Asura_NetLinkManager::FinishedPlatformDisconnecting()
{
	Asura_Assert(s_uStatus == ASURA_NETLINK_DISCONNECTING || s_uStatus == ASURA_NETLINK_CONNECTION_DROPPING, "wrong state");
	if (s_uStatus == ASURA_NETLINK_DISCONNECTING)
	{
		s_uStatus = ASURA_NETLINK_DISCONNECTED;
	}
	else
	{
		s_uStatus = ASURA_NETLINK_CONNECTION_DROPPED;
	}
}

void Asura_NetLinkManager::ConnectionDropped()
{
	Asura_Assert(s_uStatus == ASURA_NETLINK_CONNECTED, "wrong state");
#ifdef ASURA_SUPPORT_GAMESPY
	if (s_uType == ASURA_NETLINK_INTERNET)
	{
		Asura_GameSpy::Shutdown();
	}
#endif

	if (Asura_Network::IsNetworkInitialised() && Asura_Network::IsExternalNetworkActive() && Asura_Network::IsConnectionOkay())
	{
		Asura_Network::SetConnectionOkay(false);
		Asura_Network::SetReasonForDisconnection(ASURA_NDR_CONNECTION_DROPPED);
	}

	Platform_InitiateDisconnection();

	s_uStatus = ASURA_NETLINK_CONNECTION_DROPPING;
}

bool Asura_NetLinkManager::DropConnection()
{
	if (s_uStatus == ASURA_NETLINK_CONNECTED)
	{
		ConnectionDropped();
		return true;
	}
	return false;
}

#endif // #ifdef ASURA_SUPPORT_EXTERNAL_NETWORK

