//
// Tools_DialogueEvent_Overlay.cpp
//
// Created by Richard Rice - 30/10/2006
// 
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"
#include "Sound/Tools_DialogueEvent_Overlay.h"

#ifdef ASURA_USE_DIALOGUE_SYSTEM

#include "Camera/Asura_Camera.h"
#include "Core/Asura_NameTree.h"
#include "Interface/Asura_Overlay_System.h"
#include "Interface/Asura_Dialog.h"
#include "Rendering/Asura_Viewport.h"
#include "Rendering/Asura_Text_Output.h"
#include "Sound/Asura_Dialogue.h"
#include "Sound/Asura_Dialogue_System.h"

#include "Tools_Overlays.h"
#include "Load&Save/Tools_ModeDataFile.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Misc/Tools_Modal_Overlay.h"
#include "Sound/Tools_DialogueEventTab_Event.h"
#include "Sound/Tools_DialogueEventTab_LineTemplate.h"
#include "Sound/Tools_Dialogue_System.h"




//////////////////////////////////////////////////////////////////////////////////////////////////////
/*-----------------------------**
** Tools_DialogueEvent_Overlay **
**-----------------------------*/

Asura_Vector_2 Tools_DialogueEvent_Overlay::s_axOverlayPosition[2] =
{
	Asura_Vector_2(10.0f,10.0f),
	Asura_Vector_2(10.0f,10.0f),
};
int Tools_DialogueEvent_Overlay::s_aiOverlayID[2] =
{
	TOOLS_OVERLAY_DIALOGUEEVENT1,
	TOOLS_OVERLAY_DIALOGUEEVENT2,
};

Tools_DialogueEvent_Overlay* Tools_DialogueEvent_Overlay::s_apxThis[2];
bool Tools_DialogueEvent_Overlay::s_bCombineOverlays = true;

Asura_Handle Tools_DialogueEvent_Overlay::s_iDialogueHandle = ASURA_INVALID_HANDLE;


Tools_DialogueEvent_Overlay::Tools_DialogueEvent_Overlay(int iIndex)
:Asura_Overlay("Dialogue Event Editing",false,&s_axOverlayPosition[iIndex])
{
	Asura_Assert(!s_apxThis[iIndex],"Should only be one instance of the dialogue event overlay");
	m_iOverlayIndex = iIndex;
	s_apxThis[iIndex] = this;
	m_pxTabsDialog = 0;

}

Tools_DialogueEvent_Overlay::~Tools_DialogueEvent_Overlay()
{
	Asura_Assert(s_apxThis[m_iOverlayIndex] == this,"");
	s_apxThis[m_iOverlayIndex] = 0;


	while(m_xTabList.size())
	{
		delete m_xTabList.first_entry();
		m_xTabList.delete_first_entry();
	}

	if(!s_apxThis[0] && !s_apxThis[1])
	{
		//stop sound if all overlays are closed
		StopSound();
	}
}

void Tools_DialogueEvent_Overlay::TransferModeData(int mode)
{
	//open the file
	Tools_ModeDataFile file("SoundEvent.dat",mode);

	//tranfer the data
	//(none of these should be removed , and new data should go on the end, otherwise
	//the files will get screwed up)
	file.Transfer(s_axOverlayPosition[0]);
	file.Transfer(s_bCombineOverlays);
}


bool Tools_DialogueEvent_Overlay::EditEvent(Asura_Hash_ID uEventHash)
{
	//make sure the event exists
	if(!Asura_Dialogue_Storage::GetEventByHash(uEventHash))
	{
		return false;
	}

	GetEventOverlay(true)->EditEventInternal(uEventHash);	

	return true;
}

bool Tools_DialogueEvent_Overlay::EditLineTemplate(Asura_Hash_ID uLineTemplateHash)
{
	//make sure the template exists
	if(!Asura_Dialogue_Storage::GetLineTemplateByHash(uLineTemplateHash))
	{
		return false;
	}

	GetLineTemplateOverlay(true)->EditLineTemplateInternal(uLineTemplateHash);	

	return true;
}


