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


		#include "project/project.h"
		#include "scene3d/scene.h"
		#include "ui/ui.h"
		#include "script/script_engine_types.h"
		#include "script/script_variant.h"
		#include "metafile/nml_object.h"
		#include "platform.h"

		using namespace GS::Core;


//------------------------------------------------------------------------------
bool				Project::IsProjectActionAllowed(const char *f)
{
	if	(prohibit_project_action)
		__ERR__(__LOG_E__ << "Cannot call '" << f << "' while rendering or evaluating the project scene stack.\n", false)
	return true;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Project::ResetStatistics()
{
	ListForeachPtr(ProjectSceneInstance *, i, instance_list)
		switch (i->type)
		{
			case ProjectSceneInstance::Type_Scene2d:
				break;

			case ProjectSceneInstance::Type_Scene3d:
				i->instance_3d->profiler.ResetProfiles();
				break;
		}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
uint				Project::GetActiveCount() const
{
	uint count = 0;
	ListForeachPtr(ProjectSceneInstance *, i, instance_list)
		if	(i->active)
			++count;
	return count;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
ProjectSceneInstance *Project::FindSceneInstance(const char *name) const
{
	ListForeachPtr(ProjectSceneInstance *, s, instance_list)
		switch (s->GetType())
		{
			case	ProjectSceneInstance::Type_Scene3d:
				if	(s->instance_3d->name == name)
					return s;
				break;

			case	ProjectSceneInstance::Type_Scene2d:
				if	(s->instance_2d->name == name)
					return s;
				break;
		}
	return NULL;
}
ProjectSceneInstance *Project::FindSceneInstance(const GS::S3D::Scene *scene) const
{
	ListForeachPtr(ProjectSceneInstance *, s, instance_list)
		if	(s->instance_3d == scene)
			return s;
	return NULL;
}
ProjectSceneInstance *Project::FindSceneInstance(const GS::S2D::Scene *scene) const
{
	ListForeachPtr(ProjectSceneInstance *, s, instance_list)
		if	(s->instance_2d == scene)
			return s;
	return NULL;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void				Project::New()
{
	Close();

	name = "New Project";

	root_script_list.Clear();
	search_path.Clear();
}
bool				Project::Open(bool project_exec_mode)
{
	__PROJECT_ACTION_SAFETY_CHECK(false)

	vm->Set("g_platform", Platform::Get().GetName().c_str());
	vm->Set("g_context_project", project_exec_mode);

	if	(!CompileRootScripts())
		return false;
	if	(!script_unit->Open())
		return false;

	return true;
}
bool				Project::Setup()
{
	__PROJECT_ACTION_SAFETY_CHECK(false)

	// Script callback.
	if	(script_unit->SetupFunctionCall("OnSetup", script_unit->setup_callback))
		script_unit->DoFunctionCall();

	return true;
}
void				Project::Close()
{
	__PROJECT_ACTION_SAFETY_CHECK_RAW

	// OnClose callback.
	if	(script_unit->SetupFunctionCall("OnClose"))
		script_unit->DoFunctionCall(0);

	// Drop content.
	file_path.Clear();

	// Drop all layer/instance/scene.
	layer_list.Clear();
	instance_list.Clear();

	flags = 0;	

	script_unit->Close();
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
ProjectLayer		*Project::AddLayer(ProjectSceneInstance *scene, float zorder)
{
	ProjectLayer *layer = new ProjectLayer(scene, zorder);
	if	(!layer)
		__ERR__(__LOG_E__ << "Failed to allocate a new project scene layer.\n", NULL)
	layer_list.Add(layer);
	return layer;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
ProjectSceneInstance *Project::Instantiate(GS::S3D::Scene *scene)
{
	ProjectSceneInstance *inst = new ProjectSceneInstance(scene);
	if	(inst)
		instance_list.Add(inst);
	return inst;
}
ProjectSceneInstance *Project::Instantiate(GS::S2D::Scene *scene)
{
	ProjectSceneInstance *inst = new ProjectSceneInstance(scene);
	if	(inst)
		instance_list.Add(inst);
	return inst;
}
ProjectSceneInstance *Project::Instantiate(const char *name)
{
	__PROJECT_ACTION_SAFETY_CHECK(NULL)

	// Scene is already instantiated.
	ProjectSceneInstance *inst = FindSceneInstance(name);
	if	(inst)
		return inst;

	// Create new instance.
	NML::File file;
	if	(!NML::Parser::Load(name, file))
		__ERR__(__LOG_E__ << "Could not instantiate scene '" << name << "', file not found.\n", NULL)

	// Scene 3d
	if	(file.GetTag("Scene;"))
	{
		S3D::Scene *scene = new S3D::Scene(vm);
		if	(!scene)
			__ERR__(__LOG_E__ << "  Failed to allocate scene object.\n", NULL)
		scene->SetClock(clock);

		if	((inst = new ProjectSceneInstance(scene)) == NULL)
		{
			_safe_delete(scene);
			__ERR__(__LOG_E__ << "  Failed to allocate project scene instance.\n", NULL)
		}

		scene->Create(iproject_factory->NewPhysicWorld());
		NML::LoadFromFile(*scene, file);
		scene->name = name;
		scene->InstanceSetup();
		scene->RenderSetup(factories);
		scene->SetAsScriptGlobalScene();
		scene->Setup(NoTool);
		scene->Reset();
	}
	else	if	(file.GetTag("UIScene;"))
	{
		S2D::Scene *scene = new S2D::Scene(vm);
		if	(!scene)
			__ERR__(__LOG_E__ << "  Failed to allocate scene 2D object.\n", NULL)
		scene->SetClock(clock);

		if	((inst = new ProjectSceneInstance(scene)) == NULL)
		{
			_safe_delete(scene);
			__ERR__(__LOG_E__ << "  Failed to allocate project scene instance.\n", NULL)
		}

		NML::LoadFromFile(*scene, file);
		scene->name = name;
		scene->RenderSetup(factories);
		scene->SetAsScriptGlobalScene();
		scene->Setup();
		scene->Reset();
	}

	if	(inst)
		instance_list.Append(inst);
	return inst;
}
void				Project::Delete(ProjectSceneInstance *inst)
{
	__PROJECT_ACTION_SAFETY_CHECK_RAW
	if	(!inst)
		return;

	// Callback.
	if	(script_unit->SetupFunctionCall("OnUnloadScene"))
	{
		script_unit->PushFunctionCallArgument(Script::Variant((void *)inst, Script::typetag_ProjectScene));
		script_unit->DoFunctionCall(0);
	}

	// Drop scene from all layers using it.
	ListForeachPtr(ProjectLayer *, layer, layer_list)
		if	(layer->inst == inst)
			layer_list.Remove(layer);

	instance_list.Remove(inst);
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
Project::Project(ResourceFactories *f, GS::IFontFactory *ff, GS::Script::IVM *_vm) : vm(_vm)
{
	factories = f;

	name = "New Project";

	authors = "Authors";
	description = "New project";
	company = "Empty Notice";

	clock = new Clock;

	font_cache = new FontCache(ff);
	font_cache->LoadFont("@core/default.ttf");

	script_unit = new ProjectScriptUnit(vm);
	script_unit->SetInterfaceObject(this, Script::typetag_Project);

	prohibit_project_action = false;
}
Project::~Project()
{
	Close();
}
//------------------------------------------------------------------------------
