//
// Tools_Zone_Editor.cpp
//
// Created by Richard Rice - 25/01/05
// 
// Copyright (C) 2005 Rebellion, All Rights Reserved.
//

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


#include "Camera/Asura_Camera.h"
#include "Interface/Asura_Overlay.h"
#include "Interface/Asura_Overlay_System.h"
#include "Interface/Asura_Dialog.h"
#include "Rendering/Asura_Text_Output.h"
#include "Zone/Asura_Zone.h"
#include "Zone/Asura_Zone_Controller.h"

#include "Load&Save/Tools_ModeDataFile.h"
#include "Misc/Tools_Modal_Overlay.h"
#include "Misc/Tools_Render.h"
#include "Zone/Tools_Zone_VolumeEdit_Controller.h"

Tools_Zone_Editor* Tools_Zone_Editor::s_pxThis = 0;

Tools_Zone_Editor::Tools_Zone_Editor(Asura_Zone_Controller* pxZoneController)
:m_xOverlayPos(3.0f,8.0f)
,m_xZoneOverlayPos(30.0f,8.0f)
{
	m_pxZoneController = pxZoneController;
	m_iOverlayID = (int) this;
	m_iZoneOverlayID = m_iOverlayID+1;
	
	m_iNumZoneNames = 0;
	m_ppszZoneNames = 0;

	m_iSelectedZone = 0;
	m_bSelectedZoneChanged = false;
	m_pxSelectedZone = 0;
	m_pxVolumeEdit = 0;

	m_bChanged = false;
	m_bActive = false;
	m_bShowVolumes = false;

	m_bOverlayMinimised = false;
	m_bZoneOverlayMinimised = false;

}

Tools_Zone_Editor::~Tools_Zone_Editor()
{
	EndEditing();
}

void Tools_Zone_Editor::Update()
{
	if(!m_bActive) return;

	if(m_bSelectedZoneChanged)
	{
		HandleSelectionChange();
		m_bSelectedZoneChanged = false;
	}

	if(m_bChanged)
	{
		m_pxZoneController->InvalidateVolumeTree();//probably overkill to do this for every change... but simpler.
		SetModified();
		m_bChanged = false;
	}
}

void Tools_Zone_Editor::RenderWorld()
{
	if(!m_bActive) return;

	if(m_bShowVolumes)
	{
		int auColours[31] =
		{
			0x0000ff,0xffff00,0xff00ff,0x00ffff,
			0xff7f00,0xff007f,0x7fff00,0x00ff7f,
			0x7f00ff,0x007fff,0xff7f7f,0x7fff7f,
			0x7f7fff,0xff9f5f,0xff5f9f,0x9fff5f,
			0x5fff9f,0x9f5fff,0x5f9fff,0x7f0000,
			0x007f00,0x00007f,0x7f7f00,0x7f007f,
			0x007f7f,0x3f0000,0x003f00,0x00003f,
			0x7f7f7f
		};

		for(int i=0;i<m_pxZoneController->m_iNumberOfZones;i++)
		{
			if(i == m_iSelectedZone) continue;

			Asura_Zone* pxZone = m_pxZoneController->m_ppxZones[i];
			u_int uColour = auColours[i%31] | (0x8f000000);

			for(int j=0;j<pxZone->m_iNumberOfVolumes;j++)
			{
				Asura_Bounding_Box xBox = pxZone->m_pxVolumes[j].m_xBox;				
				Asura_Vector_3 xPosition = xBox.CalculateCentre();
				xBox.Displace(-xPosition);

				Asura_Matrix_3x3 xMat;
				pxZone->m_pxVolumes[j].m_xOrient.ToMatrix(xMat);

				Tools_Render::RenderCube(xBox,xPosition,xMat,uColour,false);
			}

		}
	}
}

void Tools_Zone_Editor::RenderUI()
{
	if(!m_bActive) return;

	//Show the zone that will be selected from the current position
	const Asura_Zone* pxCurrentZone = m_pxZoneController->GetZoneFromPosition(*Asura_Camera::GetPosition());

	Asura_Vector_2 xPrintPos(1.0f,10.0f);
	if(pxCurrentZone)
	{
	 	Asura_Text_Output::Print(xPrintPos.x,xPrintPos.y++,0xffffffff,0,"Cam Zone : %s",pxCurrentZone->m_xName.GetString());	
	}


	//And any other zones that the camera is within
	for(int i=0;i<m_pxZoneController->m_iNumberOfZones;i++)
	{
		const Asura_Zone* pxZone = m_pxZoneController->m_ppxZones[i];
		if(pxZone == pxCurrentZone) continue;

		if(pxZone->Contains(*Asura_Camera::GetPosition()))
		{
			Asura_Text_Output::Print(xPrintPos.x,xPrintPos.y++,0xffffffff,0,"Also In  : %s",pxZone->m_xName.GetString());	
		}
	}
}