void Tools_DialogueEvent_Overlay::EditEventInternal(Asura_Hash_ID uEventHash)
{
	//is there already a tab for this event?
	for(int i=0;i<m_xTabList.size();i++)
	{
		if(m_xTabList[i]->IsEvent() && m_xTabList[i]->GetHashID() == uEventHash)
		{
			//select the existing tab
			m_pxTabsDialog->SelectTab(i);
			Asura_Overlay_System::BringOverlayToTop(m_uID);
			return;
		}
	}

	u_int uInitialTab = m_pxTabsDialog->GetSelected();

	//Create a new tab for the event. It will be selected automatically
	Tools_DialogueEventTab_Event* pxTab = new Tools_DialogueEventTab_Event(m_pxTabsDialog,uEventHash);
	m_xTabList.add_entry(pxTab);

	CloseTabsIfTooLarge(uInitialTab,m_pxTabsDialog->GetSelected());

	ResizeOverlay();
	Asura_Overlay_System::BringOverlayToTop(m_uID);
}

void Tools_DialogueEvent_Overlay::EditLineTemplateInternal(Asura_Hash_ID uLineTemplateHash)
{
	//is there already a tab for this template?
	for(int i=0;i<m_xTabList.size();i++)
	{
		if(m_xTabList[i]->IsLineTemplate() && m_xTabList[i]->GetHashID() == uLineTemplateHash)
		{
			//select the existing tab
			m_pxTabsDialog->SelectTab(i);
			Asura_Overlay_System::BringOverlayToTop(m_uID);
			return;
		}
	}

	u_int uInitialTab = m_pxTabsDialog->GetSelected();

	//Create a new tab for the template. It will be selected automatically
	Tools_DialogueEventTab_LineTemplate* pxTab = new Tools_DialogueEventTab_LineTemplate(m_pxTabsDialog,uLineTemplateHash);
	m_xTabList.add_entry(pxTab);

	CloseTabsIfTooLarge(uInitialTab,m_pxTabsDialog->GetSelected());

	ResizeOverlay();
	Asura_Overlay_System::BringOverlayToTop(m_uID);
}


bool Tools_DialogueEvent_Overlay::CloseEventTab(Asura_Hash_ID uEventHash)
{
	Tools_DialogueEvent_Overlay* pxOverlay = GetEventOverlay(false);
	if(!pxOverlay) return true;

	
	//search for a tab for this event
	for(int i=0;i<pxOverlay->m_xTabList.size();i++)
	{
		Tools_DialogueEventTab* pxTab = pxOverlay->m_xTabList[i];

		if(pxTab->IsEvent() && pxTab->GetHashID() == uEventHash)
		{
			if(pxTab->CanBeClosed())
			{
				pxOverlay->CloseTabMain(i);
				return true;
			}
			else
			{
				return false;
			}
		}
	}
	return true;
}

bool Tools_DialogueEvent_Overlay::CloseLineTemplateTab(Asura_Hash_ID uLineTemplateHash)
{
	Tools_DialogueEvent_Overlay* pxOverlay = GetLineTemplateOverlay(false);
	if(!pxOverlay) return true;

	
	//search for a tab for this 
	for(int i=0;i<pxOverlay->m_xTabList.size();i++)
	{
		Tools_DialogueEventTab* pxTab = pxOverlay->m_xTabList[i];

		if(pxTab->IsLineTemplate() && pxTab->GetHashID() == uLineTemplateHash)
		{
			if(pxTab->CanBeClosed())
			{
				pxOverlay->CloseTabMain(i);
				return true;
			}
			else
			{
				return false;
			}
		}
	}
	return true;
}





