//
// Tools_PhotonMapping_LightSource_Object.cpp
//
// Created by KJL - 15/01/2004 15:57:52
//
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

// This file's header.
#include "Tools_PhotonMapping_LightSource_Object.h"


#include "Core/Asura_UserInput.h"
#include "Interface/Asura_Overlay_System.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Overlay.h"
#include "Rendering/Asura_Text_Output.h"

#include "Tools_Overlays.h"

#include "Interface/Tools_ColourPicker.h"
#include "Lighting/Tools_Mode_PhotonMapping.h"
#include "Lighting/Tools_PhotonMapping_System.h"
#include "Lighting/Tools_PhotonMapping_LightSource.h"
#include "Lighting/Tools_LightSets.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Objects/Tools_ObjectSystem_Param.h"


#include "Mesh/Asura_Mesh_System.h"

#include "Core/Asura_TextureManagement.h" // see lightbulb stuff below

//#include "Objects/Tools_Object_System.h"

#include "Misc/Tools_Render.h"
#include "Core/Asura_Timers.h"

#include "Camera/Asura_Camera.h"

static const char* szPHOTONSTATUS_BLANK		= "";
static const char* szPHOTONSTATUS_UPTODATE	= "Photons are up-to-date";	
static const char* szPHOTONSTATUS_INVALID	= "Photons need recalc";

Asura_String g_xLightStatusString;

List<Tools_PhotonMapping_LightSource_Object*> Tools_PhotonMapping_LightSource_Object::s_xObjectList;
bool Tools_PhotonMapping_LightSource_Object::s_bObjectsCreated = false;

bool Tools_PhotonMapping_LightSource_Object::s_bShowCone = false;
bool Tools_PhotonMapping_LightSource_Object::s_bWireframeCone = false;

Tools_PhotonMapping_LightSource_Object* Tools_PhotonMapping_LightSource_Object::s_pxThis = 0;

//////////////////////


TOOLS_DECLARE_SHARED_PARAM(bool,	0x522A5A8F,	LightSource_ShortRange)
TOOLS_DECLARE_SHARED_PARAM(bool,	0xCBA1C3E4,	LightSource_UseRange)
TOOLS_DECLARE_SHARED_PARAM(bool,	0xA2B112F8,	LightSource_Bleached)
TOOLS_DECLARE_SHARED_PARAM(int,		0x5C6EACC7,	LightSource_Accuracy)
TOOLS_DECLARE_SHARED_PARAM(float,	0xECA3D7F7,	LightSource_Power)
TOOLS_DECLARE_SHARED_PARAM(float,	0x25EAA8F5,	LightSource_InnerRange)
TOOLS_DECLARE_SHARED_PARAM(float,	0x5CC965F0,	LightSource_OuterRange)
TOOLS_DECLARE_SHARED_PARAM(float,	0xEBCFC7C5,	LightSource_Angle)
TOOLS_DECLARE_SHARED_PARAM(float,	0x6A5E38ED,	LightSource_ReflectionChance)

TOOLS_DECLARE_SHARED_PARAM(float,	0xD8D21C03,	LightSource_Red)
TOOLS_DECLARE_SHARED_PARAM(float,	0xEC261DF5,	LightSource_Green)
TOOLS_DECLARE_SHARED_PARAM(float,	0x416A3B28,	LightSource_Blue)
TOOLS_DECLARE_SHARED_PARAM(float,	0xD8D1F86A,	LightSource_Hue)
TOOLS_DECLARE_SHARED_PARAM(float,	0xE6133420,	LightSource_Saturation)
TOOLS_DECLARE_SHARED_PARAM(float,	0xDA78CAEA,	LightSource_Luminance)

static const Asura_Hash_ID g_uHash_LightSource_LightSet0 = 0xBD2D1016; 
static const Asura_Hash_ID g_uHash_LightSource_LightSetLast = g_uHash_LightSource_LightSet0+TOOLS_MAX_LIGHT_SETS-1; 
static Tools_ObjectSystem_SharedValue<bool> g_axParam_LightSource_LightSet[TOOLS_MAX_LIGHT_SETS];



Tools_PhotonMapping_LightSource_Object::Tools_PhotonMapping_LightSource_Object(const Asura_Handle iLightSourceHandle)
:Tools_Object(TOT_PhotonMapping_LightSource)
{
	m_iLightSourceHandle = iLightSourceHandle;	
	m_bColoursInverted = false;

	Asura_String mesh_name = "ToolsShape_Light";
	Mesh = Asura_Mesh_System::GetMesh(mesh_name);
	Orientation.Identity();

	Tools_PhotonMapping_LightSource* pxLightSource = Tools_PhotonMapping_System::GetLightSource(m_iLightSourceHandle);

	if (pxLightSource)
	{
		
		Asura_Vector_3 xColour = pxLightSource->GetColour();

		m_fPower = pxLightSource->GetPower();
		
		if(m_fPower<0 && Tools_Mode_PhotonMapping::s_bInvertColourForShadowLights)
		{
			m_bColoursInverted = true;
			xColour.x = 255.0f - xColour.x;			
			xColour.y = 255.0f - xColour.y;			
			xColour.z = 255.0f - xColour.z;			
		}

		m_xColourPair.Initialise(xColour.x,xColour.y,xColour.z);

		
		m_fAngle = pxLightSource->GetAngle();
		
		Position = pxLightSource->GetPosition();
		Orientation = pxLightSource->GetOrientation();

		m_bParallelSource = pxLightSource->IsParallel();
		m_bBleachedSource = pxLightSource->IsBleached();
		m_bShortRange = pxLightSource->IsShortRange();
	
		m_iAccuracy = pxLightSource->GetAccuracy();

		m_bUseRange = pxLightSource->GetUseRange();
		m_fInnerRange = pxLightSource->GetInnerRange();
		m_fOuterRange = pxLightSource->GetOuterRange();

		m_fReflectionChance = pxLightSource->GetReflectionChance();
		m_uLightSets = pxLightSource->GetLightSets();

		m_fInitialPower = m_fPower;
		m_xInitialColour = xColour;

	}

	Tools_PhotonMapping_TemplateData* pxTemplateData = Tools_PhotonMapping_System::GetTemplateData(m_iLightSourceHandle);
	Asura_Assert(pxTemplateData,"");

	m_uTemplateGuid = pxTemplateData->m_uTemplateGuid;
	m_uID = pxTemplateData->m_uID;

	
	UpdateName();

	m_bChanged = false;
	m_bChanged_NoRelightNeeded = false;
	m_bInnerRangeChanged = false;
	m_bOuterRangeChanged = false;
	

	s_xObjectList.add_entry(this);

	SetDistantIconTexture("\\tools\\lightbulb.dds");

	
}

