//
// Tools_LightSets.cpp
//
// Created by Richard Rice - 24/03/2006
// 
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"
#include "Tools_LightSets.h"

#include "Camera/Asura_Camera.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Overlay.h"
#include "Interface/Asura_Overlay_System.h"
#include "Light/Asura_GI_Solution.h"
#include "Mesh/Asura_Env.h"
#include "Mesh/Asura_Env_System.h"
#include "SpecialFX/Asura_StaticCorona_System.h"

#include "Tools_Overlays.h"
#include "Lighting/Tools_Mode_PhotonMapping.h"
#include "Lighting/Tools_GlobalIllumination_EntitySamples.h"
#include "Lighting/Tools_PhotonMapping_System.h"
#include "Load&Save/Tools_ModeDataFile.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "MeshEdit/Tools_Mode_MeshEdit.h"
#include "Misc/Tools_IniFile.h"


u_int Tools_LightSets::s_uNumGroups = 0;
Tools_LightSetGroup Tools_LightSets::s_axGroups[TOOLS_MAX_LIGHT_SET_GROUPS];

Asura_String Tools_LightSets::s_axNames[TOOLS_MAX_LIGHT_SETS];
u_int Tools_LightSets::s_uNumLightSets = 0;
u_int Tools_LightSets::s_uCurrentLightSets = 0;
u_int Tools_LightSets::s_uUsedLightSets = 1;
u_int Tools_LightSets::s_uUsedLightSetsForOverlay = 1;
u_int Tools_LightSets::s_uValidLightSets = 1;
bool Tools_LightSets::s_bUsingLightSets = false;

bool Tools_LightSets::s_bHideModulesInOtherSets = false;
bool Tools_LightSets::s_bHideObjectsInOtherSets = false;

bool Tools_LightSets::s_bDefaultSet_Current = false;
bool Tools_LightSets::s_bDefaultSet_All = true;
bool Tools_LightSets::s_bDefaultSet_NearModule = false;
bool Tools_LightSets::s_bDefaultSet_SelectedSets = false;
u_int Tools_LightSets::s_uDefaultLightSets = 0;
bool Tools_LightSets::s_bOverlayShouldBeOpen = false;
u_int Tools_LightSets::s_uOverlayMinimised = 0;

Tools_LightSets* Tools_LightSets::s_pxThis = 0;


Asura_Vector_2 Tools_LightSets::s_xOverlayPosition(15.0f,10.0f);

Tools_LightSets::Tools_LightSets()
{
	Asura_Assert(!s_pxThis,"Tools_LightSets already allocated");
	s_pxThis = this;
}

Tools_LightSets::~Tools_LightSets()
{
	Asura_Assert(s_pxThis,"Tools_LightSets not allocated");
	s_pxThis = 0;
}

