/***************************************************************************
*                                                                          *
*  Copyright (C) Golaem S.A.  All Rights Reserved.                         *
*                                                                          *
****************************************************************************/

#pragma once

#include "glmTypes.h"
#include "glmMemory.h"
#include "glmGroup.h"
#include "glmAbstractBehavior.h"
#include "glmBehaviorGraph.h"
#include "glmKnowledgeBase.h"

#include "glmString.h"
#include "glmPODArray.h"
#include "glmArray.h"

namespace glm
{
	class Simulation;
	class IContext;

	/**
	* \brief A Golaem Entity Type is a model to create bunch of identical entities.
	*
	* \ingroup simulation
	* \author Golaem <dev@golaem.com>
	*/
	class GLM_SDK_API GroupType
	{
		friend class IContext;
		friend class Simulation;

	public:

		GLM_CLASS_STAT_ID( glm::MemoryStatIds::Simulation);

		virtual ~GroupType();

		inline Simulation* getSimulation() const { return _simulation; }

		bool isIndexInUse(int64_t entityIndex) const { return _inUse[entityIndex] == 1; } // is entity in use (not destroyed) ?

		const int getUpdateOrder() const { return _updateOrder; } // is const once set at construction

		inline int getGroupTypeId() const { return _groupTypeId; } // is const once set at construction

		Group& getGroup(int64_t groupIndex) { return _groups[groupIndex]; }

		const Group& getGroup(int64_t groupIndex) const { return _groups[groupIndex]; }

		const int64_t getMaxCount() const { return static_cast<int64_t>(_groups.capacity()); }
		const int64_t getGroupCount() const { return static_cast<int64_t>(_groups.size()); } // can still be unused

		BehaviorGraph& getBehaviorGraph() { return _behaviorGraph; }

		KnowledgeBase& getKB() { return _knowledgeBase; }	//warning: use the _KBAccessMutex if there is any chance of multithreading access with writing

		void addBehavior(AbstractBehavior*, bool startAtInit = true);

		glm::GlmString	_name;

		mutable RWSpinLock _KBAccessMutex;	//mutex to use access the KB for reading or writing (if needed)

		// for visualization
		uint32_t	_colorABGR;

		float	_groupLaxness;

	protected:

		int64_t createGroup(const int64_t currentUniqueGroupId); // return groupIndex in this array
		//void initGroup(int groupIndex);
		void destroyGroup(int64_t groupIndex);

		void createGroups(int64_t* groupIndices, int64_t count, int64_t& currentUniqueGroupId);
		void destroyGroups(int64_t* groupIndices, int64_t count);

		//void initGroups();
		void updateGroups();
		void clear();

		void setComponentsInitialized(bool value);

		void setSimulation(Simulation* simulation) { _simulation = simulation; }

	protected:

		GroupType(int64_t maxGroups = 0, int updateOrder = 0);

		Array< Group >			_groups;			// preallocated array of entities
		PODArray< uint8_t >		_inUse;				// is this index valid (i.e. not destroyed)
		PODArray< uint8_t >		_componentsInitialized;
		BehaviorGraph			_behaviorGraph;		// Todo -> in BehaviorContext ?
		KnowledgeBase			_knowledgeBase;		// Todo -> in KB Context ?
		Simulation*				_simulation;

		int32_t					_groupTypeId;		// is const once set at construction
		int64_t					_inUseHelper;		// points to last used node to avoid rechecking all in use
		uint8_t					_updateOrder;		// 0 to n, add one for each parenting level above this EntityType
	};
}
