/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


#ifndef __NSCENE__
#define	__NSCENE__


		#include "scene3d/scene_message.h"
		#include "scene3d/scene_event_interface.h"
		#include "scene3d/group.h"
		#include "scene3d/scene_io_flag.h"
		#include "scene3d/scene_profiler.h"
		#include "core/core_profiler.h"
		#include "core/renderer_environment_interface.h"
		#include "core/simple_list_renderable.h"
		#include "core/octree_renderable.h"
		#include "core/tool_mode.h"
		#include "core/clock.h"
		#include "script/script_vm.h"
		#include "timing/profiler.h"
		#include "reflection/c_refl.h"


/// Debug display flags.
#define	SceneDebugDisplayItems				(1 << 0)
#define	SceneDebugDisplayBones				(1 << 1)
#define	SceneDebugDisplayBoundingVolumes	(1 << 2)
#define	SceneDebugDisplayOctreeVolumes		(1 << 3)
#define	SceneDebugDisplayMotions			(1 << 4)

/// Scene update flags.
#define SceneUpdateNone						0
#define SceneUpdateMotion					(1 << 0)
#define SceneUpdatePhysic					(1 << 1)
#define SceneUpdateEvent					(1 << 2)
#define SceneUpdateTrigger					(1 << 3)
#define SceneUpdateAll						uint(~0)

namespace GS {

	namespace GPU { class TriangleBatch; }
	namespace S2D { class Scene; }

	namespace S3D {
		struct MLight;
		struct MObject;
		struct MTrigger;
		struct IPhysicWorld;
		class PhysicWorld;

		using Render::Renderer;
		using Render::RasterFont;

/*!
	@short	Scene object.
	@author Emmanuel Julien (ejulien@gsworks.fr)
*/
class	Scene : public SharedObject
{
		uint			current_item_uid;

struct	RenderData
{
		Render::sShader	skybox_shader;
		Render::sTexture skybox_layer[2];
		Render::sTexture irradiance_probe, radiance_probe;
};

public:

		/// Scene-wide flag.
enum	SceneFlag
{
		FlagNone				=	0,
		FlagEnd					=	(1 << 0),
		FlagRenderless			=	(1 << 1),
		FlagDebugPhysics		=	(1 << 2)
};

protected:

		Core::sClock	clock;

		SharedList <MItem *> activation_queue, deactivation_queue;
		SharedList <MItem *> item_list, removal_queue;

		SharedArrayList <MItem *> active_list;	// faster traversal than a shared list

		SharedList <Group *> group_list;

		List <MLight *> light_list;
		List <MTrigger *> trigger_list;

		/// Evaluate item.
		void			UpdateItem(float dt, MItem *, Core::Item *, uint eval_flag = SceneUpdateAll);

public:

		String			name;

		BitField		flags;		///< Scene flags.

		AutoPtr <S2D::Scene> ui;
		void			Render(Renderer &);
		void			RenderUI(Renderer &, GPU::TriangleBatch * = 0);

		Core::Clock		*GetClock() const { return clock; }
		void			SetClock(Core::Clock * = 0, bool set_ui = true);

		/// Dump scene content to log.
		void			DumpContentToLog() const;

		Core::Camera	*current_camera;

/*!
	@name	Core::Item management.
	@{
*/
		void			SetupItemComponents(MItem *);
		void			AddItem(MItem *, bool setup_components);
		bool			RemoveItem(MItem *);

		void			QueueItemRemoval(MItem *);
		void			ProcessItemRemovalQueue();

		bool			ActivateItem(MItem *, bool activate);
		void			SetItemStatic(MItem *, bool is_static = true);

		void			QueueItemActivation(MItem *, bool activate = true, bool propagate = false);
		void			ProcessItemActivationQueue();

		MItem			*DuplicateItem(MItem *);

		MItemType		LocateItem(const Core::Item *, MItem **entity = 0) const;
static	MItem			*LocateManagedItem(Core::Item *);

		MItem			*Item(const char *name, MItem * = 0) const;
		MItem			*ItemFromUid(uint uid) const;

		const List <MLight *> &GetLightList() const { return light_list; }

		const SharedList <MItem *> &GetItemList() const { return item_list; }
		const SharedArrayList <MItem *> &GetActiveList() const { return active_list; }

		uint			GetItemCountByType(MItemType type) const;

		// Filter item list and extract items of a give type.
		template <class C> uint GetItemListByType(SharedList <C *> &out) const
		{
			out.Clear();
			ListForeachPtr(MItem *, i, item_list)
				if	(i->GetItemType() == C::GetMItemClassType())
					out.Add((C *)i);
			return out.GetCount();
		}
		// Find item by type.
		template <class C> C *FindItemByType(const char *name) const
		{
			ListForeachPtr(MItem *, i, item_list)
				if	(i->GetItemType() == C::GetMItemClassType())
					if	(i->name == name)
						return (C *)i;
			return 0;
		}
/*!
	@}
	@name	Group management.
	@{
*/
		Group			*AddGroup(Group *);
		bool			RemoveGroup(Group *);

