/*
	ZNetworkService.hpp
	Author: Patrick Baggett <ptbaggett@762studios.com>, James Russell <jcrussell@762studios.com>
	Created: 12/03/2013
 
	Purpose: 
 
	Network functionality for ZSimulation.  Operates a thread that handles sending and
	receiving of network data.  Can be initialized as a server instance of a client 
	instance, with the real difference being that client instances connect to a single 
	server and servers listen for connections from multiple clients and other server
	instances.
 
	License:
 
	Copyright 2013, 762 Studios.
 
 */

#pragma once

#ifndef __ZNETWORKSERVICE_HPP
#define __ZNETWORKSERVICE_HPP

#include <enet/enet.h>
#include <ZUtil/ZUtil.hpp>

#include "ZRemotePeer.hpp"
#include "ZNetworkRequest.hpp"
#include "ZNetworkUpdateStream.hpp"

// forward decl
class ZSimulation;

// class decl
class ZNetworkService
{
public:
	// c'tor
	ZNetworkService(ZSimulation& sim);

	/*
	Initializes the network system as client or server.  The name is for display 
	purposes for both client and server.

	If initialized as a server, the maximum number of simultaneous connections must 
	be specified.
	*/
	bool InitAsClient(const char* name);
	bool InitAsServer(uint32_t maxConnections, uint16_t listenPort, const char* name);

	/* 
	As a client, initializes a connection to the provided remote host.  As a server,
	links to another server instance to share workload.  Delete this when done.
	*/
	ZNetworkRequest* InitiateConnection(const char* remoteHost, uint16_t port);

	/*
	As a client, disconnects from the server (cID for server is always zero).  As 
	a server, disconnects the given	connection.  This is a soft disconnect that 
	notifies the other side and waits for a response.  Delete this when done.
	*/
	ZNetworkRequest* Disconnect(cID id);

	/*
	Generates the disconnection event immediately, but gives no notice to the other 
	side.  Delete this when done.
	*/
	ZNetworkRequest* ResetConnection(cID id);

	/*
	Given a connection id, gets the associated remote peer object.
	*/
	ZRemotePeer* GetPeer(cID id);

	/*
	Given the network name, gets the associated id.  Returns cID(-1) if not connected
	to this simulation instance.
	*/
	cID GetId(const char* netname);

	/*
	Gets a list of node and host pairings on the network.  If the pairing exists in both 
	directions, then the connection is between two servers.
	*/
	void GetTopography(ZArray<ZPair<ZString, ZString>>& mappings);

	// thread control methods
	void SignalAndWaitShutdown();
	void ThreadMain();

private:
	
	// spawns the network thread
	void SpawnThread();

	// main thread = these four functions, in a loop
	void PollNetEvents();
	void ProcessRequests();
	void PushBufferUpdates(uint64_t dt);
	void PushNetworkEvents(uint64_t dt);

	// handlers for requests (return value indicates 'done')
	bool HandleRequest(ZNetworkRequest* req);

		bool HandleConnectRequest(ZNetworkRequest* req);
		bool HandleDisconnectRequest(ZNetworkRequest* req);
		bool HandleResetRequest(ZNetworkRequest* req);
		bool HandleStopNetworkRequest(ZNetworkRequest* req);

	// handlers for ENet Events
	void HandleEvent(ENetEvent* ev);

		// server side
		void ServerHandleConnectEvent(ENetEvent* ev);		// initial connection event from ENet
		void ServerHandleDisconnectEvent(ENetEvent* ev);	// disconnect event from ENet
		void ServerHandleReceiveEvent(ENetEvent* ev);		// data received (any channel)

		void ServerReceiveJoinMessage(ZRemotePeer* client, const void* data, uint32_t len);		// process client (or server) join message

		void ServerReceiveSystemChannel(ZRemotePeer* client, const void* data, uint32_t len);	// data received
		void ServerReceiveUpdateChannel(ZRemotePeer* client, const void* data, uint32_t len);	// data received on update channel
		void ServerReceiveEventChannel(ZRemotePeer* client, const void* data, uint32_t len);	// data received on event channel

		// client side
		void ClientHandleConnectEvent(ENetEvent* ev);		// initial connect event from ENet
		void ClientHandleDisconnectEvent(ENetEvent* ev);	// disconnect event from ENet
		void ClientHandleReceiveEvent(ENetEvent* ev);		// data received (any channel)

		void ClientRecieveJoinResponse(const void* data, uint32_t len);		// process server response from join message

		void ClientReceiveSystemChannel(const void* data, uint32_t len);	// data received on system channel
		void ClientReceiveUpdateChannel(const void* data, uint32_t len);	// data received from server on update channel
		void ClientReceiveEventChannel(const void* data, uint32_t len);		// data received from server on event channel

		// adds and removes a network node mapping
		void AddNode(const ZString& node, const ZString& host);
		void RemoveNode(const ZString& node);

	enum Netstate {
		UNINITIALIZED,		// not yet initialized as client or server
		DISCONNECTED,		// initialized as client and disconnected
		LISTEN,				// initialized as server and listening for connections
		CONNECTING,			// initiating low-level connection
		JOINING,			// connected, but not yet ready to play
		CONNECTED			// connected and ready to SIMULATE
	};

	ZSimulation&  Sim;				// simulation reference
	ZString		  Name;				// network name

	Netstate	  State;			// state of this service
	SST_Thread	  Thread;			// thread running this service
		
	bool		  bIsServer;		// true if initialized as server
	bool		  bIsConnected;		// true if connected
	volatile int  iShouldShutdown;	// shutdown flag

	ENetHost*	  Host;				// enet host service (server and client)
	ENetPeer*	  RemoteServer;		// as a client, this is the server (otherwise NULL)
	uint32_t	  NrConnectionsMax;	// max number of connections
	ZRemotePeer*  Connections;		// all connections (as client, only a single remote peer, the server)

	ZMutex							NodesMutex;		// mutex for locking access to nodes listing
	ZArray<ZPair<ZString, ZString>> Nodes;			// node name and host name pairing for all simulations on the network

	void*		  PacketBuffer;		// packet data buffer, used as local copy
	size_t		  PacketBufferSize;	// size of the buffer

	ZMutex					 QueueMutex;			// mutex for locking access to request queue
	ZArray<ZNetworkRequest*> SharedRequestQueue;	// network request queue
	ZNetworkRequest*		 CurrentRequest;		// network request currently being processed
	uint32_t				 WaitTime;				// value used to modify polling frequency

	ZArray<ZNetworkRequest*> PendingRequests;		// queue used to process a single tick worth of requests

	NetworkUpdateQueue		 DelayedUpdates;		// updates that are being delayed for artificial lag simulation
	NetworkEventQueue		 DelayedEvents;			// events that are being delayed for artificial lag simulation
};

#endif