Tools_PhotonMapping_LightSource_Object::~Tools_PhotonMapping_LightSource_Object()
{
	s_xObjectList.delete_entry(this);
}


void Tools_PhotonMapping_LightSource_Object::OnSelection()
{
	s_pxThis = this;
	Tools_Object::OnSelection();
	Tools_PhotonMapping_LightSource* pxLightSource = Tools_PhotonMapping_System::GetLightSource(m_iLightSourceHandle);

	if (pxLightSource)
	{
		//will be set correctly in the per-frame update
		g_xLightStatusString = szPHOTONSTATUS_BLANK;
	}

	CreateLightPropertiesOverlay();

	Tools_Mode_PhotonMapping::s_iCurrentLightSourceHandle = m_iLightSourceHandle;
}


void Tools_PhotonMapping_LightSource_Object::OnDeselection()
{
	m_xColourPair.Update();

	Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_LIGHTPROPERTIES);

	Tools_Object::OnDeselection();

	Tools_Mode_PhotonMapping::s_iCurrentLightSourceHandle = ASURA_INVALID_HANDLE;
	s_pxThis = 0;
}


void Tools_PhotonMapping_LightSource_Object::UpdatePositionInAsuraData(bool /*bUpdateLinkedObjects*/)
{
	Tools_PhotonMapping_LightSource* pxLightSource = Tools_PhotonMapping_System::GetLightSource(m_iLightSourceHandle);

	if (pxLightSource)
	{
		if (Orientation != pxLightSource->GetOrientation() || Position != pxLightSource->GetPosition())
		{
			UpdateAsuraLight();
			SetLightingModified();
		}
	}
}

bool Tools_PhotonMapping_LightSource_Object::IsHidden()
{

	//Light sets can also be hidden by the 'hide by type' overlay
	if(Tools_Object_System::IsHideByLightSetEnabled())
	{
		if(!(m_uLightSets & Tools_Object_System::GetVisibleLightSets())) return true;
	}

	if(Tools_LightSets::ShouldHideObjectsInOtherSets())
	{
		if(!Tools_LightSets::IsMemberOfCurrentLightSets(m_uLightSets)) return true;
	}



	return Tools_Object::IsHidden();
}


