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


		#include "squirrel_binding.h"
		#include "script/script_variant.h"
		#include "async/async_call_queue_thread.h"
		#include "binding_helpers.h"
		#include "sqstdblob.h"

		using namespace GS;
		using namespace GS::Script;


#if	__PLATFORM_EMSCRIPTEN__ == 0

		#include "network_enet/enet_network.h"

//------------------------------------------------------------------------------
struct	ScriptPeerThread : public Network::Enet, public Threading::ASyncCallQueueThread
{
		void		OnIdle()
		{	UpdateHost();	}

		//----------------------------------------------------------------------
		struct	VMThreadData
		{
			SquirrelVM	*vm;

			void		OnPeerConnection(void *peer)
			{
				if	(peer && vm->SetupFunctionCall("OnPeerConnection"))
				{
					vm->PushVariant(Script::Variant(peer, typetag_Peer));
					vm->DoFunctionCall();
				}
			}
			void		OnPacketReceived(void *peer, const Array <char> &packet)
			{
				if	(peer && vm->SetupFunctionCall("OnPacketReceived"))
				{
					vm->PushVariant(Script::Variant(peer, typetag_Peer));
					vm->PushVariant(Script::Variant((const void *)packet.c_ptr(), packet.GetSize()));
					vm->DoFunctionCall();
				}
			}
			void		OnConnectionClosed(void *peer)
			{
				if	(peer && vm->SetupFunctionCall("OnConnectionClosed"))
				{
					vm->PushVariant(Script::Variant(peer, typetag_Peer));
					vm->DoFunctionCall();
				}
			}

			// To be executed from the VM thread.
			ASync::CallQueue task_queue;

			VMThreadData(SquirrelVM *v) : vm(v) {}
		};

		VMThreadData vm_thread;
		//----------------------------------------------------------------------

		//----------------------------------------------------------------------
		void		OnPeerConnection(void *peer)
		{	vm_thread.task_queue.QueueMemberCall(&vm_thread, &VMThreadData::OnPeerConnection, peer);	}
		void		OnPacketReceived(void *peer, const void *data, size_t size)
		{	vm_thread.task_queue.QueueMemberCall(&vm_thread, &VMThreadData::OnPacketReceived, peer, Array <char> (size, (const char *)data));	}
		void		OnConnectionClosed(void *peer)
		{	vm_thread.task_queue.QueueMemberCall(&vm_thread, &VMThreadData::OnConnectionClosed, peer);	}
		//----------------------------------------------------------------------

		ScriptPeerThread(SquirrelVM *v) : vm_thread(v) {}
};
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
SQInteger			PeerNetOpenServer(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSTRING(ip)
	__SQ_GETINT(port)
	__SQ_GETEND

	ScriptPeerThread *t = new ScriptPeerThread(GetVMObject(vm));
	if	(t == NULL)
		return sq_throwerror(vm, "Failed to allocate peer network controller.");

	t->Start();
	t->QueueMemberCall(t, &ScriptPeerThread::OpenServer, String(ip), port);

	__SQ_RETURNMANAGEDSAFEPTR(t, typetag_PeerController)
}
SQInteger			PeerNetOpenClient(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSTRING(ip)
	__SQ_GETINT(port)
	__SQ_GETEND

	ScriptPeerThread *t = new ScriptPeerThread(GetVMObject(vm));
	if	(t == NULL)
		return sq_throwerror(vm, "Failed to allocate peer network controller.");

	t->Start();
	t->QueueMemberCall(t, &ScriptPeerThread::OpenClient, String(ip), port);

	__SQ_RETURNMANAGEDSAFEPTR(t, typetag_PeerController)
}
SQInteger			PeerNetUpdate(HSQUIRRELVM vm)
{
	__SQ_GETSINGLESAFEPTR(t, ScriptPeerThread, typetag_PeerController)
	t->vm_thread.task_queue.ExecuteAll();
	__SQ_RETURN
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
SQInteger			PeerNetSendString(HSQUIRRELVM vm)
{
	__SQ_GETSTART(3)
	__SQ_GETSAFEPTR(t, ScriptPeerThread, typetag_PeerController)
	__SQ_GETSAFEPTR(peer, ENetPeer *, typetag_Peer)
	__SQ_GETSTRING(data)
	__SQ_GETEND
	t->QueueMemberCall(t, &ScriptPeerThread::SendString, peer, String(data));
	__SQ_RETURN
}
SQInteger			PeerNetBroadcastString(HSQUIRRELVM vm)
{
	__SQ_GETSTART(2)
	__SQ_GETSAFEPTR(t, ScriptPeerThread, typetag_PeerController)
	__SQ_GETSTRING(data)
	__SQ_GETEND
	t->QueueMemberCall(t, &ScriptPeerThread::BroadcastString, String(data));
	__SQ_RETURN
}
//------------------------------------------------------------------------------

#else

SQInteger			PeerNetOpenServer(HSQUIRRELVM vm)
{	return 0;	}
SQInteger			PeerNetOpenClient(HSQUIRRELVM vm)
{	return 0;	}
SQInteger			PeerNetUpdate(HSQUIRRELVM vm)
{	return 0;	}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
SQInteger			PeerNetSendString(HSQUIRRELVM vm)
{	return 0;	}
SQInteger			PeerNetBroadcastString(HSQUIRRELVM vm)
{	return 0;	}

#endif

//------------------------------------------------------------------------------
void				RegisterINetBinding(HSQUIRRELVM vm)
{
/*#
	Topic: Peer Network
	Desc: Implement a peer-to-peer network.
	Type: PeerController
#*/

/*#
	Section: PeerNetHandshake
	Desc: Handshaking
#*/
	/*#
		Func: PeerNetOpenServer
		Proto: PeerController:String ip,int port
		Desc: Open a listening server connection on a given ip address and port.
		Note: A server connection is usually opened on the local host ip "127.0.0.1".
		Example:
// Start a peer server on the local host on port 8000.
PeerNetOpenServer("127.0.0.1", 8000)
	#*/
	sq_register(vm, PeerNetOpenServer, "PeerNetOpenServer", _SC(".si"));
	/*#
		Func: PeerNetOpenClient
		Proto: PeerController:String ip,int port
		Desc: Open a client connection to a given ip address and port.
		Example:
// Connect client to peer server on the remote address 192.168.0.12 on port 8000.
PeerNetOpenClient("192.168.0.12", 8000)
	#*/
	sq_register(vm, PeerNetOpenClient, "PeerNetOpenClient", _SC(".ss"));
/*#
	Section: PeerNetCommunication
	Desc: Communication
#*/
	/*#
		Func: PeerNetSendString
		Proto: void:PeerController controller,Peer peer,String data
		Desc: Send a string to a specific peer controller.
		Example:
// Send the HELLO string to a specific peer.
PeerNetSendString(controller, peer, "HELLO")
	#*/
	sq_register(vm, PeerNetSendString, "PeerNetSendString", _SC(".xxs"));
	/*#
		Func: PeerNetBroadcastString
		Proto: void:PeerController controller,String data
		Desc: Send a string to all peer controllers connected to this controller.
		Example:
// Broadcast the HELLO string to all connected peers.
PeerNetBroadcastString(controller, "HELLO")
	#*/
	sq_register(vm, PeerNetBroadcastString, "PeerNetBroadcastString", _SC(".xs"));
	/*#
		Func: PeerNetUpdate
		Proto: void:PeerController controller
		Desc: Dispatch all pending network events for a controller to the global handler functions.
	#*/
	sq_register(vm, PeerNetUpdate, "PeerNetUpdate", _SC(".x"));
}
//------------------------------------------------------------------------------
