#ifndef __SCENE__H__
#define __SCENE__H__

#include "smartptr.h"
#include "rwlock.h"
#include "geometrynode.h"
#include "LODnode.h"
#include "switchnode.h"
#include "lightnode.h"
#include "backgroundnode.h"
#include "camera.h"
#include "variant.h"
#include "SoundSourceNode.h"
#include <set>
#include "environmentprobe.h"
//#include <algorithm>
//#include <deque>
//#include <wx/hashmap.h>
#include "geometrytriangles.h"
#include "VrutApi.h"
#include <atomic>

namespace VRUT
{
	class SceneManager;
	class EventHandler;

	///Structure containing information on a "flying carpet" configuration. It should point to a node
	///whose children are cameras defined in a context of a single, collectively transformed system
	///with or without tracked perspective
	struct FlyingCarpet {
		public:
			///the default constructor
			FlyingCarpet() : root(NODE_ID_NONE), isTracked(false), bodyID(-1), eyeSeparation(0) {}

			///a copy constructor
			FlyingCarpet(const FlyingCarpet & f) : root(f.root), isTracked(f.isTracked), bodyID(f.bodyID), eyeSeparation(f.eyeSeparation), offset(f.offset) {}

			///a basic all-inclusive constructor
			FlyingCarpet(NODE_ID root, bool isTracked, float eyeSeparation, int bodyID=0) : root(root), isTracked(isTracked), bodyID(bodyID), eyeSeparation(eyeSeparation) {}

			///The root node of the configuration. Collective camera transformations should be applied here
			NODE_ID root;
			///Flag indicating whether the perspective on subordinate cameras is tracked
			bool isTracked;
			///ID of the body representing the viewer (for the purposes of tracked perspective)
			int bodyID;
			///Eye separation of the viewer in tracking units (should be half of the distance between the person's eyes).
			///This value should be positive, or headaches will result.
			float eyeSeparation;
			///The general (untransformed) offset of the configuration
			Vector3 offset;
	};

	struct ClippingPlane {
	public:
		Vector4 plane;
		bool enabled;
		CLIPPING_PLANE_ID cpid;
		ClippingPlane() : enabled(false), cpid(CLIPPING_PLANE_ID_NONE) {}
		ClippingPlane(const ClippingPlane &cp) : plane(cp.plane), enabled(cp.enabled), cpid(cp.cpid) {}
		ClippingPlane(const Vector4 &plane, const bool enabled, const CLIPPING_PLANE_ID cpid=CLIPPING_PLANE_ID_NONE) : plane(plane), enabled(enabled), cpid(cpid) {}
		wxString ToString() const
		{
			return wxString::Format(wxT("ClippingPlane cpid: %lu\n"), (unsigned long)cpid) + plane.ToString() + (enabled?wxT("enabled"): wxT("disabled"));
		}
	};

	///Scene data
	class VRUT_CORE_API Scene : public Countable
	{
	public:
		typedef std::vector<smart_pointer<SceneNode> > NodeList;
		typedef std::vector<smart_pointer<Geometry> > GeometryList;
		typedef std::vector<smart_pointer<BackgroundNode> > BackgroundList;
		WX_DECLARE_STRING_HASH_MAP(MATERIAL_ID, MaterialIDHashMap);
		typedef std::vector<smart_pointer<Material> > MaterialList;
		WX_DECLARE_STRING_HASH_MAP(VARIANT_ID, VariantIDHashMap);
		typedef std::vector<smart_pointer<Variant> > VariantList;
		typedef std::vector<smart_pointer<EnvironmentProbe> > EnvironmentProbeList;

	private:
		///Scene ID
		SCENE_ID sceneID;
		///Scene nodes (ordered according to their IDs)
		NodeList sceneNodes;
		std::atomic<unsigned int> sceneNodesCount;
		///Scene graph root node ID
		NODE_ID rootID;
		///Hash map with \c Material IDs
		MaterialIDHashMap materialIDMap;
		///List of materials used in scene
		MaterialList materials;
		///List of nodes that uses given material
		std::vector<std::set<NODE_ID> > materialNodes;

		///List of all the flying carpets in the scene
		std::vector<FlyingCarpet> carpets;

		///List of geometries used in scene
		GeometryList geometries;
		std::atomic<unsigned int> geometriesCount;

		///List of nodes that uses given geometry
		std::vector<std::set<NODE_ID> > geometriesNodes;

		///List of light nodes in scene
		std::vector<NODE_ID> lightNodeIDs;

		VariantIDHashMap variantIDMap;
		///List of variants
		VariantList variants;
		struct JSScriptingContext;
		JSScriptingContext *jsCtxVariants;
		///List of background nodes
		BackgroundList backgrounds;
		///List of environment probes
		EnvironmentProbeList environmentProbes;
		///List of clipping planes
		std::vector<ClippingPlane> clippingPlanes;
		///scene name
		wxString sceneName;
		///Pointer to kernel message sink - auxiliary to enable event posting when called directly from modules
		EventHandler * msgSink;

		// TODO When are individual mutexes used? Which operations should be interlocked, and with which mutexes?
		//		Which operations need not to be interlocked?
		// TODO What about mutexes in nodes (the node derives from ReadWriteMutex (TODO Why derivation?))?
		//		When should be this mutex locked and when not?
		//		And if there are mutexes in nodes, why there are no mutexes in Geometry, Material, Variant,
		//		BackgroundNode(apart of the mutex in SceneNode), EnvironmentProbe, or ClippingPlane?
		// TODO For which operations should be used ReadLocker and WriteLocker?

		///Mutex for sceneNodes vector access
		ReadWriteMutex sceneNodesMutex;
		///Mutex for geometries vector access
		ReadWriteMutex geometriesMutex;
		///Mutex for materials vector and materialIDMap hash map access
		ReadWriteMutex materialsMutex;
		///Mutex for variants vector access
		ReadWriteMutex variantsMutex;
		///Mutex for backgrounds vector access
		ReadWriteMutex backgroundsMutex;
		///Mutex for environment probes
		ReadWriteMutex environmentProbesMutex;
		///Mutex of clipping planes
		ReadWriteMutex clippingPlanesMutex;
		///Mutex of clipping planes
		ReadWriteMutex carpetsMutex;

		/// Get world transformation matrix - update if necessary
		Matrix getWorldTransMatrix(SceneNode *node) const
		{
			Matrix worldMatrix;
			while (node)
			{
				worldMatrix*=*node->GetLocalTransMatrix();;
				node=getNode(node->GetParentID());
			}
			return worldMatrix;
		}

		///Get scene node with given ID
		///It is the responsibility of the local caller to readlock the node list
		SceneNode * getNode(NODE_ID id) const
		{
			return (id < sceneNodes.size() ? sceneNodes[id].ptr : NULL);
		}

		///Remove existing scene node which do not have a parent neither any child.
		/// It is the responsibility of the caller to test is all requirements.
		void removeNode(NODE_ID id);

		void addMaterialNodeReference(MATERIAL_ID mid, NODE_ID id)
		{
			if ((mid != MATERIAL_ID_NONE) && (id != NODE_ID_NONE))
			{
				if (materialNodes.size()<=mid)
					materialNodes.insert(materialNodes.end(), mid-materialNodes.size()+10, std::set<NODE_ID>());
				materialNodes[mid].insert(id);
			}
		}