void Tools_Zone_Editor::StartEditing()
{
	m_bActive = true;
	CreateOverlay();
	HandleSelectionChange();
}

void Tools_Zone_Editor::EndEditing()
{
	DeleteOverlay();
	DeleteZoneOverlay();
	delete m_pxVolumeEdit;
	m_pxVolumeEdit = 0;
	
	delete [] m_ppszZoneNames;
	m_ppszZoneNames = 0;

	m_bActive = false;
}

void Tools_Zone_Editor::CreateOverlay()
{
	//delete old overlay if it exists
	DeleteOverlay();

	Asura_Assert(!s_pxThis,"Not expecting another zone edit overlay to be open");
	s_pxThis = this;


	Asura_Overlay* pxOverlay = new Asura_Overlay(GetTitle(),false,&m_xOverlayPos,&m_bOverlayMinimised);
	Asura_Vector_2 xOffset = Asura_Vector_2(1.0f,3.0f);

	BuildZoneNameArray();
	m_iSelectedZone = Asura_Maths::ClampToRange(m_iSelectedZone,0,m_iNumZoneNames-1);

	AddOverlayControls(pxOverlay,xOffset);

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

}

void Tools_Zone_Editor::DeleteOverlay()
{
	Asura_Overlay_System::CloseOverlays(m_iOverlayID);
	if(s_pxThis == this)
	{
		s_pxThis = 0;
	}
}


