/*
	ZNetworkEvent.hpp
	Author: James Russell <jcrussell@762studios.com>
	Created: 9/13/2015

	Purpose: 

	Network Event abstract class.  Intended to be subclassed into various subclasses that will be 
	serialized / deserialized and processed on both client and server.

	Note that events that originate on the server will generally not have their HandleServer method 
	called, as the server already processed the occurrence that originated the event.  Events 
	that originate on the client will generally be pushed to the server, have HandleServer
	called on them, which will generally also cause an event to be pushed to each client, who
	will then have HandleClient called when the event gets deserialized.

	From the above usage pattern, we can see that clients generally push events to the server but
	do not process the local occurrence (the HandleClient) until after the server has confirmed
	the event happens by pushing the event back to all clients, the original sender included.

	This usage pattern is not required for client authoritative events, which are processed locally
	and then handed to the server to notify other attached clients.

	License:

	TODO

*/

#pragma once

#ifndef _ZNETWORKEVENT_HPP
#define _ZNETWORKEVENT_HPP

#include "ZSimulationDefs.hpp"

// forward decl
class ZSimulation;

/*
ZNetworkEvent class.
*/
class ZNetworkEvent {
DISABLE_COPY_AND_ASSIGN(ZNetworkEvent);
public:
	
	nID  Type;		// the type of event this is 
	cID	 NetTarget;	// the connection this is targeting
	cID	 NetIgnore;	// if NetTarget is set to all, this target will be ignored
	bool InQueue;	// flag set during a handle method to indicate this has been queued up for sending

	// handler for events on client side
	virtual void HandleClient(ZSimulation& server_sim) = 0;

	// handler for events on server side
	virtual void HandleServer(ZSimulation& client_sim) = 0;

	// called by the network system to deserialize this event from binary form (after header is read)
	virtual bool Deserialize(ZBinaryBufferReader& reader) = 0;

	// called by the network system to serialize this event into binary form
	virtual void Serialize(ZBinaryBufferWriter& writer) = 0;

	// called to get the serialized size of this event (including uint8_t message type flag)
	virtual size_t GetSerializedSize() = 0;	

	// called by subclass to serialize the header (Type, PlayerSource, and PlayerTarget)
	void SerializeHeader(ZBinaryBufferWriter& writer) {
		writer.WriteU32(Type);
	}

	// called by subclass to get serialized header size
	size_t GetHeaderSize() {
		return sizeof(nID);
	}

protected:
	// c'tor
	ZNetworkEvent(nID type) : Type(type) { } 
};

/*
Network Event base implementation, which attempts to automatically handle serialization and
deserialization via template POD structs.

Note the template on the game event (DS), which indicates a POD struct.  This is the data
that is serialized and sent as part of the event.  Wrap the struct definition in pragma 
pack statements to ensure correct behavior across systems and reduce bandwidth.
*/
template <typename DS>
class ZNetworkEventBase : public ZNetworkEvent
{
DISABLE_COPY_AND_ASSIGN(ZNetworkEventBase);
public:
	// the data struct for this event
	DS EventData;

	// called by the network system to deserialize this event from binary form (after header is read)
	virtual bool Deserialize(ZBinaryBufferReader& reader) {
		if (reader.CanRead(sizeof(DS))) {
			reader.ReadU8Array((uint8_t*)&EventData, sizeof(DS));
			return true;
		} else return false;
	}

	// called by the network system to serialize this event into binary form
	virtual void Serialize(ZBinaryBufferWriter& writer) {
		SerializeHeader(writer);
		writer.WriteU8Array((uint8_t*)&EventData, sizeof(DS));
	}

	// called to get the serialized size of this event
	virtual size_t GetSerializedSize() {
		return GetHeaderSize() + sizeof(DS);
	}

protected:
	// c'tor
	ZNetworkEventBase(nID type) : ZNetworkEvent(type) { }
};

#pragma pack (push, 1)

/*
Data struct used for message events, which will simply create the 
given message on the other side and pass it to the simulation.
*/
template <typename M>
struct ZMessageEventData {
	mID Type;
	eID Sender;
	eID Target;
	M   MessageData;

	cID Connection;
};

#pragma pack (pop)

/*
Message network event.  This is used to duplicate a message on other
connected simulations.  The template parameter type is the message 
data definition struct.  Be sure to use pragma pack to reduce network
overhead.
*/
template <typename MT>
class ZMessageEvent : public ZNetworkEventBase<ZMessageEventData<MT>> {
public:
	// c'tor
	ZMessageEvent(mID msg_type) : ZNetworkEventBase(0) { }
	ZMessageEvent(mID msg_type, eID sender, eID target, MT payload, cID connection) 
		: ZNetworkEventBase(0), EventData({msg_type, sender, target, connection, payload}) { }

	// subclass implementations
	virtual void HandleClient(ZSimulation& server_sim) {
		server_sim.SendLocalMessage(Data.Type, Data.Sender, Data.Target, Data.MessageData);
	}

	virtual void HandleServer(ZSimulation& client_sim) {
		client_sim.SendLocalMessage(Data.Type, Data.Sender, Data.Target, Data.MessageData);
	}
};


#endif