		void removeMaterialNodeReference(MATERIAL_ID mid, NODE_ID id)
		{
			if ((mid != MATERIAL_ID_NONE) && (mid<materialNodes.size()) && (id != NODE_ID_NONE))
				materialNodes[mid].erase(id);
		}

		void addGeometryNodeReference(GEOMETRY_ID gid, NODE_ID id)
		{
			if ((gid != GEOMETRY_ID_NONE) && (id != NODE_ID_NONE))
			{
				if (geometriesNodes.size()<=gid)
					geometriesNodes.insert(geometriesNodes.end(), gid-geometriesNodes.size()+10, std::set<NODE_ID>());
				geometriesNodes[gid].insert(id);
			}
		}

		void removeGeometryNodeReference(GEOMETRY_ID gid, NODE_ID id)
		{
			if ((gid != GEOMETRY_ID_NONE) && (gid<geometriesNodes.size()) && (id != NODE_ID_NONE))
				geometriesNodes[gid].erase(id);
		}

		///Get variant
		///\return NULL if not found
		Variant * getVariant(VARIANT_ID vid) const
		{
			return (vid < variants.size() ? variants[vid].ptr : NULL);;
		}

		EnvironmentProbe * getEnvironmentProbe(ENVIRONMENT_PROBE_ID epid) const
		{
			return (epid < environmentProbes.size() ? environmentProbes[epid].ptr : (EnvironmentProbe *)NULL);
		}

		Material * getMaterial(MATERIAL_ID mid) const
		{
			return (mid < materials.size() ? materials[mid].ptr : NULL);
		}

		Geometry * getGeometry(GEOMETRY_ID gid) const
		{
			return (gid < geometries.size() ? geometries[gid].ptr : NULL);
		}

		/// Assumes everything is locked
		/// TODO What should the returned value mean?
		bool setTransformation(NODE_ID id, SceneNode * node, Matrix matrix);

		///Updates the near and far planes and eye separation on a given flying carpet
		void setCarpetCamsNearFar(NODE_ID root, float nearPlane, float farPlane);


public:
		///Constructor
		Scene(SCENE_ID id, EventHandler *msgSink) : sceneID(id), sceneNodesCount(0), rootID(NODE_ID_NONE), geometriesCount(0), jsCtxVariants(nullptr), msgSink(msgSink) {}

		///Destructor
		~Scene();

		///Get scene ID
		SCENE_ID GetID() const
		{
			return sceneID;
		}
		///Get scene graph root ID
		NODE_ID GetRootID() const
		{
			return rootID;
		}

		///Get scene node with given ID
		template <class NodeType>
		void GetNode(smart_pointer<NodeType> & node, NODE_ID id) const
		{
			sceneNodesMutex.ReadLock();
			node.assign((NodeType *)getNode(id));
			sceneNodesMutex.ReadUnlock();
			assert(SceneNode::IsOfClass<NodeType>(node.getPtr()));
		}
		template <class NodeType>
		smart_pointer<NodeType> GetNode(NODE_ID id) const
		{
			smart_pointer<NodeType> out_node;
			GetNode(out_node, id);
			return out_node;		// RVO or move constructor are used
		}

		/// Get node with given name
		template <class NodeType>
		void GetNode(smart_pointer<NodeType> &out_node, const wxString &name) const
		{
			ReadLocker rlock(sceneNodesMutex);
			for (auto &node : sceneNodes)
			{
				if (node && (node->name == name))
				{
					out_node = node;
					assert(SceneNode::IsOfClass<NodeType>(out_node.getPtr()));
					return;
				}
			}
			out_node = nullptr;
		}
		template <class NodeType>
		smart_pointer<NodeType> GetNode(const wxString &name) const
		{
			smart_pointer<NodeType> out_node;
			GetNode(out_node, name);
			return out_node;		// RVO or move constructor are used
		}

		/// Similar to GetNode, but also checks the type of the node.
		/// TODO Write other versions, like GetNodeCheckType(string) etc.
		template <class NodeType>
		void GetNodeCheckType(smart_pointer<NodeType> & node, NODE_ID id) const
		{
			// We use GetNode and then check the result, so that we would hold the lock for as little time as possible.
			auto sc_node = GetNode<SceneNode>(id);
			if (SceneNode::IsOfClass<NodeType>(sc_node.getPtr()))
				node = std::move(sc_node);
			else
				node.reset();
		}
		template <class NodeType>
		smart_pointer<NodeType> GetNodeCheckType(NODE_ID id) const
		{
			smart_pointer<NodeType> out_node;
			GetNodeCheckType(out_node, id);
			return out_node;		// RVO or move constructor are used
		}

		///Get all scene nodes
		void GetNodes(std::vector<smart_pointer<SceneNode> > &nodes) const
		{
			sceneNodesMutex.ReadLock();
			for (auto &node : sceneNodes)
			{
				if (node)
				{
					smart_pointer<SceneNode> nodet;
					nodes.push_back(nodet);
					nodes.back().assign(node.ptr);
				}
			}
			sceneNodesMutex.ReadUnlock();
		}

		///Get scene nodes based on a given node type
		/// The 'nodes' vector is not cleared, the nodes are added into it.
		template <class NodeType>
		void GetNodes(std::vector<smart_pointer<NodeType> > &nodes, const SceneNode::SCENENODE_TYPE type) const
		{
			if (type==SceneNode::BACKGROUND)
			{
				backgroundsMutex.ReadLock();
#if 0
				// $$JB - this does not compile with msvc2005
				nodes.insert(nodes.end(), backgrounds.begin(), backgrounds.end());
#else
				for (auto &backg : backgrounds)
				{
					smart_pointer<NodeType> node;
					nodes.push_back(node);
					nodes.back().assign((NodeType *)backg.ptr);
					assert(SceneNode::IsOfClass<NodeType>(nodes.back().getPtr()));		// The assign one line above is not checked
				}
#endif
				backgroundsMutex.ReadUnlock();
			}
			else
			{
				sceneNodesMutex.ReadLock();
				for (auto &node : sceneNodes)
				{
					if (node && (node->IsOfType(type)))
					{
						smart_pointer<NodeType> nodet;
						nodes.push_back(nodet);
						nodes.back().assign((NodeType *)node.ptr);
						assert(SceneNode::IsOfClass<NodeType>(nodes.back().getPtr()));		// The assign one line above is not checked
					}
				}
				sceneNodesMutex.ReadUnlock();
			}
		}

		/// Get scene nodes of given type
		/// To get all nodes, specify SceneNode as the NodeType. There is a template specialization
		/// for this implemented after this class.
		template <class NodeType> inline std::vector<smart_pointer<NodeType> > GetNodes() const
		{
			std::vector<smart_pointer<NodeType> > result;
			GetNodes(result, NodeType::ClassType);
			return result;		// RVO or move constructor are used
		}

		///Get scene nodes based on a given node type mask
		// TODO Should 'nodes' be empty? Or is this method supposed to *add* the nodes into 'nodes'?
		template <class NodeType>
		void GetNodesByType(std::vector<smart_pointer<NodeType> > &nodes, SceneNode::TypeMask types) const
		{
			sceneNodesMutex.ReadLock();
			for (auto &node : sceneNodes)
			{
				if (node && (node->GetTypeMask() & types))
				{
					smart_pointer<NodeType> nodet;
					nodes.push_back(nodet);
					nodes.back().assign((NodeType *)node.ptr);
					assert(SceneNode::IsOfClass<NodeType>(nodes.back().getPtr()));		// The assign one line above is not checked
				}
			}
			sceneNodesMutex.ReadUnlock();
		}

