/*
	Lan Wars.
	Copyright (c) 2009, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//=====================================================================================================================
/// @brief		Represents a UDP socket that can be used to send or receive data with one remote computer. 
///				Although UDP is a message orientated protocol, this class saves the address of the foregin machine
///				for ease of use, and also increases reliability by forcing different connections to different 
///				clients to be put on different sockets.
//=====================================================================================================================

#include "Precompiled.h"
#include "Network\DatagramSocket.h"
#include "Network\NetworkCore.h"

using namespace Game;

//=====================================================================================================================
/// @brief		Creates a datagram socket bound to the given network address
/// @param		port - specifies the port to bind the datagram socket to. If the port is zero then this is a client 
///				datagram socket and we don't care about what port we are given, one is generated for us by the network
///				system.
//=====================================================================================================================

DatagramSocket::DatagramSocket( unsigned short port )
{
	// Our port is undefined initially:
	
	m_port = 0xffff;

	// Try and make our socket:
	
	m_socket = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
	
	// See if that succeeded:
	
	if ( m_socket == INVALID_SOCKET )
	{
		// Print an error if using the console:
		
		#ifdef USE_DEBUG_CONSOLE
		
			DebugConsole::Print("\n#ERROR creating a UDP Socket:\n\n");
			DebugConsole::Print( NetworkCore::GetErrorString() );
		
		#endif
		
		// Clear the error
		
		NetworkCore::ClearError();
		
		// Clear this
		
		m_socket = 0;
	}
	else
	{
		// Makeup a sockaddr_in struct
		
		sockaddr_in address; 
		
		memset(&address,0,sizeof(sockaddr_in));
		
		// Set the address and port and family:
		
		address.sin_family				= AF_INET;
		address.sin_port				= htons(port);
		address.sin_addr.S_un.S_addr	= INADDR_ANY;
				
		// Do the bind:
		
		int result = bind( m_socket , (sockaddr*)(&address) , sizeof(sockaddr_in) );
		
		// See if that succeeded:
		
		if ( result == SOCKET_ERROR )
		{
			// If using the console show what happened:
			
			#ifdef USE_DEBUG_CONSOLE
			
				DebugConsole::Print("\n#ERROR binding UDP Socket to localhost addresss:\n\n");
				DebugConsole::Print(NetworkCore::GetErrorString());
			
			#endif
		
			// Clear the error
			
			NetworkCore::ClearError();
		
			// Close the socket:
			
			Close();
		}
		else
		{
			// Save our port:
			
			m_port = ntohs(address.sin_port);
			
			// Are valid:
			
			m_is_valid = true;
		}
	}
}

//=====================================================================================================================
/// @brief		Writes the given data to the socket: returns the number of bytes written
/// @param		data - buffer to write from
/// @param		length - amount of data to write
/// @param		to - network address we are sending to
/// @returns	The number of bytes written, or zero on failure
/// @pre		The given data buffer must not be null
/// @pre		The amount of data to write must be > 0
/// @pre		The given network address should be valid
/// @remarks	Even though 0 is returned on complete failure, the requested amount of data may not be written for
///				whatever reason, so a check should be made for this also.
/// @remarks	This function is thread safe, and can be used concurrently on different threads
//=====================================================================================================================

unsigned DatagramSocket::Write( const void * data , unsigned length , const NetworkAddress & to )
{
	// Make sure ok:
	
	ENSURE( data			, "The given data buffer must not be null"		)
	ENSURE( length > 0		, "The amount of data to write must be > 0"		)
	ENSURE( to.IsValid()	, "The given network address should be valid"	)
	
	// If invalid then do nothing:
	
	if ( IsValid() == false ) return 0;
	
	// Do the write:
	
	unsigned bytes_written = sendto
	(
		m_socket					,		// socket to send on
		(const char*) data			,		// data to send
		length						,		// amount of data to send
		0							,		// flags
		&to.GetWinsockAddress()		,		// winsock address of destination
		sizeof(sockaddr)					// size of destination address
	);
	
	// See if that was ok:
	
	if ( bytes_written != SOCKET_ERROR )
	{
		// Return the number of bytes written
			
		return bytes_written;
	}
	else
	{	
		// Wrote nothing: clear the error
		
		NetworkCore::ClearError();
		
		// Return 0 for an invalid write
		
		return 0;
	}
}

//=====================================================================================================================
/// @brief		Reads data into into the given buffer
/// @returns	The number of bytes read into the buffer or zero on failure
/// @pre		The given data buffer should not be null
/// @pre		The number of bytes to receive should not be 0
/// @remarks	Even though 0 is returned on complete failure, the requested amount of data may not be read for 
///				whatever reason, so a check should be made for this also
/// @remarks	If reading fails, the address and port of from is set to ffffffff:fffff, signifying an invalid address
/// @remarks	This function is thread safe, and can be used concurrently on different threads
//=====================================================================================================================

unsigned DatagramSocket::Read( void * data , unsigned length , NetworkAddress & from )
{
	// Make sure ok:
	
	ENSURE( data		, "The given data buffer must not be null"	)
	ENSURE( length > 0	, "The amount of data to write must be > 0"	)
	
	// If invalid then do nothing:
	
	if ( IsValid() == false ) 
	{
		// Read from invalid address:
		
		from.SetIP(0xffffffff); from.SetPort(0xffff);
		
		// Received no bytes
	
		return 0;
	}
	
	// Store the address we are receiving from here:
	
	sockaddr_in recv_addr; memset(&recv_addr,0,sizeof(sockaddr_in));
	
	// Length of the address we are receiving from:
	
	int recv_len = sizeof(sockaddr_in);
	
	// Do the read:
	
	unsigned bytes_read = recvfrom
	(
		m_socket				,		// socket to read from
		(char*)data				,		// buffer to read into
		length					,		// amount of data to read
		0						,		// flags
		(sockaddr*)&recv_addr	,		// address to receive from
		&recv_len						// length of the address we are receiving from	
	);
	
	// See if that was ok:
	
	if ( bytes_read != SOCKET_ERROR )
	{
		// Set the ip and port of the sender
		
		from.SetIP( ntohl(recv_addr.sin_addr.S_un.S_addr) );
		from.SetPort( ntohs(recv_addr.sin_port) );
		
		// Return the number of bytes read:
		
		return bytes_read;
	}
	else
	{
		// There was an error: set the receiving address to invalid
		
		from.SetPort(0xffff); from.SetIP(0xffffffff);		
		
		// Clear the error
		
		NetworkCore::ClearError();		
		
		// Return 0 for an error
		
		return 0;
	}
}

//=====================================================================================================================
/// @brief		Annouces the type name for this object.
/// @returns	Type name of this object
//=====================================================================================================================

const char * DatagramSocket::GetTypeName() const
{
	return "DatagramSocket";
}
