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

	Purpose: 

	Entity class for the ZSimulation framework.  
	
	All objects affecting the simulation will exist as an entity. Entities can either
	be created with state or without state.  Entities with and without state can 
	be messaged by other entities within the simulation.  Entities with state 
	have their state function called once per tick, and this state function
	can modify the entity state, making the entity an autonomous state machine.

	// ENTITY MESSAGING //

	Entities are created in order to be processed in a threaded fashion.  When
	entities have their current state function called, they should be expected to be
	able to process this in a thread-safe manner, able to call their own non-const methods
	but only able to affect other entities via messaging.

	After the state functions are called, remaining messages are delivered to the entity 
	'mailbox'.  Any action that could mutate an entity that is not done from within the
	call to the state function must be handled via a message.

	These delivered messages are then processed in a multi threaded manner, but each entity
	has it's messages processed serially, meaning that modifications can take place as per 
	normal.

	Entity messages are processed by deferring to the handler installed for a particular 
	message	type on an entity.  If no message handler is installed for that message type 
	on an entity, the message handler installed for that type on the simulation message 
	stream will be called.  If no message handler is installed there, the message is 
	dropped.

	// ENTITY PROPERTIES //

	Entities hold their state via allocation into the simulation 'property buffer', which 
	allows for contiguous space allocation for properties to be stored.  The property
	buffer is a raw memory allocator and does not care what the underlying data actually 
	is.

	Entity properties can be allocated as 'local' or 'synchronized'.  Data stored locally
	is not updated to other simulation instances.  Data stored in a 'synchronized' state is
	mirrored to other simulation instances.

	License:

	Copyright 2013, 762 Studios
*/

#pragma once

#ifndef ZENTITY_HPP
#define ZENTITY_HPP

#include <ZUtil/ZRandomGenerator.hpp>
#include <ZUtil/ZName.hpp>

#include "ZSimulationDefs.hpp"
#include "ZPropertyBuffer.hpp"
#include "ZStringBuffer.hpp"

// forward decl
class  ZSimulation;
struct ZMessage;

/*
Entity class.  
*/
class ZEntity
{
public:
	/*
	This is the entity state typedef, which corresponds to 'AI State'.  The current think state
	is called once per simulation tick.

	@param entity - the entity who is thinking
	@param sim - the running simulation
	@param dt - the time (expressed in seconds) since last call of this function
	*/
	typedef void (*StateFunc)(ZEntity& entity, const ZSimulation& sim, double dt);

	/*
	This is the entity message handler typedef, which corresponds to 'Behavior'.  If there is no message
	handler installed for a given message type, the default handler installed in the message queue
	is used.

	@param entity - the entity receiving the message
	@param sim - the running simulation
	@param sender - the sending entity
	@param payload - the payload of the message
	*/
	typedef void (*MessageFunc)(ZEntity& entity, const ZSimulation& sim, eID sender, void* payload);

	/*
	This is the entity initialization function typedef, which can be passed to the simulation to initialize 
	the entity upon creation.

	@param entity - the entity to initialize
	@param sim - the running simulation
	*/
	typedef void (*InitFunc)(ZEntity& entity, const ZSimulation& sim);

	/*
	Entity property class.  An ease of access class for reading and writing
	property data.

	This class does not handle type coercion on read.  Types will change when
	values are set.
	*/
	struct Property {

		// we'll be using this a lot
		typedef ZStringBuffer::StringKey StringKey;

		enum {
			BOOL,		// boolean type
			INT,		// integer type
			FLOAT,		// floating point type
			DOUBLE,		// double precision floating point type
			STRING,		// string type (stored in string buffer or dynamic, limited size)
			VECTOR,		// vector type (4 floating point values)
			MATRIX,		// matrix type (16 floating point values)

			NONE		// untyped data

		}		Type;		// the type of property

		ZString LocalString; // local storage for strings
		uint8_t Local[64];	 // local storage for simple types
		void*	Data;		 // data for more complex types
		size_t  Size;		 // size of data (in bytes)

		// default constructor, which initializes an empty string
		Property();

		// generic and array constructor, which takes data and size
		Property(void* data, size_t size);