void Tools_DialogueEvent_Overlay::ShowOverlay()
{
	int iMaxTabWidth = Asura_Maths::FloatToInt((Asura_Viewport::GetWidth()/ASURA_TEXT_OUTPUT_FONT_WIDTH)-15.0f);

	Asura_Vector_2 xOffset(1.0f,3.0f);
	m_pxTabsDialog = new Asura_Tabs_Dialog(xOffset,0,0,iMaxTabWidth);
	AddDialog(m_pxTabsDialog);

	//put close tab to the top right
	Asura_Dialog* pxDialog = new Asura_Function_Dialog("Close Tab",xOffset,CloseTab,(int)this);
	pxDialog->SetHelpText("Finish editing the current event");
	AddDialog(pxDialog);
	pxDialog->SetOverlayRelativePosition(Asura_Vector_2(pxDialog->m_xOffset.x - pxDialog->m_xMaxCoord.x - 2.0f,xOffset.y - Asura_Dialog::DefaultSpacing()),false,true);

	xOffset.y += Asura_Dialog::DefaultSpacing()*2;

	pxDialog = new Asura_Function_Dialog("Delete",xOffset,Delete,(int)this);
	pxDialog->SetHelpText("Delete this event");
	AddDialog(pxDialog);
	pxDialog->SetRelativePosition(m_pxTabsDialog,true,false);

	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;
	pxDialog = new Asura_Function_Dialog("Copy",xOffset,Copy,(int)this);
	pxDialog->SetHelpText("Copy this event to the paste buffer");
	AddDialog(pxDialog);
	pxDialog->SetRelativePosition(m_pxTabsDialog,true,false);

	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;
	pxDialog = new Asura_Function_Dialog("Paste",xOffset,Paste,(int)this);
	pxDialog->SetHelpText("Paste the copied event over this one");
	AddDialog(pxDialog);
	pxDialog->SetRelativePosition(m_pxTabsDialog,true,false);

	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;
	pxDialog = new Asura_Function_Dialog("Paste Multiple",xOffset,PasteMultiple,(int)this);
	pxDialog->SetHelpText("Paste some or all of the properties from this to one or more other events");
	AddDialog(pxDialog);
	pxDialog->SetRelativePosition(m_pxTabsDialog,true,false);


	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;
	pxDialog = new Asura_Function_Dialog("Duplicate",xOffset,Duplicate,(int)this);
	pxDialog->SetHelpText("Creates a copy of this event.");
	AddDialog(pxDialog);
	pxDialog->SetRelativePosition(m_pxTabsDialog,true,false);

	xOffset.x = pxDialog->m_xMaxCoord.x + 1.0f;
	pxDialog = new Asura_Function_Dialog("Rename",xOffset,Rename,(int)this);
	pxDialog->SetHelpText("Renames this event.");
	AddDialog(pxDialog);
	pxDialog->SetRelativePosition(m_pxTabsDialog,true,false);

	//next row
	xOffset.x = 1.0f;
	xOffset.y += Asura_Dialog::LargeSpacing();


	//xOffset.x = pxDialog->MaxCoord.x + 1.0f;
	pxDialog = new Asura_Function_Dialog("New Event",xOffset,CreateEvent,(int)this);
	pxDialog->SetHelpText("Create a new dialogue event");
	AddDialog(pxDialog);
	pxDialog->SetRelativePosition(m_pxTabsDialog,true,false);

	m_uID = s_aiOverlayID[m_iOverlayIndex];
	Asura_Overlay_System::SubmitNewOverlay(this);
}

void Tools_DialogueEvent_Overlay::CloseOverlay()
{
	Asura_Overlay_System::CloseOverlays(s_aiOverlayID[0]);
	Asura_Overlay_System::CloseOverlays(s_aiOverlayID[1]);
}

void Tools_DialogueEvent_Overlay::CloseTab()
{
	CloseTabMain(m_pxTabsDialog->GetSelected());
}

void Tools_DialogueEvent_Overlay::CloseTabMain(u_int uTab)
{
	Asura_Assert(uTab<(u_int)m_xTabList.size(),"");

	//remove tab from the overlay
	m_pxTabsDialog->DeleteTab(uTab);

	//and delete entry from the list
	delete m_xTabList[uTab];
	m_xTabList.delete_entry(m_xTabList[uTab]);

	//If there are no tabs left , then close the overlay
	if(!m_xTabList.size())
	{
		Asura_Overlay_System::CloseOverlays(m_uID);
	}
}

void Tools_DialogueEvent_Overlay::Delete()
{
	u_int uTab = m_pxTabsDialog->GetSelected();
	m_xTabList[uTab]->Delete();
}

void Tools_DialogueEvent_Overlay::Copy()
{
	u_int uTab = m_pxTabsDialog->GetSelected();
	m_xTabList[uTab]->Copy();
}