void Tools_LightSets::Initialise()
{
	//Read light set names from ini file
	Tools_IniFile xIni("ToolsIniFiles\\LightSets.ini");

	if(xIni.LoadIniFile())
	{
		for(int iSection=-1;iSection<xIni.GetNumberOfSections();iSection++)
		{
			//for the -1'th section just take the text before any sections are defined (to support existing files)
			if(iSection>=0)
			{
				if(!xIni.GetIniTextSectionByIndex(iSection)) continue;
			}

			u_int uSetsInGroup = 0;
			while(xIni.GetNextIniTextLine())
			{
				int iSet;
				Asura_String xName;
				
				if(xIni.GetNextOnLine(iSet) && xIni.GetNextOnLine(xName))
				{
					if(xName.IsBlank()) continue;

					if(iSet<0 || iSet>=TOOLS_MAX_LIGHT_SETS)
					{
						//invalid light index
						Asura_Assertf(0,("Light set %s has an invalid index (%d).\nIt should be between 0 and 31",xName.GetString(),iSet));
						continue;
					}
					//index already used?
					if(!s_axNames[iSet].IsBlank())
					{
						Asura_Assertf(0,("Light sets %s and %s share the same index",s_axNames[iSet].GetString(),xName.GetString()));
						continue;
					}

					s_axNames[iSet] = xName;
					s_uValidLightSets |= (1<<iSet);
					uSetsInGroup |= (1<<iSet);

					s_uNumLightSets++;
					s_bUsingLightSets = true;
				}
			}
			if(uSetsInGroup)
			{
				//setup the next group
				Asura_Assert(s_uNumGroups<TOOLS_MAX_LIGHT_SET_GROUPS,"Too many light set groups found. Need to increase TOOLS_MAX_LIGHT_SET_GROUPS");
				if(s_uNumGroups<TOOLS_MAX_LIGHT_SET_GROUPS)
				{
					s_axGroups[s_uNumGroups].SetLightSets(uSetsInGroup);
					if(iSection >= 0)
					{
						s_axGroups[s_uNumGroups].SetName(xIni.GetCurrentSectionName());
					}
					
					bool bIncludeInExportUI = true;
					if(xIni.GetBoolInCurrentSection("Export UI", bIncludeInExportUI))
					{
						s_axGroups[s_uNumGroups].SetIncludeInExportUI(bIncludeInExportUI);
					}

					s_uNumGroups++;
				}
			}
		}
	}

	//Make light set 0 available even if file didn't exist
	if(s_axNames[0].IsBlank())
	{
		s_axNames[0] = "Default";
		s_uNumLightSets++;

		//add Default to the first group as well
		s_axGroups[0].SetLightSets(s_axGroups[0].GetLightSets()|1);
		if(!s_uNumGroups) s_uNumGroups = 1;
	}

	SetUsedLightSets(s_uUsedLightSets);
}

void Tools_LightSets::Shutdown()
{
	for(int i=0;i<TOOLS_MAX_LIGHT_SETS;i++)
	{
		s_axNames[i].MakeNull();
	}
	s_uNumLightSets = 0;
}

void Tools_LightSets::ToolsSystem_Initialise()
{
	TransferModeData(TOOLSMODEDATAFILE_LOAD);
	if(s_bOverlayShouldBeOpen) CreateOverlay();
}

void Tools_LightSets::ToolsSystem_Shutdown()
{
	s_bOverlayShouldBeOpen = OverlayIsOn();
	TransferModeData(TOOLSMODEDATAFILE_SAVE);
	CloseOverlay();
}

void Tools_LightSets::TransferModeData(int iLoadOrSave)
{

	//open the file
	Tools_ModeDataFile xFile("LightSets.dat",iLoadOrSave);

	//tranfer the data
	//(none of these should be removed , and new data should go on the end, otherwise
	//the files will get screwed up)
	if( false == xFile.GetError())
	{
		xFile.Transfer(s_xOverlayPosition);

		xFile.Transfer(s_bHideModulesInOtherSets);
		xFile.Transfer(s_bDefaultSet_Current);
		xFile.Transfer(s_bDefaultSet_All);
		xFile.Transfer(s_bDefaultSet_NearModule);
		xFile.Transfer(s_bDefaultSet_SelectedSets);
		xFile.Transfer(s_uDefaultLightSets);
		xFile.Transfer(s_bOverlayShouldBeOpen);
		xFile.Transfer(s_uOverlayMinimised);
		xFile.Transfer(s_bHideObjectsInOtherSets);
	}
}