		///Get all scene nodes that are children of a given node
		///These can be pre-cast by type
		/// The 'nodes' vector is not cleared, the nodes are added into it.
		template <class NodeType>
		void GetChildrenNodes(std::vector<smart_pointer<NodeType> > & nodes, const NODE_ID id) const
		{
			ReadLocker rlock(sceneNodesMutex);
			SceneNode * parent=getNode(id);
			if (parent)
			{
				WriteLocker wlock(*parent); // MD Why write lock? Read lock should be enough.
				for (auto childID : parent->children)
				{
					NodeType *child=(NodeType *)getNode(childID);
					if (child)
					{
						smart_pointer<NodeType> node;
						nodes.push_back(node);
						nodes.back().assign(child);
						assert(SceneNode::IsOfClass<NodeType>(nodes.back().getPtr()));		// The assign one line above is not checked
					}
				}
			}
		}

		///Get all scene nodes that are children of a given node based on a given node type mask
		///These can be pre-cast by type
		/// The 'nodes' vector is not cleared, the nodes are added into it.
		template <class NodeType>
		void GetChildrenNodesByType(std::vector<smart_pointer<NodeType> > &nodes, const NODE_ID id, SceneNode::TypeMask types) const
		{
			ReadLocker rlock(sceneNodesMutex);
			SceneNode * parent=getNode(id);
			if (parent)
			{
				WriteLocker wlock(*parent); // MD Why write lock? Read lock should be enough.
				for (auto childID : parent->children)
				{
					SceneNode *child = getNode(childID);
					if (child && (child->GetTypeMask() & types))
					{
						smart_pointer<NodeType> node;
						nodes.push_back(node);
						assert(SceneNode::IsOfClass<NodeType>(child));
						nodes.back().assign((NodeType *)child);
					}
				}
			}
		}

		///Get scene nodes based on a given node name
		/// The 'nodes' vector is not cleared, the nodes are added into it.
		template <class NodeType>
		void GetNodes(std::vector<smart_pointer<NodeType> > &nodes, const wxString &name) const
		{
			sceneNodesMutex.ReadLock();
			for (auto &node : sceneNodes)
			{
				if (node && (node->name==name))
				{
					smart_pointer<NodeType> nodet;
					nodes.push_back(nodet);
					nodes.back().assign((NodeType *)node.ptr);
					assert(SceneNode::IsOfClass<NodeType>(nodes.back().getPtr()));		// The assign one line above is not checked
				}
			}
			sceneNodesMutex.ReadUnlock();
		}

		///Get scene nodes based on a given vector of IDs
		///Use this for enhanced performance, when you know up front which nodes you need to get
		///TODO: Check whether optimization is required (to avoid repeated indexing of the node list)
		template <class NodeType>
		void GetNodes(std::vector<smart_pointer<NodeType> > & nodes, std::vector<NODE_ID> & nodeIDs) const
		{
			sceneNodesMutex.ReadLock();
			nodes.resize(nodeIDs.size(), smart_pointer<NodeType>(nullptr));
			for (size_t i=0; i<nodeIDs.size(); ++i)
			{
				nodes[i].assign((NodeType*)getNode(nodeIDs[i]));
				assert(SceneNode::IsOfClass<NodeType>(nodes[i].getPtr()));		// The assign one line above is not checked
			}
			sceneNodesMutex.ReadUnlock();
		}

		///Get scene nodes based on a given NodeIDList of IDs
		///Use this for enhanced performance, when you know up front which nodes you need to get
		///TODO: Check whether optimization is required (to avoid repeated indexing of the node list)
		template <class NodeType>
		void GetNodes(std::vector<smart_pointer<NodeType> > & nodes, const SceneNode::NodeIDList & nodeIDs) const
		{
			sceneNodesMutex.ReadLock();
			nodes.resize(nodeIDs.size(), smart_pointer<NodeType>(nullptr));
			size_t ctr=0;
			for (auto nodeID : nodeIDs)
			{
				nodes[ctr].assign(getNode(nodeID));
				assert(SceneNode::IsOfClass<NodeType>(nodes[ctr].getPtr()));		// The assign one line above is not checked
				++ctr;
			}
			sceneNodesMutex.ReadUnlock();
		}

		///Get maximum node ID + 1 in scene (number of nodes including previously removed nodes)
		size_t GetNodeMaxID() const
		{
			return sceneNodes.size();
		}

		///Get list of all nodes in scene
		///\param[out] nodes Will be expanded with all (not NULL) nodes in scene
		void GetNodeIDs(std::vector<NODE_ID> & nodes) const;
		std::vector<NODE_ID> GetNodeIDs() const
		{
			std::vector<NODE_ID> result;
			GetNodeIDs(result);
			return result;		// RVO or move constructor are used
		}

		///Get list of nodes of given type in scene
		///\param[out] nodes Will be expanded with all nodes in scene with given type
		///\param[in] type requested node type
		void GetNodeIDs(std::vector<NODE_ID> & nodes, const SceneNode::SCENENODE_TYPE type) const;
		std::vector<NODE_ID> GetNodeIDs(const SceneNode::SCENENODE_TYPE type) const
		{
			std::vector<NODE_ID> result;
			GetNodeIDs(result, type);
			return result;		// RVO or move constructor are used
		}

		///Get list of children nodes of given scene node
		///\param[out] nodes Will be expanded with all scene nodes that are children of a given node
		///\param[in] nodeID requested nodeID
		void GetChildrenNodeIDs(std::vector<NODE_ID> & nodes, const NODE_ID nodeID) const;
		std::vector<NODE_ID> GetChildrenNodeIDs(const NODE_ID nodeID) const
		{
			std::vector<NODE_ID> result;
			GetChildrenNodeIDs(result, nodeID);
			return result;		// RVO or move constructor are used
		}

		///Get list of nodes with given name in scene
		///\param[out] nodes Will be expanded with all nodes in scene with given type
		///\param[in] name requested node name
		void GetNodeIDs(std::vector<NODE_ID> & nodes, const wxString &name, const bool useMask=false) const;
		std::vector<NODE_ID> GetNodeIDs(const wxString &name, const bool useMask=false) const
		{
			std::vector<NODE_ID> result;
			GetNodeIDs(result, name, useMask);
			return result;		// RVO or move constructor are used
		}

		///Get list of nodes IDs that use given material
		///\param[out] nodes Will be expanded with GEOMETRY nodes using given material
		///\param[in] mid Material ID
		void GetNodeIDsWithMaterial(std::vector<NODE_ID> &nodes, const MATERIAL_ID mid) const
		{
			ReadLocker rlock(materialsMutex);
			if (mid<materialNodes.size())
				nodes.insert(nodes.end(), materialNodes.at(mid).begin(), materialNodes.at(mid).end());
		}

		///Get geometry nodes IDs based on a given geometry ID
		///\param[out] nodes Will be expanded with GEOMETRY nodes using given geometry
		///\param[in] gid Geometry ID
		void GetNodeIDsWithGeometry(std::vector<NODE_ID> &nodes, const GEOMETRY_ID gid) const
		{
			ReadLocker rlock(geometriesMutex);
			if (gid<geometriesNodes.size())
				nodes.insert(nodes.end(), geometriesNodes.at(gid).begin(), geometriesNodes.at(gid).end());
		}