void Tools_DialogueEvent_Overlay::Paste()
{
	u_int uTab = m_pxTabsDialog->GetSelected();
	m_xTabList[uTab]->Paste();
}

void Tools_DialogueEvent_Overlay::PasteMultiple()
{
	u_int uTab = m_pxTabsDialog->GetSelected();
	m_xTabList[uTab]->PasteMultiple();
}


void Tools_DialogueEvent_Overlay::Duplicate()
{
	u_int uTab = m_pxTabsDialog->GetSelected();
	m_xTabList[uTab]->Duplicate();
}

void Tools_DialogueEvent_Overlay::Rename()
{
	u_int uTab = m_pxTabsDialog->GetSelected();
	m_xTabList[uTab]->Rename();
	//update the tab name after renaming
	m_pxTabsDialog->RenameTab(uTab,m_xTabList[uTab]->GetTabName());
}


void Tools_DialogueEvent_Overlay::CloseTabsIfTooLarge(u_int uTabToPreserve1,u_int uTabToPreserve2)
{

	if(m_xTabList.size() > 25)
	{
		//find a tab that can safely be removed
		for(u_int uTabToDelete=0; uTabToDelete<(u_int)m_xTabList.size();uTabToDelete++)
		{
			if(uTabToDelete == uTabToPreserve1) continue;			
			if(uTabToDelete == uTabToPreserve2) continue;
			if(!m_xTabList[uTabToDelete]->CanBeClosed()) continue;

			
			m_pxTabsDialog->DeleteTab(uTabToDelete);

			//and delete entry from the list
			delete m_xTabList[uTabToDelete];
			m_xTabList.delete_entry(m_xTabList[uTabToDelete]);

			break;
		}
	}
}

void Tools_DialogueEvent_Overlay::PerFrameUpdate()
{
	Asura_Overlay::PerFrameUpdate();

	for(LIF<Tools_DialogueEventTab*> xTabLIF(&m_xTabList);!xTabLIF.done();xTabLIF.next())
	{
		xTabLIF()->PerFrameUpdate();
	}

	//Update test sound.
	//This may end up being called more than once per frame , but it doesn't really matter
	//UpdateSound();

	#if 1
	//Make sure that the  tabs are on the correct overlays
	u_int uTabIndex = 0;
	for(LIF<Tools_DialogueEventTab*> xTabLIF(&m_xTabList);!xTabLIF.done();)
	{
		bool bTemplate = xTabLIF()->IsLineTemplate();
		//events go on overlay 0 , and templates on 0 or 1 depending on the combine overlay setting
		int iDesiredOverlay = (bTemplate && !s_bCombineOverlays) ? 1 : 0;

		if(iDesiredOverlay != m_iOverlayIndex)
		{
			Asura_Hash_ID uHash = xTabLIF()->GetHashID();	
			//on wrong overlay. delete and edit again
			m_pxTabsDialog->DeleteTab(uTabIndex);
			delete xTabLIF();
			xTabLIF.delete_current();

			if(bTemplate)
			{
				EditLineTemplate(uHash);
			}
			else
			{
				EditEvent(uHash);
			}
		}
		else
		{
			xTabLIF.next();
			uTabIndex++;
		}
	}
	#endif


	//set the overlay title based on the current selection
	if(m_xTabList.size())
	{
		Tools_DialogueEventTab* pxSelectedTab = m_xTabList[m_pxTabsDialog->GetSelected()];
		
		char szTitle[100];

		const char* szPrefix = 0;
		if(pxSelectedTab->IsEvent())
		{
			szPrefix = "Event";
		}
		else if(pxSelectedTab->IsLineTemplate())
		{
			szPrefix = "Template";
		}

		Asura_PrintfToBfr(szTitle,"%s - %s",sizeof(szTitle),true,szPrefix,pxSelectedTab->GetName());

		SetTitle(szTitle);
	}
	else
	{
		Asura_Overlay_System::CloseOverlays(m_uID);
	}
}




void Tools_DialogueEvent_Overlay::UpdateEventFromAsuraData(Asura_Hash_ID uEventHash)
{
	Tools_DialogueEvent_Overlay* pxOverlay = GetEventOverlay(false);
	if(!pxOverlay) return;

	//Is there a tab for this event?
	for(int i=0;i<pxOverlay->m_xTabList.size();i++)
	{
		if(pxOverlay->m_xTabList[i]->IsEvent() && pxOverlay->m_xTabList[i]->GetHashID() == uEventHash)
		{
			pxOverlay->m_xTabList[i]->UpdateFromAsuraData();
			return;
		}
	}					  
}

