/*
	ZFrameworkDefs.hpp
	Author: James Russell <jcrussell@762studios.com>
	Created: 2/5/2013

	Purpose: 

	This is our definitions and typedefs file for the ZFramework project.

	License:

	Copyright 2013, 762 Studios
*/

#pragma once

#ifndef _ZSIMULATIONDEFS_HPP
#define _ZSIMULATIONDEFS_HPP

#include "pstdint.h"

#include <SST/SST_Math.h>		// For SST_Vec*, SST_Mat*
#include <SST/SST_NetResult.h>	// For SST_NetResult

#include <ZUtil/ZUtil.hpp>		// For Build Definitions

//////////////////////////////////////////////////////////////////////////
// Defines

#ifndef ZSIM_ENT_STATE_STACK_SIZE
#define ZSIM_ENT_STATE_STACK_SIZE (16)		// size of the entity state stack (maximum number of pushed states)
#endif

#define ZSIM_DEFAULT_HZ		 (10)			// Simulation default Hz
#define ZSIM_EID_MAX		 UINT16_MAX		// Maximum eID that will ever be returned by the simulation
#define ZSIM_EID_SYSTEM		 (0)			// eID that signifies target is 'system' or the running simulation
#define ZSIM_MSG_USER_BASE	 (128)			// This is the base required Id for user messages (anything lower is reserved)

#define ZSIM_SYNCBUFFER_MAX			(1024)			// Maximum size of a buffer-synchronized property
#define ZSIM_STRINGBUFFER_DEFAULT	(65535)			// Default size of the string buffer

#define ZSIM_NETWORK_MAX_PEER_ID		(32)			// Maximum length of a peer id string 
#define ZSIM_NETWORK_SLOT_LOCAL			(0)				// Array position for a local client
#define ZSIM_NETWORK_MAX_PACKETSIZE		(1400)			// Maximum network packet data size (coincidentally, size of the local buffer used to read packets)
#define ZSIM_NETWORK_REPLY_TIMEOUT		(50)			// Timeout on waiting for a reply from a connection
#define ZSIM_NETWORK_LISTEN_PORT		(0x762C)		// Listen port for the simulation (port 30252)
#define ZSIM_NETWORK_PROTOCOL_VERSION	0x1000000		// Protocol version
#define ZSIM_NETWORK_BYTEORDER			SST_BIG_ENDIAN	// Net system byteorder
#define ZSIM_NETWORK_DISCONNECT_TIME	(1000)			// Amount of time (in ms) we will send disconnection notices before giving up
#define ZSIM_NETWORK_MAX_CONNECTIONS	(64)			// Default number of maximum connections
#define ZSIM_NETWORK_MAX_CHANNELS		(3)				// Maximum number of network channels
#define ZSIM_NETWORK_CH_SYSTEM			(0)				// System Net Channel
#define ZSIM_NETWORK_CH_UPDATE			(1)				// Update Stream Net Channel
#define ZSIM_NETWORK_CH_EVENT			(2)				// Game Event Net Channel
#define ZSIM_NETWORK_SEND_RATE			(10)			// Number of buffer updates per second
#define ZSIM_NETWORK_MAX_NAME_LENGTH	(128)			// Maximum id length for a connection (steam max name length is 32, FYI)
#define ZSIM_NETWORK_MAX_HOST_LENGTH	(1024)			// Maximum length for readable host name
#define ZSIM_NETWORK_TARGET_ALL			(UINT16_MAX)	// Indicates the network event should target all connected

#ifndef ZSIM_MESSAGE_SIZE
#define ZSIM_MESSAGE_SIZE		(1024)		// The payload size (in bytes) for a message, which can be interpreted via a layout definition
#endif

#ifndef ZSIM_MESSAGE_ALLOC
#define ZSIM_MESSAGE_ALLOC		(65535)		//The number of messages the message stream holds locally
#endif

//////////////////////////////////////////////////////////////////////////
// Typedefs

