/*
 * $Id: scenenode.h 4270 2016-11-06 11:02:47Z antonin.misek-skoda-auto.cz $
 *
 * Description  : One line description of file.
 * Author       : Vaclav Kyba <mail/Jabber: vaseo1@gmail.com>  <ICQ: 98576293>
 *
 * Purpose      :
 *     Long description of what the file is for.
 */


#ifndef __SCENENODE__H__
#define __SCENENODE__H__

#include <stack>

#include "3dmath.h"
//#include <list>
#include "rwlock.h"
//#include "flexilog.h"
#include "moddefs.h"
#include "VrutApi.h"
#include "smartptr.h"

namespace VRUT
{
	///Scene node for scene graph
	class VRUT_CORE_API SceneNode : public ReadWriteMutex, public Countable
	{
	public:
		typedef std::list<NODE_ID> NodeIDList;

		enum SCENENODE_TYPE
		{
			ASSEMBLY = 0,  ///<Assembly node
			FILE_ROOT = 1,  ///<Root node, it's name is filesystem path to the file, it allows external references
			CAMERA = 2,  ///<Camera node
			GEOMETRY = 3,  ///<Geometry node
			LIGHT = 4,  ///<Light node
			LOD = 5, ///<LOD node
			SWITCH = 6, ///<Switch node
			BACKGROUND = 7, ///<Background node
			SOUND_SOURCE = 8, ///<Sound source node
			LAST_SCENENODE_TYPE = SOUND_SOURCE,		///< Index of the last type
		};

		/// Use TypeMask to define a mask of several SceneNode types
		typedef unsigned int TypeMask;
		
		/// Mask of all scene node types
		static const TypeMask ALL_SCENE_TYPES_MASK = (0x1 << (LAST_SCENENODE_TYPE + 1)) - 1;

		/// Returns a mask that corresponds to a given SceneNode type
		static inline TypeMask CreateTypeMask(SCENENODE_TYPE type)
		{
			return 0x1 << type;
		}

		/// Returns a mask that corresponds to a given SceneNode subclass
		template <class T>
		static inline TypeMask CreateClassMask()
		{
			return 0x1 << T::ClassType;
		}

		/// Returns a mask that corresponds to this node's type
		inline TypeMask GetTypeMask() const
		{
			return CreateTypeMask(type);
		}

	protected:
		///Local transformation matrix
		Matrix localTransMatrix;

	private:
		///Name
		wxString name;
		///ID
		NODE_ID id;
		///Node type
		const SCENENODE_TYPE type;		// The type must be set in the constructor and not further changed.
		///Is node active?
		bool active;
		///Children list
		NodeIDList children;
		///Parent node
		NODE_ID parentID;

		///Set model space transformation matrix
		virtual void SetLocalTransMatrix(const Matrix &matrix)
		{
			localTransMatrix=matrix;
		}


	public:
		///Class constructor
		SceneNode(const wxString & _name, SCENENODE_TYPE _type) : name(CloneWxString(_name)), id(NODE_ID_NONE), type(_type), active(true), parentID(NODE_ID_NONE) {}

		///Copy constructor
		SceneNode(const SceneNode & n) : ReadWriteMutex(), localTransMatrix(n.localTransMatrix), name(CloneWxString(n.name)), id(n.id), type(n.type), active(n.active), parentID(n.parentID)
		{
			children.assign(n.children.begin(), n.children.end());
		}

		///Class destructor
		virtual ~SceneNode()
		{
		}

		///Get name
		const wxString & GetName() const
		{
			return name;
		}

		void SetName(const wxString &_name)
		{
			if (parentID==NODE_ID_NONE)
				name=CloneWxString(_name);
			else
				LOGERROR(wxT("<SceneNode>Cannot change name directly, because node is in scene. Use Scene method or event."));
		}

		///Get ID
		NODE_ID GetID() const
		{
			return id;
		}

		///Set ID
		void SetID(NODE_ID _id)
		{
			if (parentID==NODE_ID_NONE)
				id=_id;
			else
				LOGERROR(wxT("<SceneNode>Cannot change ID directly, because node is in scene. Use Scene method or event."));
		}

		///Get model space transformation matrix
		const Matrix * GetLocalTransMatrix() const
		{
			return &localTransMatrix;
		}

		///Get parent node ID
		NODE_ID GetParentID() const
		{
			return parentID;
		}

		///Is node active and should be drawn?
		bool IsActive() const
		{
			return active;
		}

		///Set node active status
		void SetActive(const bool _active)
		{
			if (parentID==NODE_ID_NONE)
				active=_active;
			else
				LOGERROR(wxT("<SceneNode>Cannot change active directly, because node is in scene. Use Scene method or event."));
		}

		///Get node type
		SCENENODE_TYPE GetType() const
		{
			return type;
		}

		///Is node of given type?
		bool IsOfType(SCENENODE_TYPE _type) const
		{
			return ( type == _type );
		}

		/// Returns true if the node can be type-casted into specified type, based on its 'type' variable,
		/// using the class for the check. The implementation and its specialization is places after this class.
		template <class T> inline bool IsOfClass() const;

		/// Enhancing the class method, works with nullptr. Nullptr can be casted into anything, so it
		/// returns true in such situation.
		template <class T> static inline bool IsOfClass(const SceneNode *node)
		{
			return !node || node->IsOfClass<T>();		// Nullptr is of any type.
		}

		/// Is node same as this one?
		bool IsSameAs(const smart_pointer<SceneNode> & node) const 
		{
			if (this->type != node->type)
				return false;

			if (this->active != node->active)
				return false;

			if (this->name != node->name)
				return false;

			if (this->localTransMatrix != node->localTransMatrix)
				return false;

			if (this->children.size() != node->children.size())
				return false;

			return true;
		}

		///Get string with node's properties
		virtual wxString ToString() const
		{
			return wxString::Format(wxT("ID: %lu\nName: "), (unsigned long)GetID())+GetName()+wxString::Format(wxT("\nType: %i\nLocal:\n"), type)+GetLocalTransMatrix()->ToString();
		}

		///Get copy of instance
		virtual SceneNode * Clone() const
		{
			return new SceneNode(*this);
		}

		friend class Scene;
	};

	template <class T> inline bool SceneNode::IsOfClass() const
	{
		return IsOfType(T::ClassType);
	}
	template <> inline bool SceneNode::IsOfClass<SceneNode>() const
	{
		return true;			// SceneNode can be casted into a SceneNode
	}

}

#endif
