/*
	ZNetServer.hpp
	Author: Patrick Baggett	<ptbaggett@762studios.com>
	Created: 7/10/2013

	Purpose:

	ZNetServer -- extends ZNetHost to provide a server

	License:
	
	Copyright 2013, 762 Studios
*/

#pragma once

#ifndef _ZNETSERVER_HPP
#define _ZNETSERVER_HPP

#include <ZNet/ZNetHost.hpp>
#include <ZNet/ZNetConsts.hpp>
#include <ZSTL/ZHashMap.hpp>
#include <SST/SST_Net.h>

typedef uint32_t (*ZNetConnectCallback)(const SST_NetAddress* addr, uint32_t userdata, void* callbackParam);


class ZNetServer : public ZNetHost
{
	public:
	
		ZNetServer();
		~ZNetServer();
		
		/*
			ZNetServer::Initialize()
			
			Initializes the ZNetServer instance.
			
		*/
		bool Initialize(uint32_t peerCount, uint32_t channelCount);

		/*
			ZNetServer::AddSocket()

			Adds a socket for ZNet to listen on. ZNet may listen on multiple 
			sockets, but once a socket is added, it cannot be removed. This
			limit may be lifted. Only up to ZNET_MAX_SERVER_SOCKETS can be
			added.

			@param s - The socket to listen on.
			@return (bool) - True if successful, false otherwise
		*/
		bool AddSocket(SST_Socket s);

		/*
			ZNetServer::Update()
			
			Checks sockets and packet queues for incoming/outgoing traffic. Use GetEvent() to
			fetch events generated.

			@return (int) - Less than 0: error. 0: OK
		*/
		int Update();

		/*
			ZNetServer::SendPacket()
			
			Sends a packet to a peer via a certain channel. Use ZNetPacket::Release()
			when the packet is no longer needed.
			
			@param packet - The packet to broadcast
			@param peer - The remote host to send to
			@param channelId - The channel ID
		*/
		void SendPacket(ZNetPacket* packet, ZNetPeer* peer, uint8_t channelId);
		
		/*
			ZNetServer::BroadcastPacket()
			
			Sends a packet to all peers via a certain channel. Use ZNetPacket::Release()
			when the packet is no longer needed.
			
			@param packet - The packet to broadcast
			@param channelId - The channel ID
		*/
		void BroadcastPacket(ZNetPacket* packet, uint8_t channelId);

		/*
			ZNetServer::DisconnectPeer()
			
			Gracefully disconnects a peer. The peer is sent a message letting him/her
			that he/she has been disconnected and awaiting confirmation. The reason code
			parameter is sent to the peer.

			@param peer - The peer to signal a disconnect to
			@param reasonCode - The application-specific reason code. This is not interpreted by ZNet in any way.
		*/
		void DisconnectPeer(ZNetPeer* peer, uint32_t reasonCode);

		/*
			ZNetServer::ResetPeer()
			
			Disconnects the peer, but does not inform him/her that the disconnect has occurred.
			This should only be used when a graceful disconnect does not work or when aborting
			the server. No local event is generated, so any cleanup of the peer must be done
			immediately.
			
			@param packet - The packet to broadcast
			@param channelId - The channel ID
		*/
		void ResetPeer(ZNetPeer* peer);
		
		/*
			ZNetServer::NextPeer()
			
			Gets the next peer in list of peers. The ordering is completely arbitrary and should not be
			relied on, thus you should only pass NULL or the return value from an earlier NextPeer() call.
			
			This can be used to iterate through the list of peers:
			
				ZNetPeer* p = Server->NextPeer(NULL);
				while(p)
				{
					DoSomethingWithPeer(p);
					p = Server->NextPeer(p);
				}
				
			@param thisPeer - The peer to get the next of, or NULL to start.
			@return (ZNetPeer*) - The next peer, or NULL if reached the end.
		*/
		ZNetPeer* NextPeer(ZNetPeer* thisPeer);
		
		//======================================================================
		// TRIVIAL GETTER / SETTER
		//======================================================================
	
		/*
			ZNetServer::SetListenFlag()
			
			Sets the listen flag. When true, the server reports incoming connections
			for normal processing. When false, the server does not report incoming connections
			and automatically rejects them.
			
			@param _listenFlag - The new value for the listen flag
		*/
		void SetListenFlag(bool _listenFlag) { listenFlag = _listenFlag; }

		/*
			ZNetServer::SetConnectCallback()

			Sets the connection callback. When non-NULL, the server calls this function when
			a connection attempt is made. Returning 0 indicates the client should be accepted,
			while any other value indicates that the client should be rejected. If no function
			is present, the server automatically accepts the client. This should be used to
			implement ban lists.

			@param fn - The new callback function, or NULL to disable it
		*/
		void SetConnectCallback(ZNetConnectCallback fn) { connectCallback = fn; }

		/*
			ZNetServer::SetCallbackParam()

			Sets the parameter that is passed to the callback. It defaults to NULL.

			@param param - The callback parameter
		*/
		void SetCallbackParam(void* param) { callbackParam = param; }
		
		/*
			ZNetServer::GetListenFlag()
			
			Gets the listen flag. See ZNetServer::SetListenFlag() for a description of the
			listen flag.
			
			@return (bool) - The value of the listen flag
		*/
		bool GetListenFlag() const { return listenFlag; }

		/*
			ZNetServer::GetMaximumClientCount()

			Gets the maximum number of clients that may be simultaneously connected.

			@return (uint32_t) - The maximum number of clients
		*/
		uint32_t GetMaximumClientCount() const { return maxClients; }

		/*
			ZNetServer::GetClientCount()

			Gets the number of clients who are currently connected.

			@return (uint32_t) - The number of clients who are connected.
		*/
		uint32_t GetClientCount() const { return clientCount; }

		/*
			ZNetServer::GetConnectCallback()

			Gets the currently installed callback function, or NULL if none is
			present.
			
			@return (ZNetConnectCallback) - The connect callback
		*/
		ZNetConnectCallback GetConnectCallback() { return connectCallback; }

		/*
			ZNetServer::GetCallbackParam()

			Gets the parameter that is passed to the callback function. This
			defaults to NULL.

			@return (void*) - The callback parameter
		*/
		void* GetCallbackParam() { return callbackParam; }
	private:
		ZNetPeer* peers;		//< The number of peers
		SST_Socket sockets[ZNET_MAX_SERVER_SOCKETS];	//< The sockets that ZNet can listen on
		ZNetConnectCallback connectCallback;
		void* callbackParam;
		uint32_t channelCount;	//< The number of channels
		uint32_t maxClients;	//< The maximum number of clients
		uint32_t clientCount;	//< The current number of clients
		bool listenFlag;		//< The listen flag

		

		void HandlePacket(SST_Socket s, const SST_NetAddress* addr, const uint8_t* data, uint32_t length);

		void TrySendConnResp(SST_Socket s, const SST_NetAddress* addr, uint32_t reasonCode, uint32_t flags);

		ZNetPeer* PeerForAddress(const SST_NetAddress* addr) const;

		ZNetPeer* FindEmptyPeerSlot();
};

#endif