// Equivalent to typedef T D;, but makes it a strong type
// ARE YOU NOT ENTERTAINED?!?!
#define strong_typedef(T, D)										\
struct D {															\
	T t;															\
	explicit D(const T _t) : t(_t) {}								\
	D() {};															\
	D(const D& _t) : t(_t) {}										\
	D& operator = (const D& other) { t = other.t; return *this; }	\
	D& operator = (const T& other) { t = other; return *this; }		\
	operator const T& () const { return t; }						\
	operator T& () { return t; }									\
	bool operator == (const D& other) { return t == other.t; }		\
	bool operator < (const D& other) { return t < other.t; }		\
	T value() { return t; } \
}

typedef uint32_t eID;			// entity identifier
typedef uint16_t cID;			// connection identifier
typedef uint32_t nID;			// net event type identifier (0 reserved by simulation)
typedef uint32_t mID;			// message type identifier (0 - 127 are reserved by the simulation)

//////////////////////////////////////////////////////////////////////////
// Enumerations

// base simulation message types
enum ZMessageType {
	ZSIM_MSG_NETWORK_SHUTDOWN		= 1,		// network system has shutdown

	ZSIM_MSG_CONNECTION_ESTABLISHED,			// connection attempt successful
	ZSIM_MSG_CONNECTION_FAILED,					// connection attempt failed
	ZSIM_MSG_CONNECTION_LOST,					// connection lost

	ZSIM_MSG_NODE_JOINED,						// server node has joined the network
	ZSIM_MSG_NODE_LOST,							// server node has left the network

	ZSIM_MSG_ENT_SPAWN,							// spawns an entity
	ZSIM_MSG_ENT_GAIN_STATE,					// entity has gained state (becomes Actor)
	ZSIM_MSG_ENT_LOSE_STATE,					// actor has lost state (becomes Entity)
	ZSIM_MSG_ENT_KILL,							// kills an entity

	ZSIM_MSG_SET_INT_PROPERTY,					// sets an integer entity property
	ZSIM_MSG_SET_REAL_PROPERTY,					// sets a real number entity property
	ZSIM_MSG_SET_STRING_PROPERTY,				// sets a string entity property
	ZSIM_MSG_SET_VEC_PROPERTY,					// sets a vector entity property
	ZSIM_MSG_SET_MAT_PROPERTY,					// sets a matrix entity property
	
	ZSIM_MSG_SHUTDOWN_SIMULATION	= ZSIM_MSG_USER_BASE - 1,	// stops the running simulation and cleans up (can restart)
};

//////////////////////////////////////////////////////////////////////////
// Message Layouts

#pragma pack (push, 1)						// tightly compact data in message structs

#if SST_COMPILER == SST_COMPILER_MSVC
#pragma warning(push)		
#pragma warning(disable:4201)				// Disables the warning about nameless structs
#pragma warning(disable:4100)				// Disables the warning about unreferenced formal parameters
#endif

/* Networking Messages */

/*
Sent to the simulation when the network system thread fails to initialize.
*/
struct ZMessage_NetworkSystemShutdown { 
	int Reason;					// 0 - stop network request, 1 - failed to poll for net events
};

/*
Sent to the simulation when a connection has been established.  Contains information
about the connection and a connection identifier.
*/
struct ZMessage_ConnectionEstablished {
	cID	 ConnectionId;									// id that has been assigned to this connection
	int  ConnectionType;								// 0 - client, 1 - server, 2 - unknown
	char NetworkName[ZSIM_NETWORK_MAX_NAME_LENGTH+1];	// network name given by the other simulation
};

/*
Sent to the simulation when a connection attempt has failed.  Contains information
about the connection attempt.
*/
struct ZMessage_ConnectionFailed {
	int  Reason;										// 0 - unable to resolve host, 1 - no slots available, 2 - malformed response
	char RemoteHost[ZSIM_NETWORK_MAX_HOST_LENGTH+1];	// remote host address that connection failed 
};

/*
Sent to the simulation when a connection has been lost due to error.  Contains information
about the error and a connection identifier.  Any handler for this message should call
'Reset' on the remote peer object - otherwise the connection id remains unused.
*/
struct ZMessage_ConnectionLost {
	int Reason;					// 0 - terminated here, 1 - terminated other side, 2 - connection unresponsive, 3 - malformed data
	cID ConnectionId;			// connection id for connection that was lost
};