		Group			*FindGroup(const char *name) const;

		uint			GetItemGroupList(const MItem *, SharedList <Group *> &) const;
		void			UnregisterItemFromGroups(MItem *) const;

		const SharedList <Group *> &GetGroupList() const { return group_list; }

		void			GroupMembersSetActive(Group *, bool = true);
		void			DeleteGroupAndMembers(Group *);
/*!
	@}
	@name	Profiling.
	@{
*/
		SceneProfiler	profiler;

		/// Display the profiler to screen using the renderer writer.
		void			DrawProfilerText(Renderer &, RasterFont *font[2], float &x, float &y);
/*!
	@}
	@name	Culling system.
	@{
*/
protected:

		Core::SimpleCullingSystem simple_culling_system;
		Core::OctreeCullingSystem octree_culling_system;

public:

		void			AddRenderable(Core::Renderable *, bool is_static = false);
		void			RemoveRenderable(Core::Renderable *);

		/// Push this scene renderable onto the renderer display queue.
		void			PushRenderable(Renderer &);
/*!
	@}
	@name	Environment.
	@{
*/
		Color			background_color, ambient_color;
		float			ambient_intensity, target_exposure;

		float			time_of_day;
		String			skybox_shader;
		String			skybox_layer[2];

		String			irradiance_probe, radiance_probe;

		float			fog_near, fog_far;
		Color			fog_color;

		AutoPtr <Render::IEnvironment> irenderer_environment;
/*!
	@}
	@name	Script system.
	@{
*/
protected:

		Script::sVM		vm;

public:

		Script::IVM		*GetVM() const { return vm; }

		AutoPtr <ISceneEvent> scene_event;
		AutoPtr <Script::ScriptedObject> scripted_object;

		/// Set as the script master global scene.
		void			SetAsScriptGlobalScene(Script::IVM * = 0);
///	@}

/*!
	@name	Motion system.
	@{
*/
		Core::SceneMotionContainer motion;

		/*!
			@short	Start a motion on the scene items.
			@note	Request an animation source group if you need control on the
					started animation sources.
		*/
		void			SetMotion(const char *name, Automation::SourceGroup ** = 0, float blend = Units::Sec(0.1), Automation::Player::AddSourceMode = Automation::Player::SourceSet, float weight = 1);
/*!
	@}
	@name	Physic system.
	@{
*/
		void			SynchronizePhysics();

		AutoPtr <PhysicWorld> physic_world;
		AutoPtr <IPhysicWorld> iphysic_world;

		MTrigger		*RaytraceTriggerList(Vector4 &s, Vector4 &d, float l = -1.f, Vector4 *i = 0);
/*
	@}
	@name	Scene Management.
	@{
*/
		bool			Create(PhysicWorld *);
		void			Reset();
		void			Clear(bool keep_script = false);

		void			InstanceSetup();
		void			RenderSetup(Core::ResourceFactories *, bool setup_items = true);
		void			Setup(ToolMode);

		void			RecordAllItemLocation();
///	@}

		void			GetMinMax(MinMax &) const;

protected:

		void			TestOptimizeGeometry(Render::Geometry *, uint octree_node_size);
		void			UpdateItemTransformation(MItem *, const Time &dt);

public:

		void			OptimizeForRealtime(uint octree_node_size = 1024);

		void			UpdateTriggers();
		void			Update(uint = SceneUpdateAll);

/*!
	@name	Scene serialization
	@{
*/
protected:

		bool			IsItemToBeSaved(MItem *, const Group *, uint flag) const;
		NML::Tag		*LinkInfoAsMetaTag(MItem *, const Group * = 0, uint = SceneIOAll) const;
		NML::Tag		*SkinInfoAsMetaTag(MObject *o, const Group * = 0, uint = SceneIOAll) const;

public:

		/// Load all scene resources in render cache.
static	bool			PreloadResources(Render::ResourceFactory &, const char *uri);

		/// Load scene from meta file, store content to group.
		bool			FromMetaTagStoreGroup(const NML::Tag &, Group ** = 0, uint = SceneIOAll, ToolMode = NoTool, int load_level = 0);
		bool			FromMetaFileStoreGroup(const char *, Group ** = 0, uint = SceneIOAll, ToolMode = NoTool, int load_level = 0);
		bool			FromMetaTag(const NML::Tag &tag, ToolMode tool = NoTool) { return FromMetaTagStoreGroup(tag, 0, SceneIOAll, tool); }

		NML::Tag		*AsMetaTag(const Group *, uint = SceneIOAll, ToolMode = NoTool) const;
		NML::Tag		*AsMetaTag() const { return AsMetaTag(0); }
/// @}

		AutoPtr <RenderData> render_data;

		Scene(Script::IVM *vm = 0);
virtual	~Scene();
};

	} // S3D
} // GS


#endif // __NSCENE__