void Tools_Zone_Editor::AddOverlayControls(class Asura_Overlay* pxOverlay,Asura_Vector_2& xOffset)
{
	Asura_Dialog* pxDialog;
	
	//add zone list heading
	pxDialog = new Asura_Dialog("Zones :",xOffset);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::SmallSpacing();
	
	//add zone list
	pxDialog = new Asura_List_Dialog(xOffset,m_ppszZoneNames,&m_iSelectedZone,10,m_iNumZoneNames,0,&m_bSelectedZoneChanged);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y = pxDialog->m_xMaxCoord.y + 1.0f;

	//creation and deletion
	pxDialog = new Asura_Function_Dialog("Create",xOffset,CreateZone,(int)this);
	pxOverlay->AddDialog(pxDialog);

	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;
	pxDialog = new Asura_Function_Dialog("Delete",xOffset,DeleteZone,(int)this);
	pxDialog->SetIsEnabledFunction(IsZoneSelected);
	pxOverlay->AddDialog(pxDialog);
	xOffset.x = 1.0f;
	xOffset.y += Asura_Dialog::DefaultSpacing();

    if ( AllowDefaultZones() )
    {
	    pxDialog = new Asura_Function_Dialog("Create Default",xOffset,CreateDefaultZone,(int)this);
	    pxDialog->SetHelpText("Creates a default zone that will be used when in no other zone");
	    pxDialog->SetIsEnabledFunction(DoesntHaveDefaultZone);
	    pxOverlay->AddDialog(pxDialog);
	    xOffset.y += Asura_Dialog::DefaultSpacing();
    }

	//rename
	pxDialog = new Asura_Function_Dialog("Rename",xOffset,RenameZone,(int)this);
	pxDialog->SetHelpText("Rename the selected zone");
	pxDialog->SetIsEnabledFunction(IsZoneSelected);
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxDialog = new Asura_Tickbox_Dialog("Show Volumes",xOffset,&m_bShowVolumes);
	pxDialog->SetHelpText("Renders all volumes for all zones");
	pxOverlay->AddDialog(pxDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();
}

bool Tools_Zone_Editor::IsZoneSelected()
{
	if(!s_pxThis) return false;
	return s_pxThis->m_pxSelectedZone != 0;
}

bool Tools_Zone_Editor::DoesntHaveDefaultZone()
{
	if(!s_pxThis) return false;
	if(!s_pxThis->m_pxZoneController->m_iNumberOfZones) return true;
	return !s_pxThis->m_pxZoneController->m_ppxZones[0]->IsDefaultZone();
}

void Tools_Zone_Editor::BuildZoneNameArray()
{
	//delete old names
	delete [] m_ppszZoneNames;
	m_ppszZoneNames = 0;

	//and get a new list of names from the controller
	m_iNumZoneNames = m_pxZoneController->m_iNumberOfZones;
	if(m_iNumZoneNames)
	{
		m_ppszZoneNames = new const char*[m_iNumZoneNames];
		for(int i=0;i<m_iNumZoneNames;i++)
		{
			m_ppszZoneNames[i] = m_pxZoneController->m_ppxZones[i]->m_xName.GetString();
		}	
	}
	else
	{
		m_iNumZoneNames = 1;
		m_ppszZoneNames = new const char*[1];
		m_ppszZoneNames[0] = "*Empty List*";
	}
}



void Tools_Zone_Editor::HandleSelectionChange()
{
	DeleteZoneOverlay();
	delete m_pxVolumeEdit;
	m_pxVolumeEdit = 0;
	
	if(m_iSelectedZone<m_pxZoneController->m_iNumberOfZones)
	{
		m_pxSelectedZone = m_pxZoneController->m_ppxZones[m_iSelectedZone];
		CreateZoneOverlay();

		if(!m_pxSelectedZone->IsDefaultZone())//(default zone doesn't need a volume)
		{
			m_pxVolumeEdit = new Tools_Zone_VolumeEdit_Controller(m_pxSelectedZone,&m_bChanged);
			m_pxVolumeEdit->StartVolumeEditing();
		}
		//keep the main overlay in foreground
		Asura_Overlay_System::BringOverlayToTop(m_iOverlayID);
		
	}
	else
	{
		m_pxSelectedZone = 0;
	}
}

void Tools_Zone_Editor::CreateZoneOverlay()
{
	if(!m_pxSelectedZone) return;

	//delete old overlay if it exists
	DeleteZoneOverlay();

	Asura_Overlay* pxOverlay = new Asura_Overlay(m_pxSelectedZone->m_xName.GetString(),false,&m_xZoneOverlayPos,&m_bZoneOverlayMinimised);
	Asura_Vector_2 xOffset = Asura_Vector_2(1.0f,3.0f);

	AddZoneOverlayControls(pxOverlay,xOffset);

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

void Tools_Zone_Editor::DeleteZoneOverlay()
{
	Asura_Overlay_System::CloseOverlays(m_iZoneOverlayID);
}

void Tools_Zone_Editor::CreateZone(bool bDefaultZone)
{
	if ( bDefaultZone )
	{
		//make sure default zone doesn't exist
		if(!DoesntHaveDefaultZone()) return;
	}

	//pick a default unused name
	Asura_String xName;

	if(bDefaultZone)
	{
		xName = "*Default*";
	}
	else
	{
		for(int i=0;;i++)
		{
			char szName[20];
			sprintf(szName,"Zone %d",i);

			//this name used?
			bool bOkay = true;
			for(int j=0;j<m_pxZoneController->m_iNumberOfZones;j++)
			{
				if(!stricmp(m_pxZoneController->m_ppxZones[j]->m_xName.GetString(),szName))
				{
					bOkay = false;
					break;
				}
			}
			if(bOkay)
			{
				xName = szName;
				break;
			}
		}
	
		//ask for name , giving default as option
		while(true)
		{
			GetString_Overlay xOverlay("Name for new zone:",xName.GetString());
			if(!xOverlay.DoModal()) return;

			xName = xOverlay.GetString();

			//this name used?
			bool bOkay = true;
			for(int j=0;j<m_pxZoneController->m_iNumberOfZones;j++)
			{
				if(!stricmp(m_pxZoneController->m_ppxZones[j]->m_xName.GetString(),xName.GetString()))
				{
					bOkay = false;
					ShowMessageOverlay("That name is already in use");
					break;
				}
			}

			if(bOkay) break;
		}
	}
	//create enlarged array of zones
	Asura_Zone** ppxZones = new Asura_Zone*[m_pxZoneController->m_iNumberOfZones+1];
	for(int i=0;i<m_pxZoneController->m_iNumberOfZones;i++)
	{
		ppxZones[i] = m_pxZoneController->m_ppxZones[i];
	}

	//allocate and initialse the new zone
	int iIndex = m_pxZoneController->m_iNumberOfZones;
	ppxZones[iIndex] = m_pxZoneController->AllocateZone();
	ppxZones[iIndex]->m_xName = xName;
	ppxZones[iIndex]->m_bDefaultZone = bDefaultZone;

	//replace in zone controller
	delete [] m_pxZoneController->m_ppxZones;
	m_pxZoneController->m_ppxZones = ppxZones;
	m_pxZoneController->m_iNumberOfZones++;

	m_iSelectedZone = iIndex;

	SortZones();

	//update the main overlay
	CreateOverlay();
	
	//select the new entry
	HandleSelectionChange();

	SetModified();
}


void Tools_Zone_Editor::DeleteZone()
{
	if(m_iSelectedZone >= m_pxZoneController->m_iNumberOfZones) return;

	//confirm deletion
	if(!ShowYesNoOverlay("Delete '%s'?",m_pxZoneController->m_ppxZones[m_iSelectedZone]->m_xName.GetString()))
	{
		return;
	}

	delete m_pxVolumeEdit;
	m_pxVolumeEdit = 0;
	
	//delete and shuffle down the remaining zones
	delete m_pxZoneController->m_ppxZones[m_iSelectedZone];
	for(int i=m_iSelectedZone;i<m_pxZoneController->m_iNumberOfZones-1;i++)
	{
		m_pxZoneController->m_ppxZones[i] = m_pxZoneController->m_ppxZones[i+1];
	}
	m_pxZoneController->m_iNumberOfZones--;

	//update the main overlay
	CreateOverlay();
	
	//selection will have changed
	HandleSelectionChange();

	SetModified();
}

void Tools_Zone_Editor::RenameZone()
{
	if(!m_pxSelectedZone) return;

	//ask for new name
	Asura_String xName = m_pxSelectedZone->m_xName;

	while(true)
	{
		GetString_Overlay xOverlay("Rename Zone:",xName.GetString());
		if(!xOverlay.DoModal()) return;

		xName = xOverlay.GetString();

		//this name used?
		bool bOkay = true;
		for(int j=0;j<m_pxZoneController->m_iNumberOfZones;j++)
		{
			if(m_pxZoneController->m_ppxZones[j] == m_pxSelectedZone) continue;

			if(!stricmp(m_pxZoneController->m_ppxZones[j]->m_xName.GetString(),xName.GetString()))
			{
				bOkay = false;
				ShowMessageOverlay("That name is already in use");
				break;
			}
		}

		if(bOkay) break;

	}

	//update the name
	m_pxSelectedZone->m_xName = xName;

	SortZones();

	//update the main overlay
	CreateOverlay();
	
	//reselect to update other overlays
	HandleSelectionChange();

	SetModified();
}

int Tools_Zone_Editor::SortZoneCallback(const void* p1,const void* p2)
{
	const Asura_Zone** ppxZone1 = (const Asura_Zone**)p1;
	const Asura_Zone** ppxZone2 = (const Asura_Zone**)p2;

	if((*ppxZone1)->IsDefaultZone()) return -1;
	if((*ppxZone2)->IsDefaultZone()) return 1;
	
	return stricmp((*ppxZone1)->m_xName.GetString(),(*ppxZone2)->m_xName.GetString());
}

void Tools_Zone_Editor::SortZones()
{
	if(m_pxZoneController->m_iNumberOfZones<2) return;

	//store pointer to current selection
	Asura_Zone* pxSelection = 0;
	if(m_iSelectedZone<m_pxZoneController->m_iNumberOfZones)
	{
		pxSelection = m_pxZoneController->m_ppxZones[m_iSelectedZone];
	}
	//sort
	qsort(m_pxZoneController->m_ppxZones,m_pxZoneController->m_iNumberOfZones,sizeof(Asura_Zone*),SortZoneCallback);

	//and find out the new selection index
	if(pxSelection)
	{
		for(int i=0;i<m_pxZoneController->m_iNumberOfZones;i++)
		{
			if(m_pxZoneController->m_ppxZones[i] == pxSelection)
			{
				m_iSelectedZone = i;
				break;
			}
		}
	}

}

void Tools_Zone_Editor::TransferModeData(int iMode, const char* szFilename)
{
	//open the file
	Tools_ModeDataFile xFile(szFilename,iMode);

	//tranfer the data
	//(none of these should be removed , and new data should go on the end, otherwise
	//the files will get screwed up)
	xFile.Transfer(m_xOverlayPos);
	xFile.Transfer(m_bOverlayMinimised);
	xFile.Transfer(m_xZoneOverlayPos);
	xFile.Transfer(m_bZoneOverlayMinimised);
	xFile.Transfer(m_bShowVolumes);
}