void Tools_DialogueEvent_Overlay::UpdateLineTemplateFromAsuraData(Asura_Hash_ID uLineTemplateHash)
{
	Tools_DialogueEvent_Overlay* pxOverlay = GetLineTemplateOverlay(false);
	if(!pxOverlay) return;

	//Is there a tab for this?
	for(int i=0;i<pxOverlay->m_xTabList.size();i++)
	{
		if(pxOverlay->m_xTabList[i]->IsLineTemplate() && pxOverlay->m_xTabList[i]->GetHashID() == uLineTemplateHash)
		{
			pxOverlay->m_xTabList[i]->UpdateFromAsuraData();
			return;
		}
	}					  
}



Tools_DialogueEvent_Overlay* Tools_DialogueEvent_Overlay::GetEventOverlay(bool bCreate)
{
	int iIndex = 0;
	if(!s_apxThis[iIndex] && bCreate)
	{
		new Tools_DialogueEvent_Overlay(iIndex);
		s_apxThis[iIndex]->ShowOverlay();
	}
	return s_apxThis[iIndex];
}

Tools_DialogueEvent_Overlay* Tools_DialogueEvent_Overlay::GetLineTemplateOverlay(bool bCreate)
{
	int iIndex = s_bCombineOverlays ? 0 : 1;
	if(!s_apxThis[iIndex] && bCreate)
	{
		new Tools_DialogueEvent_Overlay(iIndex);
		s_apxThis[iIndex]->ShowOverlay();
	}
	return s_apxThis[iIndex];
}


void Tools_DialogueEvent_Overlay::CreateEvent()
{
	Tools_Dialogue_System::CreateEvent(0);
}

void Tools_DialogueEvent_Overlay::PlayDialogue(Asura_Hash_ID uEventHash)
{
	StopSound();

	const Asura_Dialogue_Event* pxEvent = Asura_Dialogue_Storage::GetEventByHash(uEventHash);
	if(!pxEvent) return;

	Asura_Dialogue_CharacterMappingList xMappings;
	Asura_Dialogue_System::PlayEvent(pxEvent,&xMappings,&s_iDialogueHandle);
}


void Tools_DialogueEvent_Overlay::StopSound()
{

	Asura_Dialogue_System::Stop(s_iDialogueHandle);
}




///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*-------------------------------**
** Tools_DialogueFilename_Dialog **
**-------------------------------*/

int Tools_DialogueFilename_Dialog::s_iListValidID;

Tools_DialogueFilename_Dialog::Tools_DialogueFilename_Dialog(const Asura_Vector_2& xOffset,Asura_String* pxFilename,bool* pbModified)
:Asura_DropDown_List_Dialog(xOffset)
{
	m_bInitialised = false;
	m_pszFilenameList = 0;
	m_uFilenameListSize = 0;
	m_pxFilenameTree = 0;

	m_pbModified = pbModified;
	m_pxFilename = pxFilename;
	Asura_Assert(m_pxFilename,"");

	m_bSelectionChanged = false;
	m_xFilenameBackup = *m_pxFilename;

	UpdateFilenameList();
	Initialise(xOffset,m_pszFilenameList,&m_iSelectedIndex,10,m_uFilenameListSize,NULL,&m_bSelectionChanged,"File");
	m_bInitialised = true;

	SetOnSelectCallback(OnSelectionChanged,(int) this);

}

Tools_DialogueFilename_Dialog::~Tools_DialogueFilename_Dialog()
{
	DeleteFilenameList();
}