		///Get list of nodes in subtree. (depth search)
		///\param[in] nodeId Node ID
		///\param[out] nodes Will be expanded with all nodeIDs in subtree
		void GetSubNodeIDs(std::vector<NODE_ID> &nodes, const NODE_ID nodeId) const
		{
			size_t i=nodes.size();
			ReadLocker rlock(sceneNodesMutex);
			SceneNode * parent=getNode(nodeId);
			while (true)
			{
				if (parent)
				{
					WriteLocker wlock(*parent);
					for (auto childID : parent->children) {
						if (getNode(childID))
							nodes.push_back(childID);
					}
				}
				if (i>=nodes.size())
					return;
				parent=getNode(nodes[i]);
				++i;
			}
		}

		///Get list of nodes in subtree. (depth search)
		///\param[in] nodeId Node ID
		///\param[out] nodes Will be filled with all nodes in subtree
		template <class NodeType>
		void GetSubNodes(std::vector<smart_pointer<NodeType> > & nodes, const NODE_ID id, bool includeRoot=false) const
		{
			size_t i=nodes.size();
			ReadLocker rlock(sceneNodesMutex);
			const SceneNode * parent=getNode(id);
			if (includeRoot && parent)
			{
				smart_pointer<NodeType> node;
				nodes.push_back(node);
				nodes.back().assign(const_cast<SceneNode*>(parent));
				assert(SceneNode::IsOfClass<NodeType>(nodes.back().getPtr()));		// The assign one line above is not checked
				++i;
			}
			while (true)
			{
				if (parent)
				{
					WriteLocker wlock(*parent);
					for (auto childID : parent->children)
					{
						NodeType *child=(NodeType *)getNode(childID);
						if (child)
						{
							smart_pointer<NodeType> node;
							nodes.push_back(node);
							nodes.back().assign(child);
							assert(SceneNode::IsOfClass<NodeType>(nodes.back().getPtr()));		// The assign one line above is not checked
						}
					}
				}
				if (i>=nodes.size())
					return;
				parent=nodes[i].getPtr();
				++i;
			}
		}

		/// Returns the topology of the scene tree, i.e. it returns:
		///		- for each node whether the node exists or not (out_node_existency)
		///		- for each node the ID of its parent
		/// The topology is consistent, i.e.
		///		- there is no cycle
		///		- there is exactly one root (node with no parent)
		///		- each node has a parent (except the root)
		void GetTopology(std::vector<bool> &out_node_existency, std::vector<NODE_ID> &out_node_parent);

		/// Checks whether the topology (as returned by GetTopology) is consistent
		static bool CheckTopology(const std::vector<bool> &node_existency, const std::vector<NODE_ID> &node_parent);

		/// Find the intersection of the ray with any Geometry
		bool CastRay(const Ray & ray, RayIntersectionInfo &info);
		/// Skip the nodes which are marked in vector
		bool CastRay(const Ray & ray, std::vector<bool> &ignoreNodes, RayIntersectionInfo &info);

		///Get list of bools with the same size as sceneNodes, with true for nodes with given subtree starting from nodeID
		void MarkSubtree(NODE_ID nodeID, std::vector<bool> &itemsMark, bool markAs=true) const
		{
			sceneNodesMutex.ReadLock();
			itemsMark.clear();
			itemsMark.resize(sceneNodes.size(), !markAs);
			std::vector<NODE_ID> nodeIDs;
			while ((nodeID!=NODE_ID_NONE) || !nodeIDs.empty())
			{
				SceneNode *node=getNode(nodeID);
				if (node)
				{
					itemsMark[nodeID]=markAs;
					WriteLocker wlock(*node);
					nodeIDs.insert(nodeIDs.end(), node->children.begin(), node->children.end());
				}
				if (!nodeIDs.empty())
				{
					nodeID=nodeIDs.back();
					nodeIDs.pop_back();
				}
				else
					nodeID=NODE_ID_NONE;
			}
			sceneNodesMutex.ReadUnlock();
		}

		///Get list of bools with the same size as sceneNodes, with true for nodes with given subtree starting from nodeID, only active nodes are marked
		void MarkActiveSubtree(NODE_ID nodeID, std::vector<bool> &itemsMark, bool markAs=true) const
		{
			sceneNodesMutex.ReadLock();
			itemsMark.clear();
			itemsMark.resize(sceneNodes.size(), !markAs);
			if (!IsActive(nodeID))
			{
				sceneNodesMutex.ReadUnlock();
				return;
			}
			std::vector<NODE_ID> nodeIDs;
			while ((nodeID!=NODE_ID_NONE) || !nodeIDs.empty())
			{
				SceneNode *node=getNode(nodeID);
				if (node && node->IsActive())
				{
					itemsMark[nodeID]=markAs;
					WriteLocker wlock(*node);
					nodeIDs.insert(nodeIDs.end(), node->children.begin(), node->children.end());
				}
				if (!nodeIDs.empty())
				{
					nodeID=nodeIDs.back();
					nodeIDs.pop_back();
				}
				else
					nodeID=NODE_ID_NONE;
			}
			sceneNodesMutex.ReadUnlock();
		}

		///Set scene name
		void SetName(const wxString & name)
		{
			sceneName = name;
		}

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

		///Get AABB of subtree or whole scene
		AABB GetAABB(NODE_ID nodeID=NODE_ID_NONE);



		/////////////////////////////////////////////////////////////
		/// Material operations                                   ///
		/////////////////////////////////////////////////////////////

		///Add material to materials list
		MATERIAL_ID AddMaterial(Material * material);

		///Remove material from material list and delete its instance, check if not in use first
		///\param[in] mid ID of material to be removed
		///\return \c false if material not found or is in use (referenced) by a node
		bool RemoveMaterial(MATERIAL_ID mid);

		///Get material ID by its name
		MATERIAL_ID GetMaterialID(const wxString & name) const
		{
			materialsMutex.ReadLock();
			MaterialIDHashMap::const_iterator it = materialIDMap.find(name);
			MATERIAL_ID mid=(it != materialIDMap.end() ? it->second : MATERIAL_ID_NONE);
			materialsMutex.ReadUnlock();
			/*if (mid==MATERIAL_ID_NONE)
				LOGWARNING(wxT("Material not exists: ")+name);*/
			return mid;
		}

		///Get material
		///\return NULL if not found
		void GetMaterial(smart_pointer<Material>& mat, MATERIAL_ID mid) const
		{
			materialsMutex.ReadLock();
			mat.assign(mid < materials.size() ? materials[mid].ptr : NULL);
			materialsMutex.ReadUnlock();
		}

		///Get material
		///\return NULL if not found
		smart_pointer<Material> GetMaterial(MATERIAL_ID mid) const
		{
			smart_pointer<Material> result;
			GetMaterial(result, mid);
			return result;
		}

		///Get all scene materials
		void GetMaterials(std::vector<smart_pointer<Material> > &mats) const
		{
			materialsMutex.ReadLock();
			for (auto &mat : materials)
			{
				if (mat)
				{
					smart_pointer<Material> matt;
					mats.push_back(matt);
					mats.back().assign(mat.ptr);
				}
			}
			materialsMutex.ReadUnlock();
		}
		std::vector<smart_pointer<Material> > GetMaterials() const
		{
			std::vector<smart_pointer<Material> > result;
			GetMaterials(result);
			return result;		// RVO or move constructor are used
		}

