#include "munga.h"
#pragma hdrstop

#include "hostmgr.h"
#include "interest.h"
#include "icom.h"
#include "app.h"
#include "notation.h"
#include "nttmgr.h"

#if defined(TRACE_ROUTE_PACKET)
	static BitTrace Route_Packet("Route Packet");
#	define SET_ROUTE_PACKET() Route_Packet.Set()
#	define CLEAR_ROUTE_PACKET() Route_Packet.Clear()
#else
#	define SET_ROUTE_PACKET()
#	define CLEAR_ROUTE_PACKET()
#endif

const NetworkAddress NullNetworkAddress = 0x00000000;// 0.0.0.0 (in TCP land);

//#############################################################################
// Shared Data Support
//
NetworkClient::SharedData
	NetworkClient::DefaultData(
		NetworkClient::GetClassDerivations(),
		NetworkClient::GetMessageHandlers()
	);
	
//
//#############################################################################
// Code for the network client class
//#############################################################################
//
Derivation*	NetworkClient::GetClassDerivations()
{
	static Derivation classDerivations(Receiver::GetClassDerivations(), "NetworkClient");
	return &classDerivations;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Constructor for the NetworkClient
//
NetworkClient::NetworkClient(
	ClassID class_ID,
	SharedData &virtual_data,
	ClientID client_ID
):
	Receiver(class_ID, virtual_data)
{
	clientID = client_ID;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Destructor for the NetworkClient
//
NetworkClient::~NetworkClient()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Destructor for the NetworkClient
//
Logical
	NetworkClient::TestInstance() const
{
	return IsDerivedFrom(*GetClassDerivations());
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Receive packet for the NetworkClient
//
void
	NetworkClient::ReceiveNetworkPacket(
		NetworkPacket*,
		Receiver::Message *packet_message
	)
{
	Dispatch(packet_message);
}

//
//#############################################################################
// Code for the network manager class
//#############################################################################

const Receiver::HandlerEntry
	NetworkManager::MessageHandlerEntries[]=
{
	MESSAGE_ENTRY(NetworkManager,ReceiveEggFile)
};

Receiver::MessageHandlerSet& NetworkManager::GetMessageHandlers()
{
	static Receiver::MessageHandlerSet messageHandlers(ELEMENTS(NetworkManager::MessageHandlerEntries), NetworkManager::MessageHandlerEntries, NetworkClient::GetMessageHandlers());
	return messageHandlers;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Shared Data Support
//
NetworkManager::SharedData
	NetworkManager::DefaultData(
		NetworkManager::GetClassDerivations(),
		NetworkManager::GetMessageHandlers()
	);

Derivation* NetworkManager::GetClassDerivations()
{
	static Derivation classDerivations(NetworkClient::GetClassDerivations(), "NetworkManager");
	return &classDerivations;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Constructor for the NetworkManager
//
NetworkManager::NetworkManager(SharedData &shared_data):
	NetworkClient(NetworkManagerClassID, shared_data, NetworkManagerClientID)
{
// basic init goes here
	gameID	= 0;
	addresses = NULL;
	num_addresses = 0;
	eggTempBuffer = NULL;
	networkEggNotationFile = NULL;
   eggTempNext = 0;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Creates a host for us.  Any derived class must deal with reading the
// notation file to get the network nodes to connect to.  This start is
// designed for STAND-ALONE MODE ONLY and should not be inherited for use by
//
void
	NetworkManager::StartConnecting(Mission *)
{
	Check(this);

	//
	//---------------------------------------------------------
	// Make the local host, and get the application to adopt it
	//---------------------------------------------------------
	//
	SOCKADDR_IN address;
	address.sin_family = AF_INET;
	address.sin_addr.S_un.S_addr = 1;
	Host *local_host = new Host(1, GameMachineHostType, &address);
	Register_Object(local_host);

	Check(application);
	Check(application->GetHostManager());
	application->GetHostManager()->AdoptLocalHost(local_host);

	//
	//--------------------------------------------------------------------
	// Now, since this host creator is for stand-alone mode, send the load
	// mission message to the application
	//--------------------------------------------------------------------
	//
	Check(application);
	Application::Message
		load_message(
			Application::LoadMissionMessageID,
			sizeof(Application::Message)
		);
	application->Post(DefaultEventPriority, application, &load_message);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::EndMission  This routine is called when a mission ends to
// allow the network management system to do stuff (like disconnecting network
// connections between pods and so on)
//
Logical
	NetworkManager::Shutdown()
{
	//
	// If there is a notation file floating about, kill it!
	//
	if (networkEggNotationFile)
	{
		Unregister_Object(networkEggNotationFile);
		delete networkEggNotationFile;
	}

	//
	// Deregister local host
	//
	Host *local_host;

	local_host = application->GetHostManager()->OrphanLocalHost();
	Unregister_Object(local_host);
	delete local_host;

	return True;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Destructor for the NetworkManager
//
NetworkManager::~NetworkManager()
{
// shutdown net goes here
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::ReceiveEggFileMessageHandler
//
void
	NetworkManager::ReceiveEggFileMessageHandler(
		ReceiveEggFileMessage* EggMessage
	)
{
	//
	// An egg sequence number of -1 means that the egg is posted locally, and
	// should just call create mission
	//
	if (EggMessage->sequenceNumber == -1)
	{
		application->CreateMission(networkEggNotationFile);
		return;
	}

	if (EggMessage->sequenceNumber == 0)
	{
		//
		// Make a buffer
		//
		eggTempBuffer	=	new char[EggMessage->notationFileLength];
		Register_Pointer(eggTempBuffer);
		eggTempNext		=	0;
	}

	//
	// Write the egg data into the buffer
	//
	memcpy(
		(eggTempBuffer+eggTempNext),
		EggMessage->notationData,
		EggMessage->thisMessageLength
	);
	eggTempNext +=	EggMessage->thisMessageLength;

	//
	// If we don't have all the data, return and wait for more
	//
	if(eggTempNext	<	EggMessage->notationFileLength)
	{
		return;
	}

	//
	// We've got all the data, make the notation file
	//
	networkEggNotationFile	=	new NotationFile();
	Register_Object(networkEggNotationFile);
	networkEggNotationFile->ReadText(eggTempBuffer, eggTempNext);
	networkEggNotationFile->WriteFile("last.egg");

	//
	// Now turn the notation file into a mission
	//
	application->CreateMission(networkEggNotationFile);

	//
	// Get rid of the ram buffer now that we're done with it
	//
	Unregister_Pointer(eggTempBuffer);
	delete eggTempBuffer;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::Send  Handles sending a message to a specific network address
// which can NOT be us.  Default behavior is to do nothing, as no network is
// hooked up if we get here
//
void
	NetworkManager::Send(
		Message *,
		ClientID,
		HostID
	)
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::Broadcast  Handles broadcasting a message to everyone,
// including ourselves.  !!!! Reliable broadcasting is currently used,
// implimented by sending a point-to-point message to every destination.
//
void
	NetworkManager::Broadcast(
		Message *message,
		ClientID client_id
	)
{
	//
	// Send this message back to ourselves
	//
	NetworkClient *client = GetNetworkClientPointer(client_id);
	Check(client);
	client->ReceiveNetworkPacket(NULL, message);

	//
	// Broadcast the message to everyone else on the network
	//
	ExclusiveBroadcast(message,client_id);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::ExclusiveBroadcast  Broadcasts a message to everyone on the
// network execpt us.  Default behavior sends it to nobody, as there is really
// no network
//
void
	NetworkManager::ExclusiveBroadcast(
		Message *,
		ClientID
	)
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::GetNetworkClientPointer  Determine who the client of a
// message is and return a pointer to them.
//
NetworkClient*
	NetworkManager::GetNetworkClientPointer(ClientID client_id)
{
	switch (client_id)
	{
	case NetworkClient::NetworkManagerClientID:
		return this;

	case NetworkClient::EntityManagerClientID:
		Check(application);
		return application->GetEntityManager();

	case NetworkClient::HostManagerClientID:
		Check(application);
		return application->GetHostManager();

	case NetworkClient::InterestManagerClientID:
		Check(application);
		return application->GetInterestManager();

	case NetworkClient::IcomManagerClientID:
		Check(application);
		return application->GetIntercomManager();

	case NetworkClient::ApplicationClientID:
		Check(application);
		return application;

	default:
		DEBUG_STREAM << "------UNKNOWN NETWORK CLIENT ID " << client_id << std::endl << std::flush;
		Fail("Unknown Client ID");
		break;
	}
	return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::RoutePacket
//
Logical
	NetworkManager::RoutePacket()
{
	Check(this);

	Byte
		message_buffer[NETWORKMANAGER_BUFFER_SIZE];
	NetworkPacket
		*p;

	//
	// Return if check buffers doesn't have a packet for us
	//
	if (!CheckBuffers((NetworkPacket*)message_buffer))
	{
		return False;
	}

	//
	// Process and route the packet properly
	//
	p = (NetworkPacket*)message_buffer;

	if (p->gameID == gameID)
	{
//		cout<<"++++++++client "<<p->clientID<<" gameID "<<p->gameID<<" fromHost "<<p->fromHost<<"\n";
		NetworkClient *client = GetNetworkClientPointer(p->clientID);

		Check(client);
		SET_ROUTE_PACKET();
		client->ReceiveNetworkPacket(p, &p->messageData);
		CLEAR_ROUTE_PACKET();
	}
	else
	{
		DEBUG_STREAM << "+++++++++ client " << p->clientID << " gameID " << p->gameID << " fromHost " << p->fromHost << std::endl << std::flush;
		Fail("######### NOT A PACKET FOR THIS GAME!");
	}

	RemovePacket(p);
	return True;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::ExecuteBackground  Allows the network to perform tasks in 
// the applications background.  The method should return True if the manager
// is busy, false otherwise.  For example, if the input and output buffers of
// the network driver are not empty then return True, otherwise return False.
//
Logical
	NetworkManager::ExecuteBackground()
{
	return True;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::CheckBuffers  Checks to see if there is a message for us
// buffered up in the network interface card and returns a pointer to it.
//
Logical
	NetworkManager::CheckBuffers(NetworkPacket*)
{
	Fail("NetworkManager::CheckBuffers - should never reach here!");
	return False;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// NetworkManager::RemovePacket  If the network implimentation requires us to
// free up a packet buffer, this routine would do it.
//
void
	NetworkManager::RemovePacket(NetworkPacket*)
{
	Fail("NetworkManager::RemovePacket - should never reach here!");
}
