//===========================================================================//
// File:        l4net.hh                                                     //
// Project: MUNGA           Brick: Network Manager                           //
// Contents: Interface specification for network brick                       //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 03/02/95  GAC  Initial coding.                                            //
//---------------------------------------------------------------------------//
// Copyright (C) 1994-1995, Virtual World Entertainment, Inc.                //
//                      PROPRIETARY AND CONFIDENTIAL                         //
//===========================================================================//

#pragma once

#include "l4host.h"

#include "..\munga\network.h"
#include "..\munga\hostmgr.h"

//WinSock support :ADB 01/06/07
//#include "..\munga\netnub.h"
#define	NETNUB_TCP_OPEN         3		// Opens a TCP stream to another computer
#define	NETNUB_TCP_LISTEN       4     // Queue's a TCP listen that we can accept a connection on
#define MULTIPLE_SEND_PACKET_MAX		10
#define	MAX_RECEIVE_DATA_SIZE	1600
#include <Winsock2.h>
#include <Ws2tcpip.h>

class NotationFile;
class L4NetworkManager__ReceiveEggFileMessage;
class	L4NetworkManager__AcknowledgeEggFileMessage;
class L4NetworkManager__HostConnectedMessage;
class L4NetworkManager__HostDisconnectedMessage;

class NetNub
{
	friend int
		Netnub_Open_File(
			const char* filename,
			int access,
			unsigned int model
		);
	friend int
		Netnub_Write_File(
			int handle,
			void *buffer,
			size_t length
		);
	friend int
		Netnub_Close_File(int handle);

public:
	//WinSock support :ADB 01/06/07
	//static void SendCommand();
};

//extern Netcom_Ptr
//	Net_Common_Ptr;

//~~~~~~~~~~~~~~~~~~~~~ MessageQueue__SendRequest ~~~~~~~~~~~~~~~~~~~~~~~~~~

class MessageQueue__SendRequest:
	public Plug
{
	friend class HostMessageBuffer__MessageQueue;
	friend class L4NetworkManager__MessageBuffer;
	
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private methods
//
private:
	MessageQueue__SendRequest(
		NetworkClient::ClientID client_ID,
		Receiver::Message *message
	);
	~MessageQueue__SendRequest();
	
	Logical
		TestInstance() const;
		
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
private:
	NetworkClient::ClientID
		clientID;
	Receiver::Message
		*messageToSend;
};

//~~~~~~~~~~~~~~~~~~~ HostMessageBuffer__MessageQueue ~~~~~~~~~~~~~~~~~~~~~~

class HostMessageBuffer__MessageQueue:
	public Node
{
	friend class L4NetworkManager__MessageBuffer;
	
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private methods
//
private:
	HostMessageBuffer__MessageQueue(HostID host_ID);
	~HostMessageBuffer__MessageQueue();
	
	Logical
		TestInstance() const;
		
	void
		AddSendRequest(
			NetworkClient::ClientID client_ID,
			Receiver::Message *message
		);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
private:
	HostID 
		hostID;
		
	typedef MessageQueue__SendRequest
		SendRequest;
	ChainOf<SendRequest*>
		sendRequestSocket;
};

//~~~~~~~~~~~~~~~~~~ L4NetworkManager__MessageBuffer ~~~~~~~~~~~~~~~~~~~~~~~

class L4NetworkManager__MessageBuffer:
	public Node
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Public methods
//
public:
	L4NetworkManager__MessageBuffer(L4NetworkManager *network_manager);
	~L4NetworkManager__MessageBuffer();
	
	Logical
		TestInstance() const;
		
	Logical
		IsEmpty();
	void
		AddSendRequest(
			HostID host_ID,
			NetworkClient::ClientID client_ID,
			Receiver::Message *message
		);
	void
		AttemptToSend();
		
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private data
//
private:
	L4NetworkManager
		*networkManager;
	typedef HostMessageBuffer__MessageQueue
		MessageQueue;
	TableOf<MessageQueue*, HostID>
		messageQueueSocket;
	IteratorPosition
		currentQueueIndex;
	CollectionSize
		bufferSize;
	#ifdef LAB_ONLY
		CollectionSize
			messageCount,
			maxBufferSize;
	#endif
};