		///Get materials for given GeometryNodes
		void GetMaterials(const std::vector<smart_pointer<GeometryNode> > &gnodes, std::vector<smart_pointer<Material> > &mats) const
		{
			mats.resize(gnodes.size());
			materialsMutex.ReadLock();
			for (size_t i=0; i<gnodes.size(); ++i)
				mats[i].assign(getMaterial(gnodes[i]->GetMaterialID()));
			materialsMutex.ReadUnlock();
		}
		std::vector<smart_pointer<Material> > GetMaterials(const std::vector<smart_pointer<GeometryNode> > &gnodes) const
		{
			std::vector<smart_pointer<Material> > result;
			GetMaterials(gnodes, result);
			return result;		// RVO or move constructor are used
		}

		///Get maximum node ID + 1 in scene (number of nodes including previously removed nodes)
		size_t GetMaterialMaxID() const
		{
			return materials.size();
		}

		///Convert transparency to reflexivity for given material, needed by FHS import
		///\param[in] materialName name of material to be converted
		///\return \c false if material not found
		bool ConvertMaterialTransparencyToReflexivity(const wxString &materialName);


		/////////////////////////////////////////////////////////////
		/// Geometry operations                                   ///
		/////////////////////////////////////////////////////////////

		///Return new scene geometry ID
		///Current implementation also prepares place for the geometry in geometries list, but it can be changed in future!
		GEOMETRY_ID GetNewGeometryID()
		{
			return geometriesCount++;
		}

		///Add geometry to geometry list
		///\param[in] geometry Geometry data to be added
		///\return Geometry ID
		GEOMETRY_ID AddGeometry(Geometry * geometry);

		///Copy geometry
		///\param[in] gid ID of Geometry to be copied
		///\return new Geometry ID
		GEOMETRY_ID CopyGeometry(GEOMETRY_ID gid);

		///Remove geometry from geometry list and delete its instance, check if not in use first or replace by new geometry
		///\param[in] geomID ID of geometry to be removed
		///\param[in] geomIDnew ID of geometry to be used instead of the original, it is used only when it is valid geometry and when it differs from original
		///\return \c false if geometry not found or is in use (referenced) by a node
		bool RemoveGeometry(GEOMETRY_ID geomID, GEOMETRY_ID geomIDnew=GEOMETRY_ID_NONE);

		///Get geometry
		///\return NULL if not found
		template <class GeometryType>
		const void GetGeometry(smart_pointer<GeometryType> & geo, GEOMETRY_ID gid) const
		{
			geometriesMutex.ReadLock();
			geo.assign((GeometryType *) getGeometry(gid));
			assert(Geometry::IsOfClass<GeometryType>(geo.getPtr()));		// The assign one line above is not checked
			geometriesMutex.ReadUnlock();
		}

		///Get geometry
		///\return NULL if not found
		template <class GeometryType>
		smart_pointer<GeometryType> GetGeometry(GEOMETRY_ID gid) const
		{
			smart_pointer<GeometryType> result;
			GetGeometry(result, gid);
			return result;		// Uses RVO
		}
		smart_pointer<Geometry> GetGeometry(GEOMETRY_ID gid) const
		{
			return GetGeometry<Geometry>(gid);
		}

		///Get all scene geometries
		void GetGeometries(std::vector<smart_pointer<Geometry> > &geoms) const
		{
			geometriesMutex.ReadLock();
			for (auto &geo : geometries)
			{
				if (geo && (geo->type == Geometry::GEOMETRY_TRIANGLE))
				{
					smart_pointer<Geometry> geot;
					geoms.push_back(geot);
					geoms.back().assign((Geometry*) geo.ptr);
				}
			}
			geometriesMutex.ReadUnlock();
		}
		///Get all scene geometries
		/// TODO Why only GeometryTriangles ????? Why is this different from
		///			void GetGeometries(const std::vector<smart_pointer<GeometryNode> > &gnodes, std::vector<smart_pointer<Geometry> > &geoms) const
		///			which returns all types of geometries, not only the triangles ????
		void GetGeometries(std::vector<smart_pointer<GeometryTriangles> > &geoms) const
		{
			geometriesMutex.ReadLock();
			for (auto &geo : geometries)
			{
				if (geo && (geo->type==Geometry::GEOMETRY_TRIANGLE))
				{
					smart_pointer<GeometryTriangles> geot;
					geoms.push_back(geot);
					geoms.back().assign((GeometryTriangles*)geo.ptr);
				}
			}
			geometriesMutex.ReadUnlock();
		}

		///Get geometries for given GeometryNodes
		void GetGeometries(const std::vector<smart_pointer<GeometryNode> > &gnodes, std::vector<smart_pointer<Geometry> > &geoms) const
		{
			geoms.resize(gnodes.size());
			geometriesMutex.ReadLock();
			for (size_t i=0; i<gnodes.size(); ++i)
				geoms[i].assign(getGeometry(gnodes[i]->GetGeometryID()));
			geometriesMutex.ReadUnlock();
		}
		std::vector<smart_pointer<Geometry> > GetGeometries(const std::vector<smart_pointer<GeometryNode> > &gnodes) const
		{
			std::vector<smart_pointer<Geometry> > result;
			GetGeometries(gnodes, result);
			return result;		// RVO or move constructor are used
		}

		///Get maximum geometry ID + 1 in scene (number of nodes including previously removed geometries)
		size_t GetGeometryMaxID() const
		{
			return geometries.size();
		}


		/////////////////////////////////////////////////////////////
		/// Node operations                                       ///
		/////////////////////////////////////////////////////////////

		///Return new scene node ID
		///Current implementation also prepares place for the node in sceneNodes list, but it can be changed in future!
		NODE_ID GetNewNodeID()
		{
			return sceneNodesCount++;
		}

		///Insert scene node, parent node takes ownership of node.
		///This method replaces the previous node with the same ID as the new node.
		///\param[in] node Node to be inserted (node's id has to be generated by GetNewNodeID)
		///\param[in] targetID ID of parent of the new node (if not defined, inserted node will be root)
		///\return Inserted node ID
		NODE_ID Insert(smart_pointer<SceneNode> node, NODE_ID targetID);

		///Copy part of \c srcScene and insert under \c parent node in this scene
		///IDs of copied nodes are generated by GetNewNodeID in target scene
		///\param[in] srcScene Source scene (if not defined, this scene is used as source and target)
		///\param[in] srcID ID of subscene's root in \c srcScene (where to start copy from) (if not defined, root is used)
		///\param[in] targetID ID of this scene's parent where nodes will be inserted (if not defined, root is used)
		///\return Inserted scene's root ID
		NODE_ID Copy(Scene * srcScene, NODE_ID srcID, NODE_ID targetID);

		///Move part of scene to another place in the same scene
		///\param[in] srcID ID of subscene's root (where to start move from)
		///\param[in] targetID ID of parent where nodes will be inserted (if not defined, root is used)
		void Move(NODE_ID srcID, NODE_ID targetID);

		///Remove node and all subnodes from scene (removed nodes ARE deleted)
		void Remove(NODE_ID id);

		///Remove nodes and all subnodes from scene (removed nodes ARE deleted)
		void Remove(const std::vector<NODE_ID> & ids);

		///Set new name for node
		void SetName(NODE_ID id, const wxString & name);


		///Set node's local transformation matrix
		void SetTransformation(NODE_ID id, const Matrix & matrix);