		// value constructors
		Property(const Property& other);
		Property(const bool val);
		Property(const int val);
		Property(const float val);
		Property(const double val);
		Property(const char* val);					// checks to see if this string has a string key, if not makes a dynamic string	
		Property(const StringKey val);				// quicker method of directly creating a static string type
		Property(const SST_Vec4f& val);				
		Property(const SST_Vec3f& val);				// convenience for SST_Vec4f(x, y, z, 0.0)
		Property(const SST_Vec2f& val);				// convenience for SST_Vec4f(x, y, 0.0, 0.0)
		Property(const SST_Mat44f& val);	

		// assignment operators
		Property& operator = (const Property& other);
		Property& operator = (const bool& val);
		Property& operator = (const int& val);
		Property& operator = (const float& val);
		Property& operator = (const double& al);
		Property& operator = (const char* val);
		Property& operator = (const StringKey val);		// quicker method of using a static string type
		Property& operator = (const SST_Vec4f& val);	
		Property& operator = (const SST_Vec3f& val);	// convenience for SST_Vec4f(x, y, z, 0.0)
		Property& operator = (const SST_Vec2f& val);	// convenience for SST_Vec4f(x, y, 0.0, 0.0)
		Property& operator = (const SST_Mat44f& val);	

		// cast operators
		operator bool		() const;
		operator int		() const;
		operator float		() const;
		operator double		() const;
		operator char*		() const;
		operator bool*		() const;
		operator int*		() const;
		operator float*		() const;
		operator double*	() const;
		operator SST_Vec4f  () const;
		operator SST_Vec3f  () const;	// gets [x, y, z] only
		operator SST_Vec2f  () const;	// gets [x, y] only
		operator SST_Mat44f () const;	
	};
	
	// c'tor
	ZEntity(eID entityId, ZSimulation& sim);

	// parameterized c'tor (creates an actor)
	ZEntity(eID entityId, ZSimulation& sim, StateFunc startState);

	// d'tor
	~ZEntity();

	//////////////////////////////////////////////////////////////////////////
	// Member Data Getter Functions 

	eID						GetId() const;
	ZRandomGenerator&		GetPRNG();

	//////////////////////////////////////////////////////////////////////////
	// Property Manipulation Functions

	/*
	Property access functions.  Used to create, remove, read, and write properties
	and their values.

	The 'HasProperty' function will return true if the named property exists on
	this entity.
	
	The 'CreateSyncProperty' functions is used to declare a synchronized property
	on this entity.  Synchronized properties are automatically mirrored across
	the network system whenever a change is made.  If the 'owner' boolean is
	set to true, modifications to the entity on this simulation are synchronized
	to other simulations.  If the owner boolean is set to false, modifications 
	are not synchronized, and the property will be updated periodically from
	other simulations.  Only one simulation should be the owner of an entity,
	or they will update over each other.

	The 'GetProperty' function will fill out a Property instance that references
	the read value of the named property.  If the named property does not exist
	on this entity, the function will return false.

	The 'SetProperty' function will set the write value of the property to be
	the value provided.  If the named property does not exist, it will be created
	as a non-synchronized property.  Note that the read value is not updated until 
	the next simulation tick.  

	The 'EraseProperty' function will queue the property for removal at the end
	of this tick, which allows 'get' operations to function as normal until the
	next tick.

	The template 'Get' function is a shortcut to directly get the value of 
	a specific property.  Note that if the property does not exist, this will
	return a default constructed property value.
	*/

	bool HasProperty(const ZName& name) const;
	void CreateSyncProperty(const ZName& name, const Property& prop, bool owner);
	bool GetProperty(const ZName& name, Property& val) const;
	void SetProperty(const ZName& name, const Property& val);
	void EraseProperty(const ZName& name);

	template <typename T>
	T Get(const ZName& name) const
		{ Property prop; GetProperty(name, prop); return (T)prop; }

	template <typename T>
	void Set(const ZName& name, const T val)
		{ Property prop(val); SetProperty(name, prop); }
	
	/*************************************************************************/

	/* State Management Functions */

	/*
	State functions.  Used to get information about the current state and 
	manipulate the state stack, as well as call the current state function.

	Push and Pop operate as you would expect for a state stack.  The maximum
	number of states is defined by ZENT_STACK_SIZE.

	SetState sets the current state to the given state, replacing it.

	ResetState clears the state stack and sets the current state to be the
	provided one.
	*/

