//
// Asura_NetSocket.h
//
// Created by KOR - 03/11/2003 11:10:17
//
// Copyright (C) 2003 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
	#pragma once
#endif

#ifndef __ASURA_NETSOCKET_H__
#define __ASURA_NETSOCKET_H__

#ifdef ASURA_SUPPORT_EXTERNAL_NETWORK

#include "Core/Asura_PlatformBasics.h"
#include "Network/Asura_NetTransport_System.h"
#include "Network/Asura_Platform_NetSocket.h"

#define ASURA_NETSOCKET_FIRST_DYNAMIC_PORT (27015)
#define ASURA_RECEIVE_BUFFER_SIZE			(8192)

class Asura_NetConnection;

struct Asura_NetSocket_MessageInfo
{
	Asura_NetAddress	m_xAddress;
	u_short				m_usPort;
	const char*			m_pcMessage;
	int					m_iLength;
	bool				m_bEncrypted;
};

class Asura_NetSocket
{
public:
	// Constructor
	Asura_NetSocket(u_int uMaxNumConnections);
	// destroy method: calls virtual functions to cleanup connection then calls delete on pointer.
	static void Destroy( Asura_NetSocket* pxSocket );
protected:
	// destructor is protected to force callers to use Destroy() method
	virtual ~Asura_NetSocket(); 
public:
	// Bind() takes IP and Port which can be 0, if so they are determined automatically.
	bool					Bind(Asura_NetAddress xAddress, u_short& usPort ) { return API_Bind(xAddress, usPort); }
	void					Close() { API_Close(); }
	void					Update();
	bool					Send(Asura_NetAddress xAddress, unsigned short usPort, const char* pcMessage, int iLength, bool bEncrypted = true);
	void					FatalError();
	// Force close does not wait for confirmation from remote side
	void					CloseAllConnections(bool bForce, Asura_Close_Reason eReason);
	bool					HandleMessage(const char* message, int len, Asura_NetAddress xAddress, unsigned short port);
	bool					ResetConnection(Asura_NetAddress xAddress, unsigned short usPort);
	Asura_NetConnection*	FindConnection(Asura_NetAddress xAddress, unsigned short usPort) const;
	Asura_NetConnection*	CreateConnection(Asura_NetAddress xAddress, unsigned short uRemotePort, int iTimeOut, bool bBlocking, bool bInitiate);
	// Access methods
	SOCKET					GetSOCKET()						{ return m_xSocket; }
	void					SetListening(bool bListening)	{ m_bListening = bListening; }
	bool					IsConnected()					{ return m_bConnected; }
	void					SetBroadcastEnabled(bool bEnabled);
	bool					SendClosed(Asura_NetAddress xAddress, unsigned short usPort, Asura_Close_Reason eReason );
	void					UseAlternativeProtocol() { m_bUseAlternativeProtocol = true; }

	u_short					GetPort	() const { return m_usPort; }
	Asura_NetAddress		GetAddress() const { return m_xAddress; }

	virtual int				API_GetLastError() = 0;
	
	// if Asura_NetAddress is u_int this function is easy, just return it. On PSP things are harder...
	static u_int			Platform_GetINetAddress( const Asura_NetAddress& xAddress );

protected:
	virtual bool			API_Bind(Asura_NetAddress xAddress, u_short& usPort ) = 0;
	virtual void			API_Close() = 0;
	virtual bool			API_ReceiveMessages() = 0;
	virtual bool			API_SendTo(const Asura_NetSocket_MessageInfo* pxMessageInfo) = 0;
	virtual void			API_SetBroadcastEnabled(bool bEnabled) = 0;
	void					UpdateConnections();
	void					FreeClosedConnections();
	// Encrypts, if necessary, and sends data
	bool					Platform_SendTo(const Asura_NetSocket_MessageInfo* pxMessageInfo);
	// Takes full (encrypted?) message and returns pointer to data as passed into Platform_SendTo
	bool					Platform_GetData(const char*& pcMessage, int& iLength);
	bool					Platform_SetToNonBlocking();
	void					Platform_Update();

	void					RejectConnection(Asura_NetAddress xAddress, u_short& usPort, int iReason);

	// static function for internet simulation code.
	static bool				HandleMessage_Callback( void* pInstance, const char* pcMessage, int iLength, Asura_NetAddress xAddress, unsigned short usPort )
	{
		return static_cast<Asura_NetSocket*>(pInstance)->HandleMessage(pcMessage, iLength, xAddress, usPort);
	}

	// member data
	SOCKET					m_xSocket;
	Asura_NetAddress		m_xAddress;
	unsigned short			m_usPort;
	bool					m_bCloseRequested;
	bool					m_bError;
	bool					m_bConnected;
	bool					m_bListening;
	bool					m_bUseAlternativeProtocol;
	u_int					m_uMaxNumConnections;
	u_int					m_uNumConnections;
	Asura_NetConnection**	m_ppxConnections;

	// RJF - 31 January 2005 - is this bool actually used anywhere?
	// KOR - 17 February 2005 - doesn't look like it...
	//static bool		s_bInitialised;

private:
	// non-copyable 
	Asura_NetSocket& operator= (const Asura_NetSocket&);
	Asura_NetSocket(const Asura_NetSocket&);
};

#endif //#ifdef ASURA_SUPPORT_EXTERNAL_NETWORK

#endif//__ASURA_NETSOCKET_H__