		///Sets transformations of several nodes at once, using two arrays, one with node IDs and the other with matrices
		template <class IDIter, class MatrixIter>
		void SetTransformations(IDIter id_begin, IDIter id_end, MatrixIter matrix_begin, MatrixIter matrix_end)
		{
			ReadLocker rlock(sceneNodesMutex);
			for (; (id_begin != id_end) && (matrix_begin != matrix_end); ++id_begin, ++matrix_begin)
			{
				setTransformation(*id_begin, getNode(*id_begin), *matrix_begin);
			}
		}

		///Sets transformations of several nodes at once, using one array with std::pair<NODE_ID, Matrix>s
		template <class IDMatrixIter>
		void SetTransformations(IDMatrixIter id_matrix_pair_begin, IDMatrixIter id_matrix_pair_end)
		{
			ReadLocker rlock(sceneNodesMutex);
			for (; id_matrix_pair_begin != id_matrix_pair_end; ++id_matrix_pair_begin)
			{
				setTransformation(id_matrix_pair_begin->first, getNode(id_matrix_pair_begin->first), id_matrix_pair_begin->second);
			}
		}

		///Multiply node's local transformation matrix
		///TODO: check multiply order
		void Transform(NODE_ID id, const Matrix & matrix);

		/// Get world transfor. matrix - update if necessary
		const Matrix GetWorldTransMatrix(NODE_ID nodeID)
		{
			ReadLocker rlock(sceneNodesMutex);
			return getWorldTransMatrix(getNode(nodeID));
		}

		/// Returns world transformation matrices of multiple nodes at once (updates if necessary), saves some locks
		template <class NodeType>
		void GetWorldTransMatrices(const std::vector<VRUT::smart_pointer<NodeType> > &in_nodes, std::vector<Matrix> &out_matrices)
		{
			// Allocate memory in output array
			if (out_matrices.size() < in_nodes.size())
				out_matrices.resize(in_nodes.size());

			ReadLocker rlock(sceneNodesMutex);
			for (unsigned int i = 0; i < in_nodes.size(); ++i)
			{
				// If some node does not exist (or something like that), we skip the entry in both in_nodes and out_matrices,
				// so that matrices in out_matrices correspond to nodes in in_nodes
				if (in_nodes[i].ptr != nullptr)
					out_matrices[i] = getWorldTransMatrix(in_nodes[i].ptr);
			}
		}
		template <class NodeType>
		std::vector<Matrix> GetWorldTransMatrices(const std::vector<VRUT::smart_pointer<NodeType> > &in_nodes)
		{
			std::vector<Matrix> result;
			GetWorldTransMatrices(in_nodes, result);
			return result;		// RVO or move constructor are used
		}

		///Special translate (sets new position, use with caution)
		void TranslateAbs(NODE_ID id, const Vector3 & transl);

		///Assign new geometry to geometry node, geometry must be added first
		void SetGeometry(NODE_ID id, GEOMETRY_ID geometryID);

		size_t DereferenceGeometries(std::vector<NODE_ID> nodesIDs);

		///Assign new material to geometry node, material must be added first
		///\param[in] id ID if the Node to set material to
		///\param[in] mid Material ID
		void SetMaterial(NODE_ID id, MATERIAL_ID mid);

		///Assign new light data to light node
		void SetLight(NODE_ID id, const Light & light) const;

		///Set new distance to LOD node
		///\param[in] LODAssemblyId ID if the LODAssembly
		///\param[in] LODId ID if the LOD
		///\param[in] distance the distance to be used for this LOD
		void SetLODDistance(NODE_ID LODAssemblyId, NODE_ID LODId, float distance);

		///Set active child to SWITCH node and update status (show/hide children)
		///\param[in] switchID ID if the SWITCH
		///\param[in] activeChild the active child
		void SetSwitchActiveChild(NODE_ID switchID, size_t activeChild);

		///Set node active status
		///\param[in] id ID if the Node
		///\param[in] active the active status
		///\param[in] isImplied specify that the change was implicated from changes of parent (so it need not to be distributed to cluster)
		void SetActive(NODE_ID id, bool active, bool isImplied=false);

		///Get node active status recursivelly, it is SLOW: don't use if not necessary
		bool IsActive(NODE_ID nodeID) const
		{
			ReadLocker rlock(sceneNodesMutex);
			SceneNode *node=getNode(nodeID);
			bool isActive=false;
			while (node)
			{
				isActive=node->IsActive();
				if (!isActive)
					return false;
				node=getNode(node->GetParentID());
			}
			return isActive;
		}

		///Write lock node - helper method for callers with const only access to node
		bool WriteLock(NODE_ID id)
		{
			smart_pointer<SceneNode> node;
			GetNode(node, id);
			if (node)
				node.ptr->WriteLock();
			return node;
		}
		///Write unlock node - helper method for callers with const only access to node
		bool WriteUnlock(NODE_ID id)
		{
			smart_pointer<SceneNode> node;
			GetNode(node, id);
			if (node)
				node.ptr->WriteUnlock();
			return node;
		}

		///get index of child node
		size_t GetChildIndex(NODE_ID parentID, NODE_ID childID) const
		{
			smart_pointer<SceneNode> parNode;
			GetNode(parNode, parentID);
			if (parNode)
			{
				size_t i = 0;
				WriteLocker wlock(*parNode);
				for (auto childID2 : parNode->children)
				{
					if (childID2==childID)
						return i;
					++i;
				}
			}
			return (size_t)-1;
		}

		///Change index of node in parents children list (it allows to reorder children lins)
		///\param[in] nodeID ID of node
		///\param[in] newIndex the index
		void ChangeChildIndex(NODE_ID nodeID, size_t newIndex);



		/////////////////////////////////////////////////////////////
		/// Camera operations                                     ///
		/////////////////////////////////////////////////////////////

		///Set camera parameters for projection matrix, assuming a centered perspective
		///When parameter is 0 no change is actually made to it
		void SetProjectionParams(NODE_ID camID, unsigned width = 0, unsigned height = 0, float nearPlane = 0.0f, float farPlane = 0.0f, float fov = 0.0f, int upFix = -1);

		///Set camera parameters for an off-center perspective projection matrix
		///\param[in] offset viewpoint offset in camera-centric coordinates (that is, after the view transformation from tracking coordinates, sans scaling)
		///the offset should respect the "eyedness" of the camera, that is, it should be shifted to the left or to the right according to orientation of the viewer
		void SetPerspectiveParams(NODE_ID camID, float nearDist, float farDist, const Vector3 &offset, int eye, float eyeSeparation, const Vector3 &pa, const Vector3 &pb, const Vector3 &pc, const Vector3 &eyeAxis, const Vector3& lookAt = Vector3(0, 0, -1));

		///Update off-center perspective viewing offset
		///\param[in] offset viewpoint offset in camera-centric coordinates (that is, after the view transformation from tracking coordinates, sans scaling)
		///offset should be shifted beforehand, to respect the "eyedness" of a camera, and this method should not be called
		///on cameras with FOV perspective
		void SetPerspectiveOffsetAndEyeAxis(NODE_ID camID, const Vector3 &offset, const Vector3 &eyeAxis, const Vector3& lookAt = Vector3(0, 0, -1));

		///Move camera to fit whole scene into view frustum (move only, no rotation)
		///\param[in] camID ID of camera to be fit
		///\param[in] farNearOnly Do not modify camera transformation, just update near and far plane to fit scene
		///\param[in] nodeID fit this node instead af all scene
		void Fit(NODE_ID camID, bool farNearOnly = false, NODE_ID nodeID=NODE_ID_NONE);

		///Reset view - like \c Fit but resets also view direction
		///\param[in] camID ID of camera to be reset
		void ResetView(NODE_ID camID);