inline Logical
	L4NetworkManager__MessageBuffer::IsEmpty()
{
	Check(this);
	return (bufferSize == 0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~ l4Network manager~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class L4NetworkManager:	public NetworkManager
{
	friend class L4NetworkManager__MessageBuffer;
	
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// Constructor, Destructor, Testing
	//
public:
	L4NetworkManager();
	~L4NetworkManager();

	static Logical TestClass();
	Logical TestInstance() const;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Network message methods
//
public:
	void Send(
			Message *what,
			ClientID to,
			HostID host_ID);
	void ExclusiveBroadcast(
			Message *what,
			ClientID to);

	void StartConnecting(Mission *mission);

	Logical Shutdown();

	Logical CheckBuffers(NetworkPacket *packet);

	void RemovePacket(NetworkPacket *packet);
		
	Logical ExecuteBackground();

	void Marker(char *marker_text);

	void Mode(NetworkMode myMode);

	void CreateConsoleHost();

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Network maintenance support
//
public:
	int netPlayerCount;

	enum NetworkState 
	{
		NormalState,
		ConsoleOnly
	};

	enum NetworkStartupMode 
	{
		SlaveMode,
		MasterMode
	};

	bool CheckSocket(SOCKET socket, SOCKADDR_IN *remoteEndpoint);
	bool ResolveAddress(CString host_name, SOCKADDR_IN *address);
	//WinSock support :ADB 01/06/07
	NetworkAddress* GetMyAddress();

	//WinSock support :ADB 01/06/07
	SOCKET OpenConnection(
		int connection_type, // NETNUB_TCP_LISTEN or NETNUB_TCP_OPEN
		int local_port,
		int remote_port,
		int internet_address);
	void CloseConnection(SOCKET socket_ptr); // socket address from netnub (to close)
	Logical GetNextMungaPacket(
			NetworkPacket *network_packet,
			HostManager::RemoteHostIterator* all_iterator);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Shared Data support
//
public:
	static Derivation *GetClassDerivations();
	static SharedData DefaultData;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Message Support
//
public:
	//
	// Message IDs
	//
	enum
	{
		AcknowledgeEggFileMessageID = NetworkManager::NextMessageID,
		HostConnectedMessageID,
		HostDisconnectedMessageID,
		NextMessageID
	};

	//
	// Message types
	//
	typedef L4NetworkManager__AcknowledgeEggFileMessage
		AcknowledgeEggFileMessage;
	typedef L4NetworkManager__HostConnectedMessage
		HostConnectedMessage;
	typedef L4NetworkManager__HostDisconnectedMessage
		HostDisconnectedMessage;
	//
	// Message table
	//
	static const HandlerEntry MessageHandlerEntries[];

	//static MessageHandlerSet MessageHandlers;
	static MessageHandlerSet& GetMessageHandlers();

	void ReceiveEggFileMessageHandler(
			ReceiveEggFileMessage *EggMessage);
	void AcknowledgeEggFileMessageHandler(
			AcknowledgeEggFileMessage *AcknowledgeEgg);
	void HostConnectedMessageHandler(
			HostConnectedMessage *HostConnected);
	void HostDisconnectedMessageHandler(
			HostDisconnectedMessage *ConsoleDisconnect);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private methods
//
private:
	typedef ChainOf<L4Host*>
		DroppedMessageHostSocket;
	typedef ChainIteratorOf<L4Host*>
		DroppedMessageHostIterator;

	Logical
		SendMessageToNetnub(
			Message *message,
			ClientID client_ID,
			HostID host_ID
		);
	void
		SendBatchedMessageToNetnub(
			Message *message,
			ClientID client,
			DroppedMessageHostSocket *dropped_message_host_socket
		);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Private Data
//
private:
	int
		numberOfMungaHostsConnected,
		numberOfConsoleHostsConnected,
		remoteHostCount;
	NetworkStartupMode networkStartupMode;
	Logical eggAcknowledged;
	NetworkState currentNetworkState;
	L4Host *myConsoleHost;
	HostID nextOpenHostID;
	IteratorPosition lastHostIteratorPosition;
		
	typedef L4NetworkManager__MessageBuffer
		MessageBuffer;
	MessageBuffer
		messageBuffer;

	//WinSock Support
	WSADATA* wsaData;
	//hostent* thisHost;
	addrinfo* thisHost;
	SOCKET gameListenerSocket;
	SOCKET consoleListenerSocket;
};

//~~~~~~~~~~~~~~~~~~~~~~ L4NetworkManager inlines ~~~~~~~~~~~~~~~~~~~~~~~~~~

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager__AcknowledgeEggFileMessage
// This message is sent back to the console to acknowledge receipt of the
// egg, it indicates this computer has established all it's connections and
// the console can procede with the next host
//
class L4NetworkManager__AcknowledgeEggFileMessage:
	public NetworkManager::Message
{
public:
	L4NetworkManager__AcknowledgeEggFileMessage():
		NetworkManager::Message(
			L4NetworkManager::AcknowledgeEggFileMessageID,
			sizeof(L4NetworkManager__AcknowledgeEggFileMessage)
		){}
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager__HostConnectedMessage
// This message is generated internally by CheckBuffers and indicates that
// a host has connected up to us.  This is an INTERNAL message only and is
// not ment to be sent on the network.
class L4NetworkManager__HostConnectedMessage :
	public NetworkManager::Message
{
public:
	L4NetworkManager__HostConnectedMessage(HostID host_id, const SOCKADDR_IN &network_address, unsigned long stream_pointer) :
		NetworkManager::Message(L4NetworkManager::HostConnectedMessageID, sizeof(L4NetworkManager__HostConnectedMessage)),
		hostID(host_id),
		networkAddress(network_address),
		streamPointer(stream_pointer)
	{
	}

	HostID hostID;
	SOCKADDR_IN networkAddress;
	unsigned long streamPointer;
};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// L4NetworkManager__HostDisconnectedMessage
// The console is expected to send us this message if it disconnects from us
// for any reason.  This allows the local host to setup the internal state
// so the game will auto start even if the console can't stay connected to us
// for some reason (for example, the console simulator in the game code can't
// connect to more than one host at once).
// !!! CheckBuffers should poll the connection state of all the streams and
// generate this message automatically whenever a host drops off the net.
// !!! HACK At the moment this message is always treated as if it came from
// the console host, so it should only be sent down the console stream.
//
class L4NetworkManager__HostDisconnectedMessage :
	public NetworkManager::Message
{
public:
	L4NetworkManager__HostDisconnectedMessage(HostID host_id, unsigned long stream_pointer) :
		NetworkManager::Message(L4NetworkManager::HostDisconnectedMessageID, sizeof(L4NetworkManager__HostDisconnectedMessage)),
		hostID(host_id),
		streamPointer(stream_pointer) 
	{
	}

	HostID hostID;
	unsigned long streamPointer;
};