void Tools_LightSets::SetCurrentLightSets(u_int uSets,bool bRelight)
{
	//make sure all the light sets are valid
	u_int uNumSetsUsed = 0;
	for(u_int uSetIndex=0;uSetIndex<TOOLS_MAX_LIGHT_SETS;uSetIndex++)
	{
		if(uSets & (1<<uSetIndex))
		{
			if(!IsValidLightSetIndex(uSetIndex))
			{
				Asura_Assert(0,"Invalid light set value");
				return;
			}
			uNumSetsUsed++;
		}
	}
	
	//Make sure exactly one set from each active group is being used
	bool bOkay = (uNumSetsUsed == s_uNumGroups);
	for(u_int i=0;i<s_uNumGroups;i++)
	{
		if(!(uSets & s_axGroups[i].GetLightSets()))
		{
			bOkay = false;
		}
	}

	if(!bOkay)
	{
		Asura_Assert(0,"Invalid combination of light sets being used");
		return;
	}
	

	if(s_uCurrentLightSets != uSets)
	{
		s_uCurrentLightSets = uSets;

		//Change handler for GI solution
		Asura_GI_Solution_Handler* pxHandler = Asura_GI_Solution::GetHandler(uSets);
		if(!pxHandler) pxHandler = Asura_GI_Solution::CreateHandler(uSets);
		Asura_GI_Solution::SetCurrentHandler(pxHandler);

		if(bRelight)
		{
			//Mesh prelighting will need to be recalculated.
			//(NB -  Photon mapping mode option to stop recalculations can prevent this...but that should only happen while in that mode).
			Tools_Mode_PhotonMapping::RelightEverything(false);
		}

		Tools_GlobalIllumination_EntitySamples::LightingMayHaveChanged();
		Tools_PhotonMapping_System::InvalidateEnvironmentIrradiance();

		//update coronas , to make sure the correct ones are visible
		Asura_StaticCorona_System::UpdateAllCoronas();
	}
}

void Tools_LightSets::SetUsedLightSets(u_int uSets)
{
	s_uUsedLightSets = uSets & s_uValidLightSets;

	//At least one light set from each group must stay in use
	for(u_int i=0;i<s_uNumGroups;i++)
	{
		if(!(s_uUsedLightSets & s_axGroups[i].GetLightSets()))
		{
			s_uUsedLightSets |= s_axGroups[i].GetValidSetByIndex(0);
		}
	}
	s_uUsedLightSetsForOverlay = s_uUsedLightSets;

	//If the current light set isn't used anymore , then switch to one that is used
	u_int uNewLightSets = s_uCurrentLightSets;
	for(u_int i=0;i<s_uNumGroups;i++)
	{
		s_axGroups[i].UpdateUsedLightSets();
		s_axGroups[i].EnsureExactlyOneUsedLightSetFromGroup(uNewLightSets);
	}


	if(uNewLightSets != s_uCurrentLightSets)
	{
		SetCurrentLightSets(uNewLightSets);
	}
}

void Tools_LightSets::ToggleOverlay()
{
	if(OverlayIsOn())
	{
		CloseOverlay();
	}
	else
	{
		CreateOverlay();		
	}
}

bool Tools_LightSets::OverlayIsOn()
{
	return Asura_Overlay_System::DoesOverlayExist(TOOLS_OVERLAY_LIGHTSETS);
}

void Tools_LightSets::CloseOverlay()
{
	Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_LIGHTSETS);
}