		///Gets the projection matrix for a camera, updates if necessary
		///Moved to scene class to avoid exposing non-const pointers outside the Scene class
		Matrix GetProjectionMatrix(NODE_ID camID, float aspectRatio, bool activeStereoEye);

                ///Gets the TMT matrix for a camera, updates if necessary
                ///Moved to scene class to avoid exposing non-const pointers outside the Scene class
		Matrix GetTMTMatrix(NODE_ID camID, float aspectRatio, bool activeStereoEye);

		///Sets the center distance for a camera
		///Moved to scene class to avoid exposing non-const pointers outside the Scene class
		void SetCameraCenterDist(NODE_ID camID, float centerDist)
		{
			smart_pointer<Camera> cam;
			GetNode(cam, camID);
			if (!cam || !cam->IsOfType(SceneNode::CAMERA))
				return;
			cam.ptr->centerDist=centerDist;
		}

		void SetCameraEyeSeparation(NODE_ID camID, float eyeSeparation);

		///Extract view direction
		Vector3 GetView(NODE_ID camID)
		{
			ReadLocker rlock(sceneNodesMutex);
			SceneNode *node = getNode(camID);
			return node ? getWorldTransMatrix(node).TransformNormal(Vector3(0,0,-1)) : Vector3(0,0,-1);
		}

		///Extract up vector
		Vector3 GetUp(NODE_ID camID)
		{
			ReadLocker rlock(sceneNodesMutex);
			SceneNode *node = getNode(camID);
			return node ? getWorldTransMatrix(node).TransformNormal(Vector3(0,1,0)) : Vector3(0,1,0);
		}

		///Get center point (world)
		Vector3 GetCenterPoint(NODE_ID camID)
		{
			ReadLocker rlock(sceneNodesMutex);
			SceneNode *node = getNode(camID);
			if (!node)
				return Vector3(0,0,-1);
			Matrix camWorldMatrix(getWorldTransMatrix(node));
			return camWorldMatrix.ExtractTranslation() + ((Camera *)node)->GetCenterDist() * camWorldMatrix.TransformNormal(Vector3(0,0,-1));
		}

		///Get the offset vector
		Vector3 GetOffset(NODE_ID camID)
		{
			ReadLocker rlock(sceneNodesMutex);
			Camera * cam = (Camera *) getNode(camID);
			if (!cam || !cam->IsOfType(SceneNode::CAMERA) || cam->projectionType!=Camera::PERSPECTIVE_GENERIC) {
				return Vector3();
			}

			return cam->offset;
		}

		/////////////////////////////////////////////////////////////
		/// FlyingCarpet operations                               ///
		/////////////////////////////////////////////////////////////

		///Declares a root node a centerpoint of a flying carpet. Its child cameras should already
		///have their projection and view matrices set appropriately, this declaration is only
		///for the purposes of synchronizing updates.
		///\param[in] root The NODE_ID of the root node (Camera). the configuration will be hereafter reffered to by this ID (there can only be one such configuration per node)
		///\param[in] isTracked A flag indicating whether the configuration is to use tracked or static perspective. The origin is considerd the default viewpoint.
		///\param[in] eyeSeparation Half the distance between the person's eyes, used for stereoscopy. You may pass a negative value at your own risk.
		///\param[in] bodyID This parameter should be set to the ID of the tracking body representing user's head
		void CreateCarpet(NODE_ID root, bool isTracked, float eyeSeparation, int bodyID=0);

		///Removes a flying carpet declared around a given centerpoint (or does nothing if there is no such carpet)
		void RemoveCarpet(NODE_ID root);

		///Updates all the flying carpets bound to this object with the new viewer position and orientation
		void UpdateCarpets(int bodyID, const Vector3 &position, const Matrix &orientation);

		///Updates the eye separation and tracked status on a given flying carpet
		void SetCarpetCamParams(NODE_ID root, float eyeSeparation, int isTracked=-1, int bodyID=0);

		///Updates the observer position on a non-tracked FC
		void UpdateCarpetOffset(NODE_ID root, const Vector3 &offset);

		///Get carpets
		const std::vector<FlyingCarpet> &GetCarpets() const;


		/////////////////////////////////////////////////////////////
		/// SoundSource operations                                ///
		/////////////////////////////////////////////////////////////

		///Set pitch and gain of a given sound node
		void SetSoundNodePitchGain(NODE_ID soundNodeID, float pitch, float gain) {
			smart_pointer<SoundSourceNode> node;
			GetNode(node, soundNodeID);
			if (!node || !node->IsOfType(SceneNode::SOUND_SOURCE))
				return;
			if (pitch > 0.0f)
				node.ptr->pitch=pitch;
			// clip gain
			if (gain < 0.0f)
				gain = 0.0f;
			node.ptr->gain=gain;
		}

		void SetSoundState(NODE_ID soundNodeID, SoundSourceNode::SoundSourceState state);

		void SetSoundNode(NODE_ID soundNodeID, float gain, bool looping, SoundSourceNode::SoundSourceState state, float minGain, float maxGain, float referenceDistance, float rollOffFactor,
			float maxDistance, float pitch, const Vector3 &direction, float coneInnerAngle, float coneOuterAngle, float coneOuterGain, float secOffset, const wxString &dataSource)
		{
			smart_pointer<SoundSourceNode> node;
			GetNode(node, soundNodeID);
			if (!node || !node->IsOfType(SceneNode::SOUND_SOURCE))
				return;
			node.ptr->gain=__max(gain, 0.0f);
			node.ptr->looping=looping;
			node.ptr->state=state;
			node.ptr->minGain=__max(minGain, 0.0f);
			node.ptr->maxGain=__min(maxGain, 1.0f);
			node.ptr->referenceDistance=referenceDistance;
			node.ptr->rollOffFactor=rollOffFactor;
			node.ptr->maxDistance=maxDistance;
			node.ptr->pitch=pitch;
			node.ptr->direction=direction;
			node.ptr->coneInnerAngle=coneInnerAngle;
			node.ptr->coneOuterAngle=coneOuterAngle;
			node.ptr->coneOuterGain=coneOuterGain;
			node.ptr->secOffset=secOffset;
			node.ptr->dataSource=dataSource;
		}

		/////////////////////////////////////////////////////////////
		/// Variants operations                                   ///
		/////////////////////////////////////////////////////////////

		///Add variant to variants list
		VARIANT_ID AddVariant(Variant * variant);

		///Remove variant from variant list and delete its instance, check if not in use first
		///\param[in] mid ID of variant to be removed
		///\return \c false if variant not found or is in use (referenced) by a node
		bool RemoveVariant(VARIANT_ID vid);

		///Get variant ID by its name
		VARIANT_ID GetVariantID(const wxString & name) const
		{
			variantsMutex.ReadLock();
			/*VariantIDHashMap::const_iterator it = variantIDMap.find(name);
			return (it != variantIDMap.end() ? it->second : VARIANT_ID_NONE);*/
			for (size_t i = 0; i < variants.size(); ++i)
			{
				if (variants[i] && variants[i]->GetName() == name)
				{
					variantsMutex.ReadUnlock();
					return i;
				}
			}
			variantsMutex.ReadUnlock();
			return VARIANT_ID_NONE;
		}

		///Get variant
		///\return NULL if not found
		void GetVariant(smart_pointer<Variant>& variant, VARIANT_ID vid) const
		{
			variantsMutex.ReadLock();
			variant.assign((Variant *)getVariant(vid));
			variantsMutex.ReadUnlock();
		}