/*
Sent to the simulation when another simulation instance is connected to the network.  
Contains the name of the node and the name of its host node.
*/
struct ZMessage_NodeGained {
	char NodeName[ZSIM_NETWORK_MAX_NAME_LENGTH+1];		// network node name of the other simulation
	char HostName[ZSIM_NETWORK_MAX_NAME_LENGTH+1];		// network node name of the host the simulation is connected to
};

/*
Sent to the simulation when a simulation node connection is lost.  Contains the name
as well as a reason flag.
*/
struct ZMessage_NodeLost {
	int  Reason;										// 1 - connection terminated, 2 - connection unresponsive
	char NodeName[ZSIM_NETWORK_MAX_NAME_LENGTH+1];		// network node name of the lost node
};

/* Simulation Messages */

/*
Spawns an entity into the simulation.
*/
struct ZMessage_SpawnEntity {
	eID		   RequestedId;	// id being requested for the entity
	ZHashValue Type;		// the entity type (name hash)
	ZHashValue State;		// the state to spawn with (zero for no state)
};

/*
Notifies the simulation that an entity has gained state, turning a base entity into an
actor entity that will have state ticked.  The sender must be a valid entity that has 
gained an initial state.  This is not needed if the entity is created with state.

Message source is used to find the entity.
*/
struct ZMessage_EntityGainState {
};

/*
Notifies the simulation that an actor entity has lost all state and become a base entity.
Base entities do not have their state ticked by the simulation (as they have none).

Message source is used to find the entity.
*/
struct ZMessage_EntityLoseState {
};

/*
Kills off an entity from the simulation. 

Message target is used to find the entity.
*/
struct ZMessage_KillEntity {
};

/*
Simulation Stop message layout.  This will take a shutdown message and shut down the
simulation.
*/
struct ZMessage_ShutdownSimulation {
	char ShutdownMessage[ZSIM_MESSAGE_SIZE];	// the shutdown message
};

/* Property Set Messages */

/*
All property set messages behave in a similar fashion to cope with the threaded processing
of values.

Provided the original value of the property, this will set it if that value remains the same at 
the time this message is processed.  If the value has changed from the original value, this will 
call the provided callback function to recompute the value. If the provided callback function is 
NULL, set messages will simply apply the value regardless of original value.  

The function pointer always has the following signature:

	T callback_function(const T& new_value);

Where T is the property type.  Note that the behavior of set messages make them unsuitable 
as network messages - rely on synchronized properties instead.
*/

/*
Templated set message.  Contains the new value, the original value, and the name hash which
is used to look up the property.  When this message is processed it will set the property
value regardless of the current read or write value.  

If you wish to have the message take the current value into account, use a lambda message.
*/
template <typename T>
struct ZMessage_Set {
	uint64_t NameHash;
	T		 Value;
};

typedef ZMessage_Set<bool>		 ZMessage_SetBool;
typedef ZMessage_Set<int64_t>	 ZMessage_SetInt;
typedef ZMessage_Set<float>		 ZMessage_SetFloat;
typedef ZMessage_Set<double>	 ZMessage_SetDouble;
typedef ZMessage_Set<SST_Vec4f>  ZMessage_SetVector;
typedef ZMessage_Set<SST_Mat44f> ZMessage_SetMatrix;
typedef ZMessage_Set<char*>		 ZMessage_SetString;

/*
Specialized set string message that holds the string in the message (up to ZSIM_MESSAGE_SIZE).  
Useful when a pointer would leave scope befor the message can be processed.
*/
struct ZMessage_SetStringCopy {
	uint64_t NameHash;
	char	 Value[ZSIM_MESSAGE_SIZE-sizeof(uint64_t)];
};

/*
Templated lambda message.  Simply carries a pointer to a lambda function which is run
on the property when this message is processed.
*/
template <typename T>
struct ZMessage_Lambda {
	uint64_t NameHash;
	T (*LambdaFnc)(const T&);
};

#if SST_COMPILER == SST_COMPILER_MSVC
#pragma warning(pop)
#endif

#pragma pack (pop)

#endif 