void Tools_PhotonMapping_LightSource_Object::CreateLightPropertiesOverlay()
{
	Asura_Overlay* pxOverlay;
	Asura_Dialog* pxDialog;

	pxOverlay = new Asura_Overlay(GetName(), false, &Tools_Mode_PhotonMapping::s_xLightPropertiesOverlayPosition);
	Asura_Vector_2 xOffset = Asura_Vector_2(1.0f,3.0f);

	pxDialog = new Asura_Function_Dialog("Rename",xOffset,Rename);
	pxDialog->SetHelpText("Rename this light");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();


	//Tab control
	static char* s_aszTabs[2] = {"Main","Light Sets"};
	Asura_Tabs_Dialog* pxTabsDialog = new Asura_Tabs_Dialog(xOffset,s_aszTabs,2);
	pxOverlay->AddDialog(pxTabsDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();

	float fTabsStartY = xOffset.y;
	float fTabsEndY = xOffset.y;

	pxOverlay->SetCurrentParentDialog(pxTabsDialog);	


	//Main Tab
	pxTabsDialog->SelectTab(0);

	if(!m_bParallelSource)
	{
		pxDialog = new Asura_Dynamic_Dialog( &g_xLightStatusString , xOffset );
		pxDialog->SetIsEnabledFunction(IsEnabled_IsNotParallel);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Function_Dialog("Recalc Photons [1]",xOffset,Tools_Mode_PhotonMapping::RecalcPhotonMapsForSelectedLights);
		pxDialog->SetHelpText("Recalculates the lighting for the currently selected lights.\nShortcut: 1");
		pxDialog->SetIsEnabledFunction(IsEnabled_IsNotParallel);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::LargeSpacing();

		//pxDialog = new Asura_Tickbox_Dialog("Short Range",xOffset,&(m_bShortRange),&m_bChanged_NoRelightNeeded);
		pxDialog = new Asura_SharedTickbox_Dialog("Short Range",xOffset,&g_xParam_LightSource_ShortRange);
		pxDialog->SetHelpText("This will make a light that is more accurate at short ranges , and less prone to bleeding through polygons.\nThe power setting effectively uses a different scale when this is used.");
		pxDialog->SetIsEnabledFunction(IsEnabled_IsNotParallel);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Numerical_Dialog("Accuracy",xOffset,&g_xParam_LightSource_Accuracy, 1, TOOLS_PHOTONMAPPING_MAX_ACCURACY, 1,"%d");
		pxDialog->SetIsEnabledFunction(IsEnabled_IsNotParallel);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::LargeSpacing();
	}

	pxDialog = new Asura_Tickbox_Dialog("Invert When Negative",xOffset,&Tools_Mode_PhotonMapping::s_bInvertColourForShadowLights);
	pxDialog->SetHelpText("If ticked then the displayed colours will be inverted when dealing with lights that have negative power (ie ,it will indicate what colour will be left behind).");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	
	xOffset.x += 12.0f;
	pxDialog = new Tools_ColourPicker_Dialog("Light Colour",xOffset,Asura_Vector_2(4.0f,3.0f),&g_xParam_LightSource_Red,&g_xParam_LightSource_Green,&g_xParam_LightSource_Blue,255.0f,NULL);
	pxOverlay->AddDialog(pxDialog);
	xOffset.x -= 12.0f;

	pxDialog = new Asura_Numerical_Dialog("Red",xOffset,&(g_xParam_LightSource_Red), 0,255.0f,1.0f,"%0.0f");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog("Green",xOffset,&(g_xParam_LightSource_Green), 0,255.0f,1.0f,"%0.0f");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog("Blue",xOffset,&(g_xParam_LightSource_Blue), 0,255.0f,1.0f,"%0.0f");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();

	pxDialog = new Asura_Numerical_Dialog("Hue",xOffset,&(g_xParam_LightSource_Hue), 0,100.0f,1.0f,"%0.0f");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog("Saturation",xOffset,&(g_xParam_LightSource_Saturation), 0,100.0f,1.0f,"%0.0f");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Numerical_Dialog("Luminance",xOffset,&(g_xParam_LightSource_Luminance), 0,100.0f,1.0f,"%0.0f");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();

	float fMinPower = m_bParallelSource ? 0.0f : -9999999.0f;
	pxDialog = new Asura_Numerical_Dialog("Power",xOffset,&g_xParam_LightSource_Power, fMinPower,9999999.0f,0.1f,"%0.3f");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();

	if(!m_bParallelSource)
	{
		const char* pszRangeHelpText = "If 'Use Range' is enabled then the distance that photons can travel are limited by the outer range value.\nThe photons will have maximum brightness until they have travelled the 'Inner Range' distance , and will then have a linear falloff until the Outer Range is hit.";

		//pxDialog = new Asura_Tickbox_Dialog("Use Range",xOffset,&m_bUseRange,&m_bChanged_NoRelightNeeded);
		pxDialog = new Asura_SharedTickbox_Dialog("Use Range",xOffset,&g_xParam_LightSource_UseRange);
		pxDialog->SetHelpText(pszRangeHelpText);
		pxOverlay->AddDialog(pxDialog);

		xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;
		pxDialog = new Asura_Tickbox_Dialog("Show",xOffset,&Tools_Mode_PhotonMapping::s_bShowLightRadius);
		pxDialog->SetHelpText("Renders a sphere indicating the range of the selected light. (If the light is flagged as using range).");
		pxOverlay->AddDialog(pxDialog);
		xOffset.x = 1.0f;
		xOffset.y+=Asura_Dialog::DefaultSpacing();


		pxDialog = new Asura_Numerical_Dialog("Inner Range",xOffset,&g_xParam_LightSource_InnerRange,0.0f,9999.0f,1.0f,"%.1f");
		pxDialog->SetHelpText(pszRangeHelpText);
		pxDialog->SetIsEnabledFunction(IsEnabled_UsesRange);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Numerical_Dialog("Outer Range",xOffset,&g_xParam_LightSource_OuterRange,0.0f,9999.0f,1.0f,"%.1f");
		pxDialog->SetHelpText(pszRangeHelpText);
		pxDialog->SetIsEnabledFunction(IsEnabled_UsesRange);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::LargeSpacing();


		
		pxDialog = new Asura_Numerical_Dialog("Angle",xOffset,&g_xParam_LightSource_Angle,0.0f,360.0f,10.0f,"%.1f");
		pxDialog->SetHelpText("Angle of light in degrees. For making directional lights.");
		pxDialog->SetIsEnabledFunction(IsEnabled_IsNotParallel);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Tickbox_Dialog("Show Cone",xOffset,&s_bShowCone);
		pxDialog->SetHelpText("Draws a cone if this is a directional light");
		pxDialog->SetIsEnabledFunction(IsEnabled_IsAngular);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

		pxDialog = new Asura_Tickbox_Dialog("Wireframe",xOffset,&s_bWireframeCone);
		pxDialog->SetHelpText("Causes cone to be drawn wireframe , if being shown");
		pxDialog->SetIsEnabledFunction(IsEnabled_IsAngular);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::LargeSpacing();

		pxDialog = new Asura_Numerical_Dialog("Bounce Chance",xOffset,&g_xParam_LightSource_ReflectionChance,0.0f,0.99f,0.01f,"%.2f");
		pxDialog->SetHelpText("Chance of photons emitted by this light bouncing when they hit a polygon.\nNote that if this is set too high , then the overall brightness will fall ,since many photons will exceed the maximum number of bounces.");
		pxDialog->SetIsEnabledFunction(IsEnabled_IsNotParallel);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();

	
		//pxDialog = new Asura_Tickbox_Dialog("Bleached",xOffset,&(m_bBleachedSource),&m_bChanged);
		pxDialog = new Asura_SharedTickbox_Dialog("Bleached",xOffset,&g_xParam_LightSource_Bleached);
		pxDialog->SetIsEnabledFunction(IsEnabled_IsNotParallel);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::LargeSpacing();
	}

	//End of main tab
	if(xOffset.y > fTabsEndY) fTabsEndY = xOffset.y;

	//Light set tab
	pxTabsDialog->SelectTab(1);
	xOffset.y = fTabsStartY;

	//add a checkbox for each light set
	for(u_int i=0;i<TOOLS_MAX_LIGHT_SETS;i++)
	{
		const char* szSetName = Tools_LightSets::GetLightSetNameByIndex(i);
		if(!szSetName) continue;

		g_axParam_LightSource_LightSet[i].SetParamID(g_uHash_LightSource_LightSet0+i);

		pxDialog = new Asura_SharedTickbox_Dialog(szSetName,xOffset,&g_axParam_LightSource_LightSet[i]);
		pxDialog->SetHelpText("Enables this light for this light set.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();
	}

	//End of light set tab
	if(xOffset.y > fTabsEndY) fTabsEndY = xOffset.y;

	//End of tabs
	pxOverlay->SetCurrentParentDialog(0);
	static u_int s_uSelectedTab = 0;
	pxTabsDialog->SetSelectedTabPointer(&s_uSelectedTab);
	xOffset.y = fTabsEndY + 1.0f;


	pxDialog = new Asura_Function_Dialog("Delete Light",xOffset,Tools_Mode_PhotonMapping::DestroyCurrentLightSource);
	pxOverlay->AddDialog(pxDialog);

	//set things up so that right clicking on the overlay will be equivalent to right clicking on the object
	//RAR 06/11/2007
	pxOverlay->SetContextMenuCallback(Tools_Object_System::AddContextMenuItemsFromOverlay, this);

	pxOverlay->m_uID = TOOLS_OVERLAY_LIGHTPROPERTIES;
	Asura_Overlay_System::SubmitNewOverlay(pxOverlay,false);
}

void Tools_PhotonMapping_LightSource_Object::UpdateWhileSelected()
{
	//handle inverting colours for negative lights
	UpdateInvertedColourStatus();

	m_xColourPair.Update();

	
	//copy any changes to the asura light data
	UpdateAsuraLight();

	Tools_PhotonMapping_LightSource* pxLightSource = Tools_PhotonMapping_System::GetLightSource(m_iLightSourceHandle);
	if (pxLightSource)
	{
		if (pxLightSource->IsParallel())
		{
			g_xLightStatusString = szPHOTONSTATUS_BLANK;
		}
		else if (pxLightSource->IsPhotonMapCompletelyValid())
		{
			g_xLightStatusString = szPHOTONSTATUS_UPTODATE;
		}
		else if(pxLightSource->IsPhotonMapPartiallyValid())
		{
			char szText[100];
			Asura_PrintfToBfr(szText,"Partially valid (Acc %d)",100,true,pxLightSource->GetPhotonMapValidity());
			g_xLightStatusString = szText;
		}
		else
		{
			g_xLightStatusString = szPHOTONSTATUS_INVALID;
		}
	}

	UpdateIfChanged();
}

void Tools_PhotonMapping_LightSource_Object::UpdateAsuraLight()
{
	Tools_PhotonMapping_LightSource* pxLightSource = Tools_PhotonMapping_System::GetLightSource(m_iLightSourceHandle);

	if (pxLightSource)
	{


		if (Orientation != pxLightSource->GetOrientation())
		{
			pxLightSource->SetOrientation(Orientation);

			// We want to update the lighting of the environment if a parallel light source has its orientation changed.
			if (pxLightSource->IsParallel())
			{
				m_bChanged = true;
			}
		}
		
		if (Position != pxLightSource->GetPosition())
		{
			pxLightSource->SetPosition(Position);
		}

		if (m_iAccuracy != static_cast<int>(pxLightSource->GetAccuracy()))
		{
			pxLightSource->SetAccuracy(m_iAccuracy);
		}


		Asura_Vector_3 xColour(m_xColourPair.m_xRGB.m_fR,m_xColourPair.m_xRGB.m_fG,m_xColourPair.m_xRGB.m_fB);

		if(m_bColoursInverted)
		{
			xColour.x = 255.0f - xColour.x;
			xColour.y = 255.0f - xColour.y;
			xColour.z = 255.0f - xColour.z;
		}

		pxLightSource->SetColour(xColour);
		pxLightSource->SetPower(m_fPower);
		pxLightSource->SetAngle(m_fAngle);
		pxLightSource->SetParallel(m_bParallelSource);
		pxLightSource->SetBleached(m_bBleachedSource);
		pxLightSource->SetShortRange(m_bShortRange);
		pxLightSource->SetUseRange(m_bUseRange);
		pxLightSource->SetInnerRange(m_fInnerRange);
		pxLightSource->SetOuterRange(m_fOuterRange);
		pxLightSource->SetReflectionChance(m_fReflectionChance);
		pxLightSource->SetLightSets(m_uLightSets);

	}
}

void Tools_PhotonMapping_LightSource_Object::UpdateInvertedColourStatus() const
{
	bool bShouldInvert = m_fPower<0 && Tools_Mode_PhotonMapping::s_bInvertColourForShadowLights;
	if(bShouldInvert != m_bColoursInverted)
	{
		m_bColoursInverted = !m_bColoursInverted;
		m_xColourPair.m_xRGB.m_fR = 255.0f - m_xColourPair.m_xRGB.m_fR;
		m_xColourPair.m_xRGB.m_fG = 255.0f - m_xColourPair.m_xRGB.m_fG;
		m_xColourPair.m_xRGB.m_fB = 255.0f - m_xColourPair.m_xRGB.m_fB;

		m_xInitialColour.x = 255.0f-m_xInitialColour.x;
		m_xInitialColour.y = 255.0f-m_xInitialColour.y;
		m_xInitialColour.z = 255.0f-m_xInitialColour.z;

		m_xColourPair.Update();
	}
	
}

void Tools_PhotonMapping_LightSource_Object::Render()
{
	
	
	if(IsSelected() && Tools_Mode_PhotonMapping::s_bShowLightRadius && m_bUseRange && !m_bParallelSource)
	{
		//show the light's inner and outer range
		extern void Render_Sphere( const Asura_Vector_3& xCentre, float fRadius, int iColour );
		if(m_fInnerRange)
		{
			Render_Sphere(Position,m_fInnerRange,0x7f0000ff);
		}
		if(m_fOuterRange)
		{
			Render_Sphere(Position,m_fOuterRange,0x7fff0000);
		}
	}
	
	
	
	Tools_Object::Render();

	if(!IsBeingRenderedAsIcon())
	{
		//draw some of the sides using the colour of the light
		//get the colour to use
		int colour = 0xff000000|m_xColourPair.m_xRGB.GetAsUint();


		Tools_Render::RenderPolygonOutline(Mesh,0,&Position,&Orientation,colour,Tools_Render::POM_ForceSolid);
		Tools_Render::RenderPolygonOutline(Mesh,1,&Position,&Orientation,colour,Tools_Render::POM_ForceSolid);
		Tools_Render::RenderPolygonOutline(Mesh,2,&Position,&Orientation,colour,Tools_Render::POM_ForceSolid);
		Tools_Render::RenderPolygonOutline(Mesh,3,&Position,&Orientation,colour,Tools_Render::POM_ForceSolid);
	}
	

	if(IsSelected() && s_bShowCone && m_fAngle != 360.0f)
	{
		//draw cone for light
		//determine colour...
		u_int uR = Asura_Maths::ClampToRange(Asura_Maths::FloatToInt(m_xColourPair.m_xRGB.m_fR),0,255);
		u_int uG = Asura_Maths::ClampToRange(Asura_Maths::FloatToInt(m_xColourPair.m_xRGB.m_fG),0,255);
		u_int uB = Asura_Maths::ClampToRange(Asura_Maths::FloatToInt(m_xColourPair.m_xRGB.m_fB),0,255);
		u_int uA = s_bWireframeCone ? 255 : 127;
		u_int uColour = (uA<<24)|(uR<<16)|(uG<<8)|uB;

		Asura_Angle fAngle = (m_fAngle/180.0f)*Asura_Maths::Pi;

		Tools_Render::RenderCone(Position,Orientation,2.0f,fAngle,uColour,s_bWireframeCone);

	}
}





void Tools_PhotonMapping_LightSource_Object::Destroy()
{
	Tools_PhotonMapping_System::DestroyLightSource(m_iLightSourceHandle);
	Tools_Object::Destroy();

	// force a mesh relight to take account of the changes
	Tools_PhotonMapping_System::InvalidateEnvironmentIrradiance();
	Tools_Mode_PhotonMapping::RelightEverything(false);
}


void Tools_PhotonMapping_LightSource_Object::UpdateIfChanged()
{
	if(m_bChanged || m_bChanged_NoRelightNeeded)
	{
		Tools_PhotonMapping_System::InvalidateEnvironmentIrradiance();
		SetLightingModified();
	}
	
	if (m_bChanged)
	{
		UpdateName();
		Tools_Mode_PhotonMapping::RelightEverything(false);
	}

	m_bChanged = false;
	m_bChanged_NoRelightNeeded = false;
}

void Tools_PhotonMapping_LightSource_Object::UpdateName()
{
	Tools_PhotonMapping_LightSource* pxLightSource = Tools_PhotonMapping_System::GetLightSource(m_iLightSourceHandle);

	//light has been assigned name by user?
	m_xName = pxLightSource->GetName();
	
	if(m_xName.IsBlank())
	{
		//generate light name from its ID
		char buffer[40];
		if(m_bParallelSource)
		{
			sprintf(buffer,"Infinite Light %d",(m_uID&TOOLS_PHOTONMAPPING_IDMASK));
		}
		else
		{
			sprintf(buffer,"Light %d",(m_uID&TOOLS_PHOTONMAPPING_IDMASK));
		}
		m_xName = buffer;
	}
}

bool Tools_PhotonMapping_LightSource_Object::IsSelectable()
{
	//lights that are members of templates can't be touched
	if(m_uTemplateGuid != ASURA_GUID_UNREGISTERED) return false;

	if(Tools_SaveAsura::GetFileType() == AsuraFile_Template)
	{
		//while editing template files ,don't allow selection of the test environment lights
		if(!(m_uID & TOOLS_PHOTONMAPPING_TEMPLATELIGHT)) return false;
	}

	return Tools_Object::IsSelectable();
}

Tools_PhotonMapping_LightSource_Object* Tools_PhotonMapping_LightSource_Object::GetLightObjectFromID(u_int uID)
{
	for(LIF<Tools_PhotonMapping_LightSource_Object*> xObjectLIF(&s_xObjectList);!xObjectLIF.done();xObjectLIF.next())
	{
		if(xObjectLIF()->m_uID == uID)
		{
			return xObjectLIF();
		}
	}
	return 0;
}

void Tools_PhotonMapping_LightSource_Object::Copy(const Tools_Object* pxObject)
{
	const Tools_PhotonMapping_LightSource_Object* pxFrom = (const Tools_PhotonMapping_LightSource_Object*) pxObject;

	//copy from the other tools object
	m_fPower = pxFrom->m_fPower;
	m_fInitialPower = pxFrom->m_fInitialPower;
	m_fAngle = pxFrom->m_fAngle;
	m_xColourPair = pxFrom->m_xColourPair;
	m_xInitialColour = pxFrom->m_xInitialColour;
	m_bParallelSource = pxFrom->m_bParallelSource;
	m_bBleachedSource = pxFrom->m_bBleachedSource;
	m_bShortRange = pxFrom->m_bShortRange;
	m_iAccuracy = pxFrom->m_iAccuracy;
	m_bUseRange = pxFrom->m_bUseRange;
	m_fInnerRange = pxFrom->m_fInnerRange;
	m_fOuterRange = pxFrom->m_fOuterRange;
	m_fReflectionChance = pxFrom->m_fReflectionChance;
	m_uLightSets = pxFrom->m_uLightSets;
	m_bColoursInverted = pxFrom->m_bColoursInverted;

	//update the changes in the asura light data
	m_bChanged = true;
	UpdateAsuraLight();
	UpdateIfChanged();

}

Tools_Object* Tools_PhotonMapping_LightSource_Object::CreateObjectOfThisType() const
{
	return CreateLightSource(m_bParallelSource);	
}


Tools_PhotonMapping_LightSource_Object* Tools_PhotonMapping_LightSource_Object::CreateLightSource(bool bParallel)
{
	Asura_Handle iHandle = Tools_PhotonMapping_System::CreateLightSource();

	if (iHandle == ASURA_INVALID_HANDLE)
	{
		return 0;
	}

	//generate an ID for the new light
	Tools_PhotonMapping_TemplateData* pxTemplateData = Tools_PhotonMapping_System::GetTemplateData(iHandle);
	pxTemplateData->GenerateID();

	if(Tools_SaveAsura::GetFileType() == AsuraFile_Template)
	{
		//lights created while editing template files need to be marked as such.
		pxTemplateData->m_uID |= TOOLS_PHOTONMAPPING_TEMPLATELIGHT;
	}
	if(Tools_Object_System::ShouldCreateNewObjectsInMissionFile() && Tools_SaveAsura::IsUsingMissionGlobalIllumination())
	{
		pxTemplateData->m_uID |= TOOLS_PHOTONMAPPING_MISSIONLIGHT;
	}


	Tools_PhotonMapping_LightSource* pxLightSource = Tools_PhotonMapping_System::GetLightSource(iHandle);

	const Asura_Vector_3& xPosition = *Asura_Camera::GetPosition();
	const Asura_Matrix_3x3& xOrientation = *Asura_Camera::GetOrientation();

	pxLightSource->SetPosition(xPosition);
	pxLightSource->SetOrientation(xOrientation);
	pxLightSource->SetParallel(bParallel);
	pxLightSource->SetLightSets(Tools_LightSets::GetLightSetsForNewObject(xPosition));

	Tools_PhotonMapping_LightSource_Object* pxObject = new Tools_PhotonMapping_LightSource_Object(iHandle);

	pxObject->SetLightingModified();

	return pxObject;
}

bool Tools_PhotonMapping_LightSource_Object::IsEnabled_IsNotParallel()
{
	if(!s_pxThis) return false;
	return !s_pxThis->m_bParallelSource;
}

bool Tools_PhotonMapping_LightSource_Object::IsEnabled_IsAngular()
{
	if(!s_pxThis) return false;
	if(s_pxThis->m_bParallelSource) return false;
	return s_pxThis->m_fAngle != 360.0f;
	
}

bool Tools_PhotonMapping_LightSource_Object::IsEnabled_UsesRange()
{
	if(!s_pxThis) return false;
	return !s_pxThis->m_bParallelSource && s_pxThis->m_bUseRange;
}

void Tools_PhotonMapping_LightSource_Object::UpdateInitialLightValues()
{
	for(LIF<Tools_PhotonMapping_LightSource_Object*> xObjectLIF(&s_xObjectList);!xObjectLIF.done();xObjectLIF.next())
	{
		Tools_PhotonMapping_LightSource_Object* pxLight = xObjectLIF();
		
		pxLight->m_fInitialPower = pxLight->m_fPower;
		pxLight->m_xInitialColour.x = pxLight->m_xColourPair.m_xRGB.m_fR;
		pxLight->m_xInitialColour.y = pxLight->m_xColourPair.m_xRGB.m_fG;
		pxLight->m_xInitialColour.z = pxLight->m_xColourPair.m_xRGB.m_fB;
	}
}

void Tools_PhotonMapping_LightSource_Object::ApplyLightScaleValues()
{
	for(LIF<Tools_PhotonMapping_LightSource_Object*> xObjectLIF(&s_xObjectList);!xObjectLIF.done();xObjectLIF.next())
	{
		Tools_PhotonMapping_LightSource_Object* pxLight = xObjectLIF();
	
		//determine if the scale should be applied to this light
		bool bApplyScale = true;

		if(Tools_Mode_PhotonMapping::s_bLightScale_SelectedOnly)
		{
			if(!pxLight->IsSelected() && !pxLight->IsGroupSelected())
			{
				bApplyScale = false;
			}
		}
		if(pxLight->m_bParallelSource && !Tools_Mode_PhotonMapping::s_bLightScale_ParallelLights)
		{
			bApplyScale = false;
		}
		if(!pxLight->m_bParallelSource && !Tools_Mode_PhotonMapping::s_bLightScale_StandardLights)
		{
			bApplyScale = false;
		}

		//determine the new colour and power values
		Asura_Vector_3 xColour = pxLight->m_xInitialColour;
		float fPower = pxLight->m_fInitialPower;

		if(bApplyScale)
		{
			fPower *= Tools_Mode_PhotonMapping::s_fLightScale_Power;

			if(fPower<0 && !pxLight->m_bColoursInverted)
			{
				//for negative lights do the scaling on invered values
				xColour.x = 255.0f - xColour.x;
				xColour.y = 255.0f - xColour.y;
				xColour.z = 255.0f - xColour.z;
			}
			
			
			xColour.x *= Tools_Mode_PhotonMapping::s_fLightScale_Red; 
			xColour.y *= Tools_Mode_PhotonMapping::s_fLightScale_Green; 
			xColour.z *= Tools_Mode_PhotonMapping::s_fLightScale_Blue; 

			if(fPower<0 && !pxLight->m_bColoursInverted)
			{
				//invert back
				xColour.x = 255.0f - xColour.x;
				xColour.y = 255.0f - xColour.y;
				xColour.z = 255.0f - xColour.z;
			}


			xColour.x = Asura_Maths::Min(xColour.x,255.0f);
			xColour.y = Asura_Maths::Min(xColour.y,255.0f);
			xColour.z = Asura_Maths::Min(xColour.z,255.0f);

		}

		//apply these values if different
		if(fPower != pxLight->m_fPower || 
			xColour.x != pxLight->m_xColourPair.m_xRGB.m_fR ||
			xColour.y != pxLight->m_xColourPair.m_xRGB.m_fG ||
			xColour.z != pxLight->m_xColourPair.m_xRGB.m_fB)
		{
			pxLight->m_fPower = fPower;
			pxLight->m_xColourPair.Initialise(xColour.x,xColour.y,xColour.z);
			pxLight->m_bChanged = true;
			pxLight->UpdateAsuraLight();
			pxLight->UpdateIfChanged();
		}
			 
	}
}

void Tools_PhotonMapping_LightSource_Object::Rename()
{
	if(!s_pxThis) return;

	//get the current name
	Tools_PhotonMapping_LightSource* pxLightSource = Tools_PhotonMapping_System::GetLightSource(s_pxThis->m_iLightSourceHandle);
	Asura_String xName = pxLightSource->GetName();

	//ask for a new one
	if(Tools_Object_System::AskForName(xName,"Rename Light",s_pxThis,true))
	{
		//update name in object and light data
		pxLightSource->SetName(xName.GetString());
		s_pxThis->UpdateName();
		s_pxThis->SetLightingModified();

		//update name on overlay
		Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_LIGHTPROPERTIES);
		s_pxThis->CreateLightPropertiesOverlay();
	}
}

bool Tools_PhotonMapping_LightSource_Object::GetParameter(Asura_Hash_ID uParamID,class Tools_Parameter* pxParam) const
{
	bool bColoursInverted = Tools_Mode_PhotonMapping::SelectedLightHasInvertedColour();

	if(uParamID>=g_uHash_LightSource_LightSet0 && uParamID<=g_uHash_LightSource_LightSetLast)
	{
		u_int uBit = 1 << (uParamID - g_uHash_LightSource_LightSet0);
		pxParam->SetBool((m_uLightSets & uBit) ? true : false);
	}
	else
	{
		switch(uParamID)
		{
			case g_uHash_LightSource_ShortRange :
				pxParam->SetBool(m_bShortRange);
				break;
			case g_uHash_LightSource_UseRange :
				pxParam->SetBool(m_bUseRange);
				break;
			case g_uHash_LightSource_Bleached :
				pxParam->SetBool(m_bBleachedSource);
				break;

			case g_uHash_LightSource_Accuracy :
				pxParam->SetInt(m_iAccuracy);
				break;

			case g_uHash_LightSource_Power :
				pxParam->SetFloat(m_fPower);
				break;

			case g_uHash_LightSource_InnerRange :
				pxParam->SetFloat(m_fInnerRange);
				break;

			case g_uHash_LightSource_OuterRange :
				pxParam->SetFloat(m_fOuterRange);
				break;

			case g_uHash_LightSource_Angle :
				pxParam->SetFloat(m_fAngle);
				break;

			case g_uHash_LightSource_Red :
				//To avoid getting colours messed up , only allow getting/setting of
				//the if the inverted state matches that used by the main selection.
				UpdateInvertedColourStatus();
				if(bColoursInverted != m_bColoursInverted) return false;

				pxParam->SetFloat(m_xColourPair.m_xRGB.m_fR);
				break;

			case g_uHash_LightSource_Green :
				UpdateInvertedColourStatus();
				if(bColoursInverted != m_bColoursInverted) return false;
				pxParam->SetFloat(m_xColourPair.m_xRGB.m_fG);
				break;

			case g_uHash_LightSource_Blue :
				UpdateInvertedColourStatus();
				if(bColoursInverted != m_bColoursInverted) return false;
				pxParam->SetFloat(m_xColourPair.m_xRGB.m_fB);
				break;

			case g_uHash_LightSource_Hue :
				UpdateInvertedColourStatus();
				if(bColoursInverted != m_bColoursInverted) return false;
				pxParam->SetFloat(m_xColourPair.m_xHSV.m_fH);
				break;

			case g_uHash_LightSource_Saturation :
				UpdateInvertedColourStatus();
				if(bColoursInverted != m_bColoursInverted) return false;
				pxParam->SetFloat(m_xColourPair.m_xHSV.m_fS);
				break;

			case g_uHash_LightSource_Luminance :
				UpdateInvertedColourStatus();
				if(bColoursInverted != m_bColoursInverted) return false;
				pxParam->SetFloat(m_xColourPair.m_xHSV.m_fV);
				break;

			case g_uHash_LightSource_ReflectionChance :
				pxParam->SetFloat(m_fReflectionChance);
				break;


			default :
				return false;
			
		}
	}
	return true;
}

bool Tools_PhotonMapping_LightSource_Object::SetParameter(Asura_Hash_ID uParamID,const class Tools_Parameter* pxParam)
{
	bool bColoursInverted = Tools_Mode_PhotonMapping::SelectedLightHasInvertedColour();

	if(uParamID>=g_uHash_LightSource_LightSet0 && uParamID<=g_uHash_LightSource_LightSetLast)
	{
		u_int uBit = 1 << (uParamID - g_uHash_LightSource_LightSet0);
		bool bSet = pxParam->GetBool();
		if(bSet)
			m_uLightSets |= uBit;
		else
			m_uLightSets &=~ uBit;

		//Need for relight depends on whether the light set being altered is the one being shown
		if(uBit & Tools_LightSets::GetCurrentLightSets())
			m_bChanged = true;
		else
			m_bChanged_NoRelightNeeded = true;

	}
	else
	{
		switch(uParamID)
		{
			case g_uHash_LightSource_ShortRange :
				m_bShortRange = pxParam->GetBool();
				m_bChanged_NoRelightNeeded = true;
				break;

			case g_uHash_LightSource_UseRange :
				m_bUseRange = pxParam->GetBool();
				m_bChanged_NoRelightNeeded = true;
				break;

			case g_uHash_LightSource_Bleached :
				m_bBleachedSource = pxParam->GetBool();
				m_bChanged = true;
				break;

			case g_uHash_LightSource_Accuracy :
				m_iAccuracy = pxParam->GetInt();
				m_bChanged_NoRelightNeeded =true;
				break;

			case g_uHash_LightSource_Power :
				m_fPower = pxParam->GetFloat();
				m_bChanged = true;
				break;

			case g_uHash_LightSource_InnerRange :
				m_fInnerRange = pxParam->GetFloat();
				m_fOuterRange = Asura_Maths::Max(m_fInnerRange,m_fOuterRange);
				m_bChanged_NoRelightNeeded =true;
				break;

			case g_uHash_LightSource_OuterRange :
				m_fOuterRange = pxParam->GetFloat();
				m_fInnerRange = Asura_Maths::Min(m_fInnerRange,m_fOuterRange);
				m_bChanged_NoRelightNeeded =true;
				break;

			case g_uHash_LightSource_Angle :
				m_fAngle = pxParam->GetFloat();
				m_bChanged_NoRelightNeeded = true;
				break;

			case g_uHash_LightSource_Red :
				//To avoid getting colours messed up , only allow getting/setting of
				//the if the inverted state matches that used by the main selection.
				UpdateInvertedColourStatus();
				if(bColoursInverted != m_bColoursInverted) return false;
				
				m_xColourPair.m_xRGB.m_fR = pxParam->GetFloat();
				m_xColourPair.Update();
				m_bChanged = true;
				break;

			case g_uHash_LightSource_Green :
				UpdateInvertedColourStatus();
				if(bColoursInverted != m_bColoursInverted) return false;

				m_xColourPair.m_xRGB.m_fG = pxParam->GetFloat();
				m_xColourPair.Update();
				m_bChanged = true;
				break;

			case g_uHash_LightSource_Blue :
				UpdateInvertedColourStatus();
				if(bColoursInverted != m_bColoursInverted) return false;

				m_xColourPair.m_xRGB.m_fB = pxParam->GetFloat();
				m_xColourPair.Update();
				m_bChanged = true;
				break;

			case g_uHash_LightSource_Hue :
				UpdateInvertedColourStatus();
				if(bColoursInverted != m_bColoursInverted) return false;

				m_xColourPair.m_xHSV.m_fH = pxParam->GetFloat();
				m_xColourPair.Update();
				m_bChanged = true;
				break;

			case g_uHash_LightSource_Saturation :
				UpdateInvertedColourStatus();
				if(bColoursInverted != m_bColoursInverted) return false;

				m_xColourPair.m_xHSV.m_fS = pxParam->GetFloat();
				m_xColourPair.Update();
				m_bChanged = true;
				break;

			case g_uHash_LightSource_Luminance :
				UpdateInvertedColourStatus();
				if(bColoursInverted != m_bColoursInverted) return false;

				m_xColourPair.m_xHSV.m_fV = pxParam->GetFloat();
				m_xColourPair.Update();
				m_bChanged = true;
				break;

			case g_uHash_LightSource_ReflectionChance :
				m_fReflectionChance = pxParam->GetFloat();
				m_bChanged_NoRelightNeeded = true;
				break;

			default :
				return false;		
		}
	}

	UpdateAsuraLight();
	UpdateIfChanged();	

	return true;
}


void Tools_PhotonMapping_LightSource_Object::SetLightingModified()
{
	Tools_PhotonMapping_TemplateData* pxTemplateData = Tools_PhotonMapping_System::GetTemplateData(m_iLightSourceHandle);
	Asura_Assert(pxTemplateData,"");

	if(pxTemplateData->IsMissionLight())
	{
		Tools_SaveAsura::SetMissionGlobalIlluminationModified();
	}
	else
	{
		Tools_SaveAsura::SetGlobalIlluminationModified();
	}
}

void Tools_PhotonMapping_LightSource_Object::PrintInfo(float& x, float& y)
{
	Tools_Object::PrintInfo(x,y);

	if(Tools_SaveAsura::IsEditingMission())
	{
		Tools_PhotonMapping_TemplateData* pxTemplateData = Tools_PhotonMapping_System::GetTemplateData(m_iLightSourceHandle);
		Asura_Assert(pxTemplateData,"");

		Asura_Text_Output::Print(x,y++,0xffffffff,0,"Mission : %s", pxTemplateData->IsMissionLight() ? "Yes" : "No");
	}

}
