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


		#include "scene3d/scene_renderer_environment_interface.h"
		#include "scene3d/mlight.h"
		#include "scene3d/scene.h"
		#include "physic/physic_world.h"
		#include "core/renderer.h"
		#include "sort/sort.h"

		using namespace GS;
		using namespace GS::Core;
		using namespace GS::S3D;


//------------------------------------------------------------------------------
float				IEnvironment::GetClock() const
{	return scene->GetClock()->Getf();	}
float				IEnvironment::GetTimeOfDay() const
{	return scene->time_of_day;	}
Render::Shader		*IEnvironment::GetSkyboxShader() const
{	return scene->render_data ? scene->render_data->skybox_shader.c_ptr() : NULL;	}
bool				IEnvironment::GetSkyboxLayers(Render::sTexture layers[2]) const
{
	if	(scene->render_data.IsValid())
		for (uint n = 0; n < 2; ++n)
			layers[n] = scene->render_data->skybox_layer[n];

	return layers[0].IsValid() && layers[1].IsValid();
}
void				IEnvironment::GetEnvironmentProbe(Render::sTexture &radiance, Render::sTexture &irradiance) const
{
	if	(scene->render_data.IsValid())
	{
		radiance = scene->render_data->radiance_probe;
		irradiance = scene->render_data->irradiance_probe;
	}
}
bool				IEnvironment::IsFogEnabled() const
{	return scene->fog_far > 0.f;	}
bool				IEnvironment::GetFogConfiguration(Color &color, float &fog_near, float &fog_far) const
{
	if	(scene->fog_far < 0.f)
		return false;

	color = scene->fog_color;
	fog_near = scene->fog_near;
	fog_far = scene->fog_far;
	return true;
}
Color				IEnvironment::GetClearColor() const
{	return scene->background_color;		}
Color				IEnvironment::GetAmbientColor() const
{	return scene->ambient_color * scene->ambient_intensity;		}
static float CompareLightPriority(const MLight *a, const MLight *b) { return a->GetPriority() - b->GetPriority(); }
void				IEnvironment::GetLightsInFrustum(const Vector4 &world_pos, const Frustum &frustum, List <Light *> &list, uint limit) const
{
	list.Clear();

	// On very large scenes filtering items by type can become very slow.
#if	0
	nSharedList <nMLight *> lights;
	if	(!scene->GetItemListByType(lights))
		return;
#else
	List <MLight *> lights;
	scene->GetLightList().Clone(lights);
#endif
	lights.Sort(CompareLightPriority);

	ListForeachPtr(MLight *, l, lights)
	{
		if	(!l->isActive())
			continue;

		// Check light range and clip distance.
		if	(l->range)
		{
			if	(Vector4::Dist(world_pos, l->GetMatrix().GetRow(3)) >= (l->clip_distance + l->range))
				continue;

			// Check frustum intersection.
			switch (l->model)
			{
				case Light::Model_Point:
					if	(frustum.ClassifySphere(l->GetMatrix().GetRow(3), l->range) == Frustum::Outside)
						continue;
					break;

				case Light::Model_Spot:
					if	(frustum.ClassifyFrustrum(l->frustum) == Frustum::Outside)
						continue;
					break;

				default: break;
			}
		}

		list.Add(l);
		if	(list.GetCount() == limit)
			break;
	}
}
void				IEnvironment::GetClosestLights(const Vector4 &world_pos, List <Light *> &list, uint limit) const
{
	list.Clear();

#if	0
	nSharedList <nMLight *> lights;
	if	(!scene->GetItemListByType(lights))
		return;
#else
	List <MLight *> lights;
	scene->GetLightList().Clone(lights);
#endif

	Array <Sort <float, MLight *>::Entry> array(lights.GetCount());
	if	(!array)
		__ERRRAW__(__LOG_E__ << "Failed to allocated sort structure.\n")

	int c = 0;
	ListForeachPtr(MLight *, l, lights)
		if	(l->isActive())
		{
			array[c].o = l;
			array[c].v = Vector4::Dist2(world_pos, l->GetMatrix().GetRow(3)) * (1.f - l->GetPriority());
			++c;
		}

	Sort <float, MLight *>::QuickSort(c, array);
	for	(int n = 0; n < c; n++)
	{
		list.Add(array[n].o);
		if	(list.GetCount() == limit)
			break;
	}
}
Camera				*IEnvironment::GetCurrentCamera() const
{	return scene->current_camera;	}
void				IEnvironment::OnRenderUser(Renderer *renderer) const
{
	scene->scene_event->OnRenderUser(scene);

	// Debug physics.
	if	(scene->flags.IsSet(Scene::FlagDebugPhysics))
		if	(PhysicWorld *world = scene->physic_world)
		{
			if	(!world->HasDebugger())
				world->CreateDebugger(renderer);

			renderer->SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix());
			renderer->ApplyCamera();

			world->DrawDebug(*renderer, scene->current_camera, NULL/*profiler_font[0]*/, true);
			world->DrawDebug(*renderer, scene->current_camera, NULL/*profiler_font[0]*/, false);
		}
}
//------------------------------------------------------------------------------