	StateFunc	GetCurrentState() const;
	double		GetTimeInCurrentState() const;
	void		PushState(StateFunc state);
	void		PopState();
	void		SetState(StateFunc state);
	void		ResetState(StateFunc state);

	/*
	State property stack manipulation functions.  Used to read and write information
	to the state property stack, which can be used to pass transient information from
	one state function to another.
	*/

	size_t	 GetStatePropertyCount() const;
	Property PopStateProperty();
	void	 PushStateProperty(const Property& prop);	
	void	 ClearStatePropertyStack();

	/*************************************************************************/

	/* Messaging Management Functions */

	/*
	Messages, once posted to the message queue, can be 'delivered' to entities for 
	sorting purposes to	be later processed in a convenient fashion.

	When processing delivered messages, order of delivery is maintained.  Any 
	messages that do not have a corresponding handler type installed will be 
	handled by the handler installed in the message stream.  If no corresponding 
	handler is installed there, the message is dropped, and the simulation is
	notified.
	*/

	void DeliverMessage(ZMessage* msg);
	void ProcessMessages();
	void PushMessageHandler(mID type, MessageFunc handler);
	void PopMessageHandler(mID type);
	void ClearMessageHandlers();

protected:
	/*
	Typedef for the property map used by ZEntity.  It has a bucket size of 32, local
	storage for the buckets of 32, 10 local nodes for each hash-chain, 10 local storage
	for each property array, and will not resize based on load factor.  Uses a 64-bit
	hash value.
	*/
	typedef ZHashMap<ZName, ZPropertyBuffer::Property, int64_t, ZHasher<ZName, int64_t>, 0> PropMap;

	/*
	Stack used to indicate which properties have been modified since last swap of properties.
	*/
	typedef ZArray<ZPair<ZName, ZPropertyBuffer::Property>> PropModifiedStack;

	/*
	Stack used to indicate which properties have been deleted since last swap of properties.
	*/
	typedef ZArray<ZName> PropDeletedStack;

	/*
	Stack used for state properties, which can be used to send data from one state to another.
	*/
	typedef ZArray<Property> StatePropStack;
	
	/*
	Buffer used as an entity mailbox - targeted messages are delivered here.
	*/
	typedef ZRingBuffer<ZMessage*> Mailbox;

	/*
	Typedef for the handler map used by ZEntity, which maps message type to a 
	stack of handlers.  Only keeps two local nodes in the allocator, and will not 
	resize based on load factor.
	*/
	typedef ZHashMap<mID, ZArray<MessageFunc>, int64_t, ZHasher<ZName, int64_t>, 0, 
					 ZListPooledAllocator<ZHashNode<mID, ZArray<MessageFunc>, int64_t>, 2 >> HandlerMap;

	/* Member Data */
	ZSimulation&		Sim;									// the simulation running this thing
	const eID			Id;										// the id of the entity
	ZRandomGenerator	PRNG;									// entity PRNG, kept so there are no thread conflicts with PRNG access

	/* Property Management */
	PropMap				PropertyMap;							// entity properties map
	PropModifiedStack	PropertyModifiedStack;					// properties that need to be mirrored into the read properties map
	PropDeletedStack	PropertyDeletedStack;					// properties have been deleted

	/* State Management */
	int					StateIndex;								// index to our current state (-1 indicates no state)
	StateFunc			StateStack[ZSIM_ENT_STATE_STACK_SIZE];		// stack of states (StateStack[StateIndex] is our current)
	StatePropStack		StatePropertyStack;						// stack of properties so that states can pass data to each other
	double				StateTime;								// amount of time we have spent in our current state
	
	/* Message Management */
	Mailbox				MessageMailbox;							// entity 'Mailbox', which is where messages get delivered to
	HandlerMap			MessageHandlers;						// entity handlers, used to process received messages

private:
	friend class ZSimulation;
	friend class ActorTickTask;
	DISABLE_COPY_AND_ASSIGN(ZEntity);

	// allocates a new property
	// TODO

	// called by the simulation to tick the current state
	void StateTick(double dt);

	// called by the simulation to dealloc properties
	void HandlePropertyDelete();
};

#endif
 
