//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//===========================================================================//

#include "networkserver.h"
#include "networksystem.h"
#include "icvar.h"
#include "filesystem.h"
#include "UDP_Socket.h"
#include "sm_protocol.h"
#include "NetChannel.h"
#include "UDP_Process.h"
#include <winsock.h>
#include "networkclient.h"



//-----------------------------------------------------------------------------
//
// Implementation of CPlayer
//
//-----------------------------------------------------------------------------
CNetworkServer::CNetworkServer( )
{
	m_pSocket = new CUDPSocket;
}

CNetworkServer::~CNetworkServer()
{
	delete m_pSocket;
}

bool CNetworkServer::Init( int nServerPort )
{
	if ( !m_pSocket->Init( nServerPort ) )
	{
		Warning( "CNetworkServer:  Unable to create socket!!!\n" );
		return false;
	}

	return true;
}

void CNetworkServer::Shutdown()
{
	m_pSocket->Shutdown();
}

CNetChannel *CNetworkServer::FindNetChannel( const netadr_t& from )
{
	CPlayer *pl = FindPlayerByAddress( from );
	if ( pl )
		return &pl->m_NetChan;
	return NULL;
}

CPlayer *CNetworkServer::FindPlayerByAddress( const netadr_t& adr )
{
	int c = m_Players.Count();
	for ( int i = 0; i < c; ++i )
	{
		CPlayer *player = m_Players[ i ];
		if ( player->GetRemoteAddress().CompareAdr( adr ) )
			return player;
	}
	return NULL;
}

CPlayer *CNetworkServer::FindPlayerByNetChannel( INetChannel *chan )
{
	int c = m_Players.Count();
	for ( int i = 0; i < c; ++i )
	{
		CPlayer *player = m_Players[ i ];
		if ( &player->m_NetChan == chan )
			return player;
	}
	return NULL;
}

#define SPEW_MESSAGES 

#if defined( SPEW_MESSAGES )
#define SM_SPEW_MESSAGE( code, remote ) \
	Warning( "Message:  %s from '%s'\n", #code, remote );
#else
#define SM_SPEW_MESSAGE( code, remote ) 
#endif
// process a connectionless packet
bool CNetworkServer::ProcessConnectionlessPacket( CNetPacket *packet )
{
	int code = packet->m_Message.ReadByte();
	switch ( code )
	{
	case c2s_connect:
		{
			SM_SPEW_MESSAGE( c2s_connect, packet->m_From.ToString() );

			CPlayer *pl = FindPlayerByAddress( packet->m_From );
			if ( pl )
			{
				Warning( "Player already exists for %s\n", packet->m_From.ToString() );
			}
			else
			{
				// Creates the connection
				pl = new CPlayer( this, packet->m_From );
				m_Players.AddToTail( pl );

				// Now send the conn accepted message
				AcceptConnection( packet->m_From );
			}
		}
		break;
	default:
		{
			Warning( "CNetworkServer::ProcessConnectionlessPacket:  Unknown code '%i' from '%s'\n",
				code, packet->m_From.ToString() );
		}
		break;
	}

	return true;
}


void CNetworkServer::AcceptConnection( const netadr_t& remote )
{
	byte data[ 512 ];
	bf_write buf( "CNetworkServer::AcceptConnection", data, sizeof( data ) );

	buf.WriteLong( -1 );
	buf.WriteByte( s2c_connect_accept );

	m_pSocket->SendTo( remote, buf.GetData(), buf.GetNumBytesWritten() );
}

void CNetworkServer::ReadPackets( void )
{
	UDP_ProcessSocket( m_pSocket, this, this );

	int c = m_Players.Count();
	for ( int i = c - 1; i >= 0 ; --i )
	{
		if ( m_Players[ i ]->m_bMarkedForDeletion )
		{
			CPlayer *pl = m_Players[ i ];
			m_Players.Remove( i );
			delete pl;
		}
	}
}

void CNetworkServer::SendUpdates()
{
	int c = m_Players.Count();
	for ( int i = 0; i < c; ++i )
	{
		m_Players[ i ]->SendUpdate();
	}
}

void CNetworkServer::OnConnectionStarted( INetChannel *pChannel )
{
	// Create a network event
	NetworkConnectionEvent_t *pConnection = g_pNetworkSystemImp->CreateNetworkEvent< NetworkConnectionEvent_t >( );
	pConnection->m_nType = NETWORK_EVENT_CONNECTED;
	pConnection->m_pChannel = pChannel;
}

void CNetworkServer::OnConnectionClosing( INetChannel *pChannel, char const *reason )
{
	Warning( "OnConnectionClosing '%s'\n", reason );

	CPlayer *pPlayer = FindPlayerByNetChannel( pChannel );
	if ( pPlayer )
	{
		pPlayer->Shutdown();
	}

	// Create a network event
	NetworkDisconnectionEvent_t *pDisconnection = g_pNetworkSystemImp->CreateNetworkEvent< NetworkDisconnectionEvent_t >( );
	pDisconnection->m_nType = NETWORK_EVENT_DISCONNECTED;
	pDisconnection->m_pChannel = pChannel;
}

void CNetworkServer::OnPacketStarted( int inseq, int outseq )
{
}

void CNetworkServer::OnPacketFinished()
{
}


//-----------------------------------------------------------------------------
//
// Implementation of CPlayer
//
//-----------------------------------------------------------------------------
CPlayer::CPlayer( CNetworkServer *server, netadr_t& remote ) :
	m_bMarkedForDeletion( false )
{
	m_NetChan.Setup( true, &remote, server->m_pSocket, "player", server );
}

void CPlayer::Shutdown()
{
	m_bMarkedForDeletion = true;
	m_NetChan.Shutdown( "received disconnect\n" );
}

void CPlayer::SendUpdate()
{
	if ( m_NetChan.CanSendPacket() )
	{
		m_NetChan.SendDatagram( NULL );
	}
}