void Tools_DialogueFilename_Dialog::UpdateFilenameList()
{
	DeleteFilenameList();

	m_pxFilenameTree = new Asura_NameTree;

	//make sure the filename being edited isn't blank
	if(m_pxFilename->IsBlank())
	{
		*m_pxFilename = "Default";
		m_xFilenameBackup = *m_pxFilename;
	}
	//and add it to the tree
	int iJunkIndex;
	m_pxFilenameTree->FindNodeIndexByName(m_pxFilename->GetString(),true,iJunkIndex);

	//add the filenames from all the events
	for(u_int i=0;i<Asura_Dialogue_Storage::GetNumberOfEvents();i++)
	{
		const Asura_Dialogue_Event* pxEvent = Asura_Dialogue_Storage::GetEventByIndex(i);
		const char* szFilename = pxEvent->GetFilename();

		if(szFilename && *szFilename)
		{
			m_pxFilenameTree->FindNodeIndexByName(szFilename,true,iJunkIndex);
		}
	}

	//add level specific file if a level is being edited
	if(Tools_SaveAsura::GetFileType() == AsuraFile_Environment)
	{
		m_pxFilenameTree->FindNodeIndexByName(Tools_Dialogue_System::GetLevelSpecificFile(),true,iJunkIndex);
	}


	//Fill array with these strings , adding an entry for new category at the beginning
	m_uFilenameListSize = m_pxFilenameTree->GetNumberOfNodes()+1;
	m_pszFilenameList = new const char*[m_uFilenameListSize];
	m_pszFilenameList[0] = "*New*";
	m_pxFilenameTree->MakeSortedNameList(m_pszFilenameList+1);

	//find the index of the current string in the sorted list
	m_iSelectedIndex = 0;
	for(u_int i=1;i<m_uFilenameListSize;i++)
	{
		if(!m_pxFilename->Compare(m_pszFilenameList[i]))
		{
			m_iSelectedIndex = (int) i;
			break;
		}
	}
	Asura_Assert(m_iSelectedIndex,"Failed to find filename in list");

	if(m_bInitialised)
	{
		ReplaceStrings(m_uFilenameListSize,m_pszFilenameList);
	}

	m_iListValidID = s_iListValidID;
}

void Tools_DialogueFilename_Dialog::DeleteFilenameList()
{
	delete [] m_pszFilenameList;
	m_pszFilenameList = 0;
	m_uFilenameListSize = 0;
	delete m_pxFilenameTree;
	m_pxFilenameTree = 0;
}

void Tools_DialogueFilename_Dialog::OnSelectionChanged()
{
	if(m_iSelectedIndex)
	{
		//an existing filename has been chosen
		*m_pxFilename = m_pszFilenameList[m_iSelectedIndex];
		Tools_Dialogue_System::SetMostRecentFile(m_pszFilenameList[m_iSelectedIndex]);
		if(m_pbModified) *m_pbModified = true;
	}
	else
	{
		//The new filename option has been chosen.
		//Ask for a new name
		GetString_Overlay xOverlay("New Filename (without path/extension):","");
		if(xOverlay.DoModal())
		{
			const char* szFilename = xOverlay.GetString();
			if(*szFilename)
			{
				if(!stricmp(szFilename,Tools_Dialogue_System::GetLevelSpecificFile()))
				{
					//don't allow 'Level' to be picked.
					ShowMessageOverlay("%s is reserved for the level specific file",Tools_Dialogue_System::GetLevelSpecificFile());
				}
				else
				{
				
					*m_pxFilename = szFilename;
					Tools_Dialogue_System::SetMostRecentFile(szFilename);
					if(m_pbModified) *m_pbModified = true;

					//Bump the list valid ID so that other filename dialogs will be updated as well
					s_iListValidID++;
				}
			}
		}
		//Update the list so that the new filename (if any) is added , and so that the selection index
		//is put abck to a non-zero value.
		UpdateFilenameList();
	}
	m_xFilenameBackup = *m_pxFilename;
	m_bSelectionChanged = false;
}

void Tools_DialogueFilename_Dialog::PerFrameUpdate()
{
	//check for external changes to the filename
	if(!m_bSelectionChanged && m_xFilenameBackup.Compare(*m_pxFilename))
	{
		m_xFilenameBackup = *m_pxFilename;
		UpdateFilenameList();
	}

	if(m_iListValidID != s_iListValidID && !IsHidden())
	{
		//update strings
		UpdateFilenameList();
	}

	Asura_DropDown_List_Dialog::PerFrameUpdate();
}


#endif // ASURA_USE_DIALOGUE_SYSTEM