		///Get all scene variants
		void GetVariants(std::vector<smart_pointer<Variant> > &vars) const
		{
			variantsMutex.ReadLock();
			for (auto &var : variants)
			{
				if (var)
				{
					smart_pointer<Variant> vart;
					vars.push_back(vart);
					vars.back().assign(var.ptr);
				}
			}
			variantsMutex.ReadUnlock();
		}

		template <class VariantType>
		void GetVariants(std::vector<smart_pointer<VariantType> > &vars, const Variant::VARIANT_TYPE type) const
		{
			variantsMutex.ReadLock();
			for (auto &var : variants)
			{
				if (var && (var->type==type))
				{
					smart_pointer<Variant> vart;
					vars.push_back(vart);
					vars.back().assign((VariantType*)var.ptr);
				}
			}
			variantsMutex.ReadUnlock();
		}

		///Get maximum variant ID + 1 in scene (number of nodes including previously removed variants)
		size_t GetVariantMaxID() const
		{
			return variants.size();
		}

		///Set active state for variant and update status (show/hide children)
		///\param[in] vid ID of the Variant
		///\param[in] activeState the ID of active state
		void SetVariantActiveState(VARIANT_ID vid, size_t activeState);

		///Set active state for variant and update status (show/hide children)
		///\param[in] vid ID of the Variant
		///\param[in] activeState the name of active state
		void SetVariantActiveState(VARIANT_ID vid, const wxString &activeState);

		///Set status for node for state
		///\param[in] vid ID of the Variant
		///\param[in] state the name of state
		///\param[in] detail the name of node
		///\param[in] status show/hide status
		void SetVariantDetailState(VARIANT_ID vid, const wxString &state, const wxString &detail, const wxString &status);

		///Set status for node for state
		///\param[in] variantName the name of the Variant
		///\param[in] state the name of state
		///\param[in] detail the name of node
		///\param[in] status show/hide status
		void SetVariantDetailState(const wxString &variantName, const wxString &state, const wxString &detail, const wxString &status);

		///Set cameraID to all VariantCameras
		///\param[in] cameraID ID of the camera
		void SetAllVariantsCameraID(NODE_ID cameraID);

		///Remove node from all variants
		///\param[in] id ID of the node
		void RemoveNodeFromVariants(NODE_ID id);

		///Replace node in all variants by new ID
		///\param[in] id ID of the node
		///\param[in] newID ID of the new node
		void ReplaceNodeInVariants(NODE_ID id, NODE_ID newID);

		///Remove material from all variants
		///\param[in] mid ID of the material
		void RemoveMaterialFromVariants(const wxString &materialName);

		/////////////////////////////////////////////////////////////
		/// Environment probe operations                          ///
		/////////////////////////////////////////////////////////////

		///Return new scene environment probe ID
		///Current implementation also prepares place for the environment probe in environment probes list
		ENVIRONMENT_PROBE_ID GetNewEnvironmentProbeID()
		{
			environmentProbesMutex.WriteLock();
			ENVIRONMENT_PROBE_ID epid=ENVIRONMENT_PROBE_ID(environmentProbes.size());
			environmentProbes.push_back(NULL);
			environmentProbesMutex.WriteUnlock();
			return epid;
		}

		///Get env probe
		///\return NULL if not found
		void GetEnvironmentProbe(smart_pointer<EnvironmentProbe> &environmentProbe, ENVIRONMENT_PROBE_ID epid) const
		{
			environmentProbesMutex.ReadLock();
			environmentProbe.assign(getEnvironmentProbe(epid));
			environmentProbesMutex.ReadUnlock();
		}

		///Binds node's cube map render target as the current render target
		///\param[in] environmentProbe Environment probe
		///\return True if setup has created new texture attachments, false otherwise
		bool BindAndSetupEnvironmentProbe(ENVIRONMENT_PROBE_ID id, GLuint &environmentProbeTextureID);

		///Enables node's cube map render target.
		///\param[in] environmentProbe Environment probe
		///\param[in] enable enable or disable
		void EnableEnvironmentProbe(ENVIRONMENT_PROBE_ID environmentProbe, bool enable);

		///Sets the EP as a single shot EP
		///\param[in] environmentProbe Environment probe
		///\param[in] singleShot
		void SetSingleShotEnvironmentProbe(ENVIRONMENT_PROBE_ID environmentProbe, bool singleShot);

		///Adds environment probe
		void AddEnvironmentProbe(EnvironmentProbe *environmentProbe);
		void AddEnvironmentProbe(NODE_ID nodeID, float nearPlaneDistance, float farPlaneDistance, float envProbeResolution, bool singleShot);
		///Sets the environment probe (used by sourceEnvProbeNodeID) texture to the target node's material
		void AssignEnvironmentProbeTexToNodesMaterial(NODE_ID sourceEnvProbeNodeID, NODE_ID targetNodeID);

		void GetEnvironmentProbes(std::vector<smart_pointer<EnvironmentProbe> > &envProbes) const
		{
			environmentProbesMutex.ReadLock();
			for (auto &probe : environmentProbes)
			{
				if (probe)
				{
					smart_pointer<EnvironmentProbe> probet;
					envProbes.push_back(probet);
					envProbes.back().assign(probe.ptr);
				}
			}
			environmentProbesMutex.ReadUnlock();
		}

		/////////////////////////////////////////////////////////////
		/// Clipping planes operations                            ///
		/////////////////////////////////////////////////////////////

		///Return new scene clipping plane ID
		///Current implementation also prepares place for the clipping plane in the list
		CLIPPING_PLANE_ID GetNewClippingPlaneID()
		{
			clippingPlanesMutex.WriteLock();
			CLIPPING_PLANE_ID cpid=CLIPPING_PLANE_ID(clippingPlanes.size());
			clippingPlanes.push_back(ClippingPlane());
			clippingPlanesMutex.WriteUnlock();
			return cpid;
		}

		///Add clipping plane
		CLIPPING_PLANE_ID AddClippingPlane(ClippingPlane *clippingPlane);

		///Get all scene clipping planes
		void GetClippingPlane(ClippingPlane &clippingPlane, CLIPPING_PLANE_ID cpid) const
		{
			clippingPlanesMutex.ReadLock();
			clippingPlane = (cpid < clippingPlanes.size()) ? clippingPlanes[cpid] : ClippingPlane();
			clippingPlanesMutex.ReadUnlock();
		}

		///Get all scene clipping planes
		void GetClippingPlanes(std::vector<ClippingPlane> &clippingPlanes) const
		{
			clippingPlanesMutex.ReadLock();
			clippingPlanes.assign(this->clippingPlanes.begin(), this->clippingPlanes.end());
			clippingPlanesMutex.ReadUnlock();
		}

		///make referenced names unique
		size_t MakeReferencedNamesUnique();

		///fix node names for VRML
		size_t FixNodeNamesForVRML();

		wxString ToString() const;

		/// Mutex for scene rendering, can be locked by modules
		/// requiring synchronized modification to the scene
		wxCriticalSection renderMutex;

		friend class SceneManager;
	};

	template <> inline std::vector<smart_pointer<SceneNode> > Scene::GetNodes<SceneNode>() const
	{
		// "All Scene Nodes" is not a type
		std::vector<smart_pointer<SceneNode> > result;
		GetNodes(result);
		return result;		// RVO or move constructor are used
	}

}

#endif