void Tools_LightSets::CreateOverlay()
{
	Asura_Overlay* pxOverlay = new Asura_Overlay("Light Sets",false,&s_xOverlayPosition,&s_uOverlayMinimised);	

	Asura_Dialog* pxDialog;
	Asura_Vector_2 xOffset = Asura_Vector_2(1.0f,3.0f);

	//start of tabs
	Asura_Tabs_Dialog* pxTabsDialog = new Asura_Tabs_Dialog(xOffset,0,0);
	pxOverlay->AddDialog(pxTabsDialog);
	xOffset.y += Asura_Dialog::LargeSpacing();
	pxOverlay->SetCurrentParentDialog(pxTabsDialog);

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

	//Main tab
	pxTabsDialog->AddTab("Main");
	xOffset.y = fTabsStartY;
	

	for(u_int i=0; i<s_uNumGroups; i++)
	{
		pxDialog = new Tools_LightSet_Dialog(xOffset,i,0,0,Tools_LightSets::GetGroupName(i));
		pxDialog->SetHelpText("Sets the current light set to view.\nOnly light sets that have been marked as used can be selected.");
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();
	}
	xOffset.y += Asura_Dialog::LargeSpacing() - Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("Hide Modules In Other Sets",xOffset,&s_bHideModulesInOtherSets);
	pxDialog->SetHelpText("Hides any modules that don't belong to the current light set.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("Hide Objects In Other Sets",xOffset,&s_bHideObjectsInOtherSets);
	pxDialog->SetHelpText("Hides any objects for entities/lights etc. that don't belong to the current light set.");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();



	pxDialog = new Asura_Dialog("Default set(s) :",xOffset);
	pxDialog->SetHelpText("The default set(s) to apply to newly created objects");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("Current Set",xOffset,&s_bDefaultSet_Current);
	pxDialog->SetHelpText("New objects will be assigned to the current light.");
	pxDialog->SetRadioButtonGroup(1,true);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("All Sets",xOffset,&s_bDefaultSet_All);
	pxDialog->SetHelpText("New objects will be assigned to all light sets");
	pxDialog->SetRadioButtonGroup(1,true);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("Nearby Module",xOffset,&s_bDefaultSet_NearModule);
	pxDialog->SetHelpText("New objects will be assigned the light sets used by a nearby module.");
	pxDialog->SetRadioButtonGroup(1,true);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("Selected Sets",xOffset,&s_bDefaultSet_SelectedSets);
	pxDialog->SetHelpText("New objects will be assigned the light sets selected below.");
	pxDialog->SetRadioButtonGroup(1,true);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	xOffset.x += 1.0f;
	for(u_int i=0;i<TOOLS_MAX_LIGHT_SETS;i++)
	{
		const char* szSetName = Tools_LightSets::GetLightSetNameByIndex(i);
		if(!szSetName) continue;

		pxDialog = new Asura_FlagTickbox_Dialog(szSetName,xOffset,&s_uDefaultLightSets,1<<i);
		pxDialog->SetHelpText("New objects will be assigned to this light set.");
		pxDialog->SetIsEnabledFunction(IsDefaultingToSelectedSets);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();
	}
	xOffset.x -= 1.0f;


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

	//Used light set tab
	pxTabsDialog->AddTab("Used Sets");
	xOffset.y = fTabsStartY;

	for(u_int i=0;i<TOOLS_MAX_LIGHT_SETS;i++)
	{
		const char* szSetName = Tools_LightSets::GetLightSetNameByIndex(i);
		if(!szSetName) continue;

		pxDialog = new Asura_FlagTickbox_Dialog(szSetName,xOffset,&s_uUsedLightSetsForOverlay,1<<i);
		pxDialog->SetHelpText("Indicates whether this light set should be used by this level.");
		((Asura_FlagTickbox_Dialog*)pxDialog)->SetClickedFunction(UsedLightSetsChanged);
		pxOverlay->AddDialog(pxDialog);
		xOffset.y+=Asura_Dialog::DefaultSpacing();
	}

	pxDialog = new Asura_Dialog("(Saved to .gi file)",xOffset);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

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

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

	if(s_pxThis)
	{
		s_pxThis->AddOverlayControls(pxOverlay, xOffset);
	}


	pxOverlay->m_uID = TOOLS_OVERLAY_LIGHTSETS;
	Asura_Overlay_System::SubmitNewOverlay(pxOverlay);

}

void Tools_LightSets::UsedLightSetsChanged()
{
	SetUsedLightSets(s_uUsedLightSetsForOverlay);

	Tools_SaveAsura::SetGlobalIlluminationModified();
}

u_int Tools_LightSets::GetLightSetsForNewObject()
{
	return GetLightSetsForNewObject(*Asura_Camera::GetPosition());
}

u_int Tools_LightSets::GetLightSetsForNewObject(const Asura_Vector_3& xPosition)
{
	int iModuleIndex = -1;
	if(s_bDefaultSet_NearModule)
	{
		///Use the same rules for picking modules as used when mering modules into the environment
		iModuleIndex = Tools_Mode_MeshEdit::GetModuleForObjectMerge(xPosition, xPosition);
	}
	return GetLightSetsForNewObject(iModuleIndex);
}

u_int Tools_LightSets::GetLightSetsForNewObject(int iModuleIndex)
{
	if(!s_bUsingLightSets)
	{
		//Light sets not being used , so assign everything to all sets
		return 0xffffffff;
	}

	if(s_bDefaultSet_Current)
	{
		return GetCurrentLightSets();
	}
	if(s_bDefaultSet_All)
	{
		return 0xffffffff;
	}
	if(s_bDefaultSet_SelectedSets)
	{
		return s_uDefaultLightSets;
	}
	if(s_bDefaultSet_NearModule && iModuleIndex>=0)
	{
		const Asura_Environment* pxEnv = Asura_Environment_System::GetEnvironment();
		Asura_Assert(pxEnv, "");
		return pxEnv->GetLightSets(iModuleIndex);
	}

	return 0xffffffff;
}

bool Tools_LightSets::IsModuleInCurrentLightSet(int iModuleIndex)
{
	const Asura_Environment* pxEnv = Asura_Environment_System::GetEnvironment();
	Asura_Assert(pxEnv, "");

	return IsMemberOfCurrentLightSets(pxEnv->GetLightSets(iModuleIndex));	
}

u_int Tools_LightSets::GetNumUsedCombinations()
{
	u_int uResult = 1;
	for(u_int i=0;i<s_uNumGroups;i++)
	{
		uResult *= s_axGroups[i].GetNumUsedSets();
	}
	return uResult;
}

bool Tools_LightSets::IsUsedLightSetCombination(u_int uSets)
{
	//Check to make sure uSets contains a used set from each group
	for(u_int i=0;i<s_uNumGroups;i++)
	{
		if(!(uSets & s_axGroups[i].GetUsedLightSets())) return false;
	}
	//make sure just one bit per set is used
	u_int uBitsSet = 0;
	for(u_int i=0;i<TOOLS_MAX_LIGHT_SETS;i++)
	{
		if(uSets & (1<<i)) uBitsSet++;
	}
	return uBitsSet == s_uNumGroups;
}

const Tools_LightSetGroup* Tools_LightSets::GetGroup(u_int uIndex)
{
	Asura_Assert(uIndex<s_uNumGroups,"");
	return &s_axGroups[uIndex];
}

void Tools_LightSets::GetCurrentLightSetNames(Asura_String& xOutput)
{
	char acBuffer[200];
	char* pcPos = acBuffer;
	u_int uSpaceLeft = sizeof(acBuffer);

	bool bFirstName = true;
	for(int i=0;i<TOOLS_MAX_LIGHT_SETS;i++)
	{
		if(s_axNames[i].IsBlank()) continue;
		
		if(s_uCurrentLightSets & (1<<i))
		{
			if(bFirstName)
			{
				Asura_PrintfToBfr(pcPos,"%s",uSpaceLeft,true,s_axNames[i].GetString());
				bFirstName = false;
			}
			else
			{
				Asura_PrintfToBfr(pcPos," - %s",uSpaceLeft,true,s_axNames[i].GetString());
			}
			u_int uLength = strlen(pcPos);
			uSpaceLeft -= uLength;
			pcPos += uLength;
		}
	}

	xOutput = acBuffer;
}

const char* Tools_LightSets::GetGroupName(u_int uIndex)
{
	Asura_Assert(uIndex<s_uNumGroups,"");
	return s_axGroups[uIndex].GetName();
}

bool Tools_LightSets::ShouldIncludeLightSetIndexInExportUI(u_int uIndex)
{
	u_int uValue = 1<<uIndex;

	//find the group that contains this light set  , and see if it should be included as an export option
	for(u_int i=0;i<s_uNumGroups;i++)
	{
		if(uValue & s_axGroups[i].GetLightSets())
		{
			return s_axGroups[i].GetIncludeInExportUI();
		}
	}

	return false;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------**
** Tools_LightSetGroup **
**---------------------*/

Tools_LightSetGroup::Tools_LightSetGroup()
{
	m_uSets = 0;
	m_uUsedSets = 0;
	m_uNumValidSets = 0;
	m_uNumUsedSets = 0;
	m_bIncludeInExportUI = true;
}

void Tools_LightSetGroup::SetLightSets(u_int uSets)
{
	m_uSets = uSets;

	//determine how many bits are set
	m_uNumValidSets = 0;
	for(u_int i=0;i<TOOLS_MAX_LIGHT_SETS;i++)
	{
		u_int uBit = 1<<i;
		if(uSets & (uBit))
		{
			m_auValidSets[m_uNumValidSets] = uBit;
			m_uNumValidSets++;
		}
	}
}

void Tools_LightSetGroup::UpdateUsedLightSets()
{
	m_uUsedSets = m_uSets & Tools_LightSets::GetUsedLightSets();

	m_uNumUsedSets = 0;
	for(u_int i=0;i<TOOLS_MAX_LIGHT_SETS;i++)
	{
		u_int uBit = 1<<i;
		if(m_uUsedSets & (uBit))
		{
			m_auUsedSets[m_uNumUsedSets] = uBit;
			m_uNumUsedSets++;
		}
	}
}


void Tools_LightSetGroup::EnsureExactlyOneUsedLightSetFromGroup(u_int& uSets)  const
{
	//find the used light sets in this group
	u_int uUsedSets = m_uSets & Tools_LightSets::GetUsedLightSets();
	if(!uUsedSets)
	{
		Asura_Assert(0,"No light sets in this group are in use");
	}

	//get the overlap in the used sets and those passed in
	u_int uSetOverlap = uSets & uUsedSets;
	
	//remove all sets from this group from the passed in sets
	uSets &=~ m_uSets;

	//And find one bit from the overlap , adding it back in
	//If there are no such bits , then pick the first used one
	if(!uSetOverlap) uSetOverlap = uUsedSets;
	for(u_int i=0;i<TOOLS_MAX_LIGHT_SETS;i++)
	{
		if(uSetOverlap & (1<<i))
		{
			uSets |= (1<<i);
			break;
		}
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*----------------------------**
** Tools_UsedLightSetIterator **
**----------------------------*/

Tools_UsedLightSetIterator::Tools_UsedLightSetIterator(u_int uStyle /*= TOOLS_LIGHTSETITERATOR_DEFAULT*/)
{
	m_uStyle = uStyle;
	m_uMask = 0xffffffff;
	
	//remove the light sets that aren't being used in this iteration from the mask
	for(int i=Tools_LightSets::s_uNumGroups-1;i>=0;i--)
	{
		const Tools_LightSetGroup* pxGroup = Tools_LightSets::GetGroup(i);

		bool bIncludeInIteration = true;
		if(m_uStyle == TOOLS_LIGHTSETITERATOR_EXPORT && !pxGroup->GetIncludeInExportUI())
		{
			bIncludeInIteration = false;
		}
		if(!bIncludeInIteration)
		{
			m_uMask &=~ pxGroup->GetLightSets();
		}
	}


	Restart();
}

void Tools_UsedLightSetIterator::Next()
{
	m_iIndex++;
	m_uCurrentValue = 0;

	//determine the index within each group based on the overall index
	int iRemainder = m_iIndex;
	for(int i=Tools_LightSets::s_uNumGroups-1;i>=0;i--)
	{
		const Tools_LightSetGroup* pxGroup = Tools_LightSets::GetGroup(i);

		if(m_uMask & pxGroup->GetLightSets())
		{
			int iNumUsedSets = pxGroup->GetNumUsedSets();
			if(!iNumUsedSets)
			{
				Asura_Assert(0,"Light set group doesn't have any used light sets");
				break;
			}

			int iIndexWithinGroup = iRemainder%iNumUsedSets;
			iRemainder = iRemainder/iNumUsedSets;

			m_uCurrentValue |= pxGroup->GetUsedSetByIndex(iIndexWithinGroup);
		}
	}

	if(iRemainder)
	{
		//finished iteration
		m_uCurrentValue = 0;
		m_bDone = true;
	}

}

void Tools_UsedLightSetIterator::Restart()
{
	m_iIndex = -1;
	m_bDone = false;
	Next();
}

u_int Tools_UsedLightSetIterator::GetCurrent() const
{
	//Use the current light sets for any groups that aren't being covered by the iteration
	return (m_uCurrentValue & m_uMask) | (Tools_LightSets::s_uCurrentLightSets &~ m_uMask);
}



///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*-----------------------**
** Tools_LightSet_Dialog **
**-----------------------*/

Tools_LightSet_Dialog::Tools_LightSet_Dialog(const Asura_Vector_2& xOffset,u_int uGroup,u_int* puLightSets,bool* pbChangedFlag,const char* szName)
:Asura_DropDown_List_Dialog(xOffset)
{
	m_uGroup = uGroup;

	m_puLightSets = puLightSets;
	m_pbChangedFlag = pbChangedFlag;

	m_bChanged = 0;
	m_uLightSetsCopy = puLightSets ? *puLightSets : Tools_LightSets::GetCurrentLightSets();

	UpdateNameList(false);
	UpdateIndexFromSet();

	Initialise(xOffset,m_aszNameList,&m_iSelectedIndex,8,m_iNameListSize,NULL,&m_bChanged,szName);
}

void Tools_LightSet_Dialog::PerFrameUpdate()
{
	u_int uExternalLightSetValue = m_puLightSets ? *m_puLightSets : Tools_LightSets::GetCurrentLightSets();

	//Check for changes to the list of used light sets
	if(m_uUsedLightSets != Tools_LightSets::GetUsedLightSets())
	{
		UpdateNameList(true);
		UpdateIndexFromSet();
	}

	//Check for external changes to selection
	if(m_uLightSetsCopy != uExternalLightSetValue)
	{
		m_uLightSetsCopy = uExternalLightSetValue;
		UpdateIndexFromSet();

	}

	Asura_DropDown_List_Dialog::PerFrameUpdate();
}

void Tools_LightSet_Dialog::Update(const Asura_Vector_2& xOrigin)
{
	Asura_DropDown_List_Dialog::Update(xOrigin);

	//check for changes to the selection
	if(m_bChanged && !IsDroppedDown())
	{
		if(m_pbChangedFlag) *m_pbChangedFlag = true;
		m_bChanged = false;

		//Determine light set from index
		const Tools_LightSetGroup* pxGroup = Tools_LightSets::GetGroup(m_uGroup);

		m_uLightSetsCopy &=~ pxGroup->GetLightSets();
		m_uLightSetsCopy |= m_auLightSetValue[m_iSelectedIndex];
		if(m_puLightSets)
		{
			*m_puLightSets = m_uLightSetsCopy;
		}
		else
		{
			Tools_LightSets::SetCurrentLightSets(m_uLightSetsCopy);
		}
	}
}



void Tools_LightSet_Dialog::UpdateIndexFromSet()
{
	u_int uExternalLightSetValue = m_puLightSets ? *m_puLightSets : Tools_LightSets::GetCurrentLightSets();

	const Tools_LightSetGroup* pxGroup = Tools_LightSets::GetGroup(m_uGroup);

	if(!pxGroup->GetUsedLightSets() & uExternalLightSetValue)
	{
		//Not expecting to get an invalid light set if taken directly from the light set system
		Asura_Assert(m_puLightSets,"");

		//Light set no longer used.. pick the first one that is
		m_iSelectedIndex = 0;

		uExternalLightSetValue &=~ pxGroup->GetLightSets();
		uExternalLightSetValue |= m_auLightSetValue[0];

		if(m_puLightSets) *m_puLightSets = uExternalLightSetValue;
		m_uLightSetsCopy = uExternalLightSetValue;
	}
	else
	{
		m_iSelectedIndex = 0;
		for(int i=0;i<m_iNameListSize;i++)
		{
			if(m_auLightSetValue[i] & uExternalLightSetValue)
			{
				m_iSelectedIndex = i;
				break;
			}
		}
	}
}

void Tools_LightSet_Dialog::UpdateNameList(bool bUpdateDialog)
{
	const Tools_LightSetGroup* pxGroup = Tools_LightSets::GetGroup(m_uGroup);

	m_iNameListSize = 0;
	for(u_int i=0;i<pxGroup->GetNumUsedSets();i++)
	{
		u_int uSet = pxGroup->GetUsedSetByIndex(i);
		m_aszNameList[m_iNameListSize] = Tools_LightSets::GetLightSetNameByValue(uSet);
		m_auLightSetValue[m_iNameListSize] = uSet;
		m_iNameListSize++;
	}
	Asura_Assert(m_iNameListSize,"Expecting at least one entry in list");
	m_uUsedLightSets = Tools_LightSets::GetUsedLightSets();

	if(bUpdateDialog)
	{
		ReplaceStrings(m_iNameListSize,m_aszNameList);
	}
}
