/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include <cstring>
		#include "network_enet/enet_network.h"
		#include "nstring/nstring.h"
		#include "thread/mutex.h"
		#include "log/log.h"

		using GS::String;
		using namespace GS::Network;


//------------------------------------------------------------------------------
void				Enet::GetStatistics(Statistics &stat)
{
	stat.received_data = host->totalReceivedData;
	stat.sent_data = host->totalSentData;
}
int					Enet::GetPeerPacketLossRatio(void *peer)
{
	ENetPeer *enet_peer = (ENetPeer *)peer;
	return enet_peer && enet_peer->packetsSent ? (enet_peer->packetsLost * 100) / enet_peer->packetsSent : 0;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Enet::SetPeerTimeout(void *peer, Timeout timeout)
{
	int k = 1;

	switch (timeout)
	{
		case TimeoutLong:		k = 2;	break;
		case TimeoutVeryLong:	k = 4;	break;

		default: break;
	}

	enet_peer_timeout((ENetPeer *)peer, ENET_PEER_TIMEOUT_LIMIT * k, ENET_PEER_TIMEOUT_MINIMUM * k, ENET_PEER_TIMEOUT_MAXIMUM * k);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Enet::UpdateHost()
{
	if	(host)
	{
	    ENetEvent event;
		while (enet_host_service(host, &event, 0) > 0)
			switch (event.type)
			{
				case ENET_EVENT_TYPE_CONNECT:
					OnPeerConnection(event.peer);
					break;

				case ENET_EVENT_TYPE_RECEIVE:
					OnPacketReceived(event.peer, (void *)event.packet->data, (size_t)event.packet->dataLength);
					enet_packet_destroy(event.packet);
					break;

				case ENET_EVENT_TYPE_DISCONNECT:
					OnConnectionClosed(event.peer);
					break;

				default: break;
			}
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Enet::Close()
{
	if	(host)
	    enet_host_destroy(host);
	host = NULL;
}
bool				Enet::OpenServer(const char *hostname, int port)
{
	Close();

	ENetAddress	address;
	if	(hostname)
		enet_address_set_host(&address, hostname);
	else
		address.host = ENET_HOST_ANY;
    address.port = (enet_uint16)port;

	__LOG__ << "Starting server on " << (hostname ? hostname : "ANY") << ":" << port << "... ";
	if	((host = enet_host_create(&address, 4, 0, 0, 0)) != NULL)
		__LOG__ << "OK\n";
	else
		__LOG__ << "FAILED\n";
/*
	if	(host)
		enet_host_compress_with_range_coder(host);
*/
	return asbool(host);
}
bool				Enet::OpenClient(const char *hostname, int port)
{
	Close();

	if	((host = enet_host_create(NULL, 1, 0, 0, 0)) == NULL)
		return false;

//	enet_host_compress_with_range_coder(host);

	ENetAddress	address;
	enet_address_set_host(&address, hostname);
	address.port = (enet_uint16)port;

	// Connect on one channel to server.
	__LOG__ << "Opening client connection to " << hostname << ":" << port << "... ";
	bool r = asbool(enet_host_connect(host, &address, 1, 0));
	if	(r)
		__LOG__ << "OK.\n";
	else
		__LOG__ << "FAILED\n";
	return r;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Enet::GetHostAddress(String &address)
{
	if	(host)
	{
		char ip[64];
		if	(enet_address_get_host_ip(&((ENetHost *)host)->address, ip, 63) < 0)
			return false;
		address = ip;
	}
	return true;
}
bool				Enet::GetPeerAddress(void *peer, String &address)
{
	if	(peer)
	{
		char ip[64];
		if	(enet_address_get_host_ip(&((ENetPeer *)peer)->address, ip, 63) < 0)
			return false;
		address = ip;
	}
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				Enet::Send(void *peer, const void *data, size_t size)
{
//	__LOG_V__ << "Send Enet packet of " << size << " bytes.\n";

	ENetPacket *packet = enet_packet_create(data, size, ENET_PACKET_FLAG_RELIABLE);
	if	(!packet || enet_peer_send((ENetPeer *)peer, 0, packet))
		return false;
	enet_host_flush(host);
	return true;
}
bool				Enet::Broadcast(const void *data, size_t size)
{
	ENetPacket *packet = enet_packet_create(data, size, ENET_PACKET_FLAG_RELIABLE);
	if	(!packet)
		return false;
	enet_host_broadcast(host, 0, packet);
	enet_host_flush(host);
	return true;
}
void				Enet::Disconnect(void *peer)
{
	if	(peer)
		enet_peer_disconnect((ENetPeer *)peer, 0);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
Enet::Enet()
{
	host = NULL;
	enet_initialize();
}
Enet::~Enet()
{
	Close();
	enet_deinitialize();
}
//------------------------------------------------------------------------------
