//
// Asura_Overlay.cpp
//
// Created by Kevin Lea - 12/06/2001 11:11:15
// 
// Copyright (C) 2001 Rebellion, All Rights Reserved.
//

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#ifdef ASURA_TOOLS

#include "Interface/Asura_Overlay.h"

#include "Core/Asura_UserInput.h"
#include "Debugging/Asura_Assert.h"
#include "Interface/Asura_Cursor.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Gadget.h"
#include "Interface/Asura_Overlay_System.h"
#include "Rendering/Asura_Text_Output.h"
#include "Rendering/Asura_Render.h"

//tools
#include "Interface/Tools_ContextMenu_System.h"
#include "Interface/Tools_WindowsCursor.h"

// G L O B A L S /////////////////////////////////////////////////////////////

extern unsigned int Asura_OverlayBrightness;
extern int MenuBackdropTextureIndex;
extern int MenuSkinTextureIndex;

// C L A S S   I M P L E M E N T A T I O N S /////////////////////////////////


Asura_Overlay::Asura_Overlay( const char* szName, bool bAlwaysOnTop, Asura_Vector_2* pxPosition, bool* pbMinimised )
{
	Initialise(szName, bAlwaysOnTop, pxPosition, pbMinimised, NULL);
}


Asura_Overlay::Asura_Overlay( const char* szName, bool bAlwaysOnTop, Asura_Vector_2* pxPosition, u_int* puMinimisedState )
{
	Initialise(szName, bAlwaysOnTop, pxPosition, NULL, puMinimisedState);
}


void Asura_Overlay::Initialise(const char *szName, bool bAlwaysOnTop, Asura_Vector_2* pxPosition, bool* pbMinimised, u_int* puMinimisedState)
{
	Asura_Assert( szName && *szName, "" );
	m_xName = szName;

	m_pxDialogList = 0;

	m_uMinimisedState = ASURA_OVERLAY_MINIMISED_NOTMINIMISED;
	m_bIsBeingDragged = false;
	m_bShouldClose = false;
	m_bClosedByCloseGadget = false;
	m_bSortToBack = false;
	m_bSortToFront = true;
	m_bOnPanel = false;
	m_bNeedsResize = false;
	m_bDialogTookFocus = false;
	m_bOverrideOverlaysHiddenFlag = false;

	m_bAlwaysOnTop = bAlwaysOnTop;
	m_pxPositionBackup = pxPosition;

	if( pxPosition )
	{
		m_xPosition = *pxPosition;
	}
	else
	{
		m_xPosition.x = 1.0f;
		m_xPosition.y = 2.0f;
	}

	m_pbExternalMinimisedFlag = pbMinimised;
	if( pbMinimised )
	{
		m_uMinimisedState = *pbMinimised ? ASURA_OVERLAY_MINIMISED_ATBOTTOM : ASURA_OVERLAY_MINIMISED_NOTMINIMISED;
	}
	m_puExternalMinimisedState = puMinimisedState;
	if( puMinimisedState)
	{
		m_uMinimisedState = *puMinimisedState;
	}

	m_xOrigin.x = 1.0f;
	m_xOrigin.y = 2.0f;

	m_uID = 0;

	m_bAlwaysFullbright = false;
	m_bCanBeMovedByUser = true;
	m_bHasWindowsDialog = true;
	m_bJustCreated = true;
	m_bClickedOnOverlay = false;

	m_pxTabFocusDialog = NULL;
	m_iNumCurrentParentDialog = 0;
	for(int i=0;i<ASURA_OVERLAY_MAX_NESTED_TAB_DIALOG;i++)
	{
		m_apxCurrentParentDialog[i] = 0;
	}


	m_iMaxDialogPriority = 0;

	m_uHideFlags = 0;

	m_fWidth = 0.0f;
	m_fHeight = 0.0f;
	m_fFullHeight = 0.0f;
	m_uBrightness = 127;

	m_pfnAddContextMenuItems = 0;
	m_pAddContextMenuItems_UserData = 0;

	m_bResizeUsingDialogBorder = false;
	m_xResizeDelta = Asura_Maths::ZeroVector2;
	m_uResizeFlags = 0;
	m_pxResizedDialog = 0;

	m_uSubmissionIndex = 0;
	m_uTieBreakIndex = 0;
}

Asura_Overlay::~Asura_Overlay( void )
{
	Asura_Dialog* pxDialog = m_pxDialogList;
	while(pxDialog)
	{
		Asura_Dialog* pxNextDialog = pxDialog->m_pxNextDialog;
		delete pxDialog;
		pxDialog = pxNextDialog;
	}
}

void Asura_Overlay::AddDialog( Asura_Dialog* pxNewDialog, bool bAddedByTabDialog /*= false*/ )
{
	if(pxNewDialog->m_pxOverlay)
	{
		Asura_Assert(pxNewDialog->m_pxOverlay == 0,"Dialog already belongs to an overlay");
		return;
	}
	
	if(m_iNumCurrentParentDialog && !bAddedByTabDialog)
	{
		//redirect to tab dialog
		m_apxCurrentParentDialog[m_iNumCurrentParentDialog-1]->AddDialog(pxNewDialog);

	}
	else
	{
		if( pxNewDialog )
		{
			pxNewDialog->m_pxOverlay = this;
			if (m_pxDialogList)
			{
				pxNewDialog->m_pxNextDialog = m_pxDialogList;
			}
			m_iMaxDialogPriority = Asura_Maths::Max(pxNewDialog->GetFocusPriority(), m_iMaxDialogPriority);
			m_pxDialogList = pxNewDialog;
		}
	}
}

void Asura_Overlay::PerFrameUpdate(void)
{
	for(Asura_Dialog* pxDialog = m_pxDialogList;pxDialog;pxDialog = pxDialog->m_pxNextDialog)
	{
		pxDialog->PerFrameUpdate();
	}
	if(m_bNeedsResize)
	{
		ResizeOverlay();
	}
}

void Asura_Overlay::Update(void)
{
	if (m_pxDialogList)
	{
		m_bDialogTookFocus = false;
		// Set current priority to maximum added
		int iPriority = m_iMaxDialogPriority;
		while(iPriority >= 0)
		{
			// Update next set of priorities
			iPriority = m_pxDialogList->UpdateList(m_xOrigin, iPriority);
			// Don't update any more dialogs if the higher priority ones took focus
			if(m_bDialogTookFocus)
			{
				m_bDialogTookFocus = false;
				break;
			}
		}
	}


	Asura_Dialog* pxFocusDialog = GetCurrentFocusDialog();
	if(pxFocusDialog) m_pxTabFocusDialog = pxFocusDialog;

	if(!Asura_UserInput::KeyPressed(ASURA_KEYID_CTRL))
	{
		//check for tabbing through controls
		if(Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_TAB))
		{
			if(Asura_UserInput::KeyPressed(ASURA_KEYID_SHIFT))
			{
				TabBackwards();
			}
			else
			{
				TabForwards();
			}
		}
	}
}

void Asura_Overlay::RenderBackdrop( u_int uColour ) const
{
	//if on the panel at the bottom of the screen , the backdrop will be handled seperately
	if(m_bOnPanel) return;

	if( Asura_Overlay_System::UseSkinnedUI() )
	{
		// skinned overlay renderer
		Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS);
		Asura_Render::CurrentStates.RequestZBufferWriteEnabled(true);

		uColour |= Asura_OverlayBrightness;

		Asura_Render::Overlay.Colour = 0xffffffff; //uColour;

		Asura_Render::Overlay.TextureIndex = MenuSkinTextureIndex;
		Asura_Render::Overlay.TranslucencyMode = ASURA_TRANSLUCENCY_NORMAL;

		float x = Asura_Maths::FloatToInt(m_xOrigin.x * ASURA_TEXT_OUTPUT_FONT_WIDTH) - 0.5f;
		float y = Asura_Maths::FloatToInt(m_xOrigin.y * ASURA_TEXT_OUTPUT_FONT_HEIGHT) - 0.5f;
		float w = m_fWidth * ASURA_TEXT_OUTPUT_FONT_WIDTH;
		float h = m_fHeight * ASURA_TEXT_OUTPUT_FONT_HEIGHT;

		float afX[4],afY[4];
		float afU[4],afV[4];

		float fType=0.0f;
		if ((uColour>>24) < 0xff)
		{
			// Select the unfocused version
			fType=0.25f;
		}

		afX[0]=x;
		afX[1]=x+8.0f;
		afX[2]=(x+w)-8.0f;
		afX[3]=x+w;

		afY[0]=y;
		afY[1]=y+28.0f;
		afY[2]=(y+h)-8.0f;
		afY[3]=y+h;

		float fTexWidth = 256.0f;
		afU[0]= fType+(0.0f/fTexWidth);
		afU[1]= fType+(8.0f/fTexWidth);
		afU[2]= fType+(56.0f/fTexWidth);
		afU[3]= fType+(64.0f/fTexWidth);

		float fTexHeight = 256.0f;
		afV[0]= (0.0f/fTexHeight);
		afV[1]= (28.0f/fTexHeight);
		afV[2]= (56.0f/fTexHeight);
		afV[3]= (64.0f/fTexHeight);

		for(int iY=0; iY<3; iY++)
		{	
			for(int iX=0; iX<3; iX++)
			{
				Asura_Render::Overlay.SetRectUV(afU[iX],afV[iY],afU[iX+1]-afU[iX],afV[iY+1]-afV[iY]);
				Asura_Render::Overlay.SetRectXY(afX[iX],afY[iY],afX[iX+1]-afX[iX],afY[iY+1]-afY[iY]);
				Asura_Render::Overlay.Render();
			}
		}
	}
	else
	{
		// standard overlay renderer
		Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS);
		Asura_Render::CurrentStates.RequestZBufferWriteEnabled(true);

		uColour |= Asura_OverlayBrightness;

		Asura_Render::Overlay.Colour = uColour;
		Asura_Render::Overlay.TextureIndex = MenuBackdropTextureIndex;
		Asura_Render::Overlay.TranslucencyMode = ASURA_TRANSLUCENCY_NORMAL;

		float x=Asura_Maths::FloatToInt(m_xOrigin.x * ASURA_TEXT_OUTPUT_FONT_WIDTH) - 0.5f;
		float y=Asura_Maths::FloatToInt(m_xOrigin.y * ASURA_TEXT_OUTPUT_FONT_HEIGHT) - 0.5f;

		Asura_Render::Overlay.SetRectUV(0.0f,0.0f,1.0f,1.0f);
		Asura_Render::Overlay.SetRectXY(x,y,m_fWidth * ASURA_TEXT_OUTPUT_FONT_WIDTH,m_fHeight * ASURA_TEXT_OUTPUT_FONT_HEIGHT);

		Asura_Render::Overlay.Render();

		if( m_bHasWindowsDialog )
		{
			Asura_Render::Overlay.Y[2] = y + 2.0f*ASURA_TEXT_OUTPUT_FONT_HEIGHT;
			Asura_Render::Overlay.Y[3] = y + 2.0f*ASURA_TEXT_OUTPUT_FONT_HEIGHT;

			Asura_Render::Overlay.Render();
		}
	}
}

void Asura_Overlay::Render(void)
{
	if (m_pxDialogList)
	{
		if(IsMinimised())
		{
			//if minimised we only want to draw the 'windows' dialog
			Asura_Dialog* pxDialog = m_pxDialogList;
			while(pxDialog)
			{
				if(pxDialog->IsWindowsDialog())
				{
					pxDialog->Render(m_xOrigin);
					break;
				}
				pxDialog = pxDialog->m_pxNextDialog;
			}

		}
		else
		{
			//Render lowest priority first
			int iPriority = 0;
			while(iPriority != ASURA_DIALOG_INVALIDPRIORITY)
			{
				// Render next set of priorities
				iPriority = m_pxDialogList->RenderList(m_xOrigin, iPriority);
			}
			//make sure clipping is disabled after rendering has finished
			Asura_Subwindow_Dialog::DisableClipping();
		}
	}
}

bool Asura_Overlay::HasCursorOnTop(void) const
{
	float cx,cy;

	Asura_Cursor::GetScreenCoords(cx,cy);

	if (m_xOrigin.x <= cx && m_xOrigin.x + m_fWidth >= cx)
		if (m_xOrigin.y <= cy && m_xOrigin.y + m_fHeight >= cy)
		{
			return true;
		}

	if(!IsMinimised()) 
	{
		//consider cursor to be on the overlay if its over any dialog ,even if they are outside
		//of the overlay boundaries. (for drop down lists that have dropped down too far). RAR 02/06/2005
		return IsCursorOverADialog();
	}
	return false;
}

bool Asura_Overlay::HasCursorOverTitleBar() const
{
	float cx,cy;

	Asura_Cursor::GetScreenCoords(cx,cy);

	if (m_xOrigin.x <= cx && m_xOrigin.x + m_fWidth >= cx)
	{
 		if (m_xOrigin.y <= cy && m_xOrigin.y + 2.0f >= cy)
		{
			return true;
		}
	}
	return false;
}

bool Asura_Overlay::IsCursorOverADialog() const
{
	float fX,fY;

	Asura_Cursor::GetScreenCoords(fX,fY);

	Asura_Vector_2 xPos(fX,fY);

	const Asura_Dialog* pxDialog = m_pxDialogList;
	while(pxDialog)
	{
		if(pxDialog->IsPointInDialog(m_xOrigin,xPos)) return true;
		pxDialog = pxDialog->m_pxNextDialog;
	}
	return false;
}


Asura_Dialog* Asura_Overlay::GetDialog(int id)
{
	if(id==-1)	return 0;

	Asura_Dialog* dialog = m_pxDialogList;
	while(dialog)
	{
		if(dialog->m_iControlID == id) return dialog;
		dialog = dialog->m_pxNextDialog;
	}
	return 0;
}

Asura_Dialog* Asura_Overlay::GetDialog(const char* pszLabel)
{
	if(!pszLabel) return 0;

	Asura_Dialog* pxDialog = m_pxDialogList;
	while(pxDialog)
	{
		if(!pxDialog->m_xName.Compare(pszLabel)) return pxDialog;
		pxDialog = pxDialog->m_pxNextDialog;
	}
	return 0;
}

void Asura_Overlay::UpdateDialogLabel(int id,const char* text)
{
	//find the control to be changed
	Asura_Dialog* dialog = GetDialog(id);
	if(!dialog) return;
	UpdateDialogLabel(dialog,text);
}


void Asura_Overlay::UpdateDialogLabel(Asura_Dialog* dialog,const char* text)
{
	Asura_Assert(dialog,"")	;

	//change the text on that control
	dialog->UpdateLabel(text);

	//calculate the new size for the overlay
	ResizeOverlay();
}

void Asura_Overlay::SetTitle(const char* szName)
{
	if(strcmp(szName,m_xName.GetString()))
	{
		m_xName = szName;
		//find the windows dialog , and update that as well
		Asura_Dialog* pxDialog = m_pxDialogList;
		while(pxDialog)
		{
			if(pxDialog->IsWindowsDialog())
			{
				UpdateDialogLabel(pxDialog,szName);
				break;
			}
			pxDialog = pxDialog->m_pxNextDialog;
		}
	}
}

void Asura_Overlay::ResizeOverlay()
{
	float oldHeight = m_fHeight;
	float oldWidth = m_fWidth;
	CalculateOverlaySize();

	if(oldHeight == 2.0f && IsMinimised())
	{
		m_fHeight = 2.0f;
	}

	//update window controls dialog thingy
	float adjust = m_fWidth - oldWidth;
	Asura_Dialog* pxDialog = m_pxDialogList;
	while(pxDialog)
	{
		if(pxDialog->IsWindowsDialog())
		{
			//dialog->Offset.x+=adjust;
			pxDialog->m_xMaxCoord.x+=adjust;
			//shift all the gadgets on this dialog
			Asura_Gadget* gadget = pxDialog->m_pxGadgetList;
			while(gadget)
			{
				gadget->m_xOffset.x+=adjust;
				gadget = gadget->m_pxNextGadget;
			}


		}
		pxDialog = pxDialog->m_pxNextDialog;
	}

	//if dragging the mins, then update the overlay position to leave the right/bottom in the same place
	if(m_uResizeFlags & ASURA_OVERLAY_RESIZE_MINX)
	{
		m_xPosition.x -= m_fWidth - oldWidth;
	}
	if(m_uResizeFlags & ASURA_OVERLAY_RESIZE_MINY)
	{
		m_xPosition.y -= m_fHeight - oldHeight;
	}
	

	m_bNeedsResize = false;
}

void Asura_Overlay::CalculateOverlaySize()
{
	float fMaxX = 0.0f;
	float fMaxY = 0.0f;

	//Make sure the sizes of the individual controls are up to date
	for(Asura_Dialog* pxDialog = m_pxDialogList; pxDialog; pxDialog = pxDialog->m_pxNextDialog)
	{
		pxDialog->UpdateSizeInit();
	}
	for(Asura_Dialog* pxDialog = m_pxDialogList; pxDialog; pxDialog = pxDialog->m_pxNextDialog)
	{
		pxDialog->UpdateSize();
	}


	{

		Asura_Dialog* dialog = m_pxDialogList;

		while(dialog)
		{
			//ignore the windows dialog , since it will need to be sized to match the overlay size
			//ignore members of subwindow dialogs , since their size will be included in the main control
			if( !dialog->IsHidden() && !dialog->IsWindowsDialog() && !dialog->GetParentSubwindow())
			{
				Asura_Vector_2 xCoords = dialog->GetMaxCoords();

				//ignore x/y components of controls that are relative to the right/bottom of the overlay
				if(!dialog->IsRelativeToOverlayRight())
				{
					fMaxX = Asura_Maths::Max(fMaxX,xCoords.x);
				}
				if(!dialog->IsRelativeToOverlayBottom())
				{
					fMaxY = Asura_Maths::Max(fMaxY,xCoords.y);
				}

			}

			Asura_Assert(dialog!=dialog->m_pxNextDialog, ""); // SD: Infinite loop (happened when adding two times in a row the same dialog.)
			dialog = dialog->m_pxNextDialog;
		}
	}

	if( m_bHasWindowsDialog )
	{
		float fNameWidth = 0.0f;
		if(!m_xName.IsBlank())
		{
			if(Asura_Overlay_System::UseSkinnedUI())
			{
		 		fNameWidth = Asura_Text_Output::WidthOfString(m_xName.GetString(),ASURA_TEXT_FORMAT_SMALLFONT);
			}
			else
			{
				fNameWidth = (float)Asura_Text_Output::LengthOfString(m_xName.GetString());
			}
		}

		Asura_Vector_2 v = Asura_Vector_2(fNameWidth,0.0f);
		if (!m_bAlwaysOnTop) v.x += 6.0f;
		fMaxX = Asura_Maths::Max(fMaxX,v.x);
		fMaxY = Asura_Maths::Max(fMaxY,v.y);
	}

	m_fFullHeight = fMaxY+1.0f;
	m_fWidth = fMaxX+1.0f;
	m_fHeight = m_fFullHeight;
}

void Asura_Overlay::SetPosition( float fX, float fY, bool bInstant )
{
	m_xPosition.x = fX;
	m_xPosition.y = fY;

	if( bInstant ) { m_xOrigin = m_xPosition; }
}


void Asura_Overlay::TabForwards()
{
	if(IsMinimised()) return;

	Asura_Dialog* pxCurrentFocus = GetCurrentFocusDialog();
	if(!pxCurrentFocus) pxCurrentFocus = m_pxTabFocusDialog;

	Asura_Dialog* pxNewFocus = 0;
	bool bBeforeCurrent = true;

	//Search for the control closest to being in front of the current one that can take tab focus
	//or the one closest to the end of the list if no other is found.
	//(Because of the way controls get added to the list , going 'Forwards' involves going
	//backwards through the list).
	//RAR 27/07/04

	Asura_Dialog* pxDialog = m_pxDialogList;
	while(pxDialog)
	{
		if(pxDialog == pxCurrentFocus)
		{
			bBeforeCurrent = false;
			if(pxNewFocus) break;//we have found an acceptable control , so no need to search further
		}

		if(pxDialog->CanTakeTabFocus())
		{
			pxNewFocus = pxDialog;
		}

		pxDialog = pxDialog->m_pxNextDialog;

	}
	if(pxNewFocus) pxNewFocus->TakeTabFocus();
}

void Asura_Overlay::TabBackwards()
{
	if(IsMinimised()) return;

	Asura_Dialog* pxCurrentFocus = GetCurrentFocusDialog();
	if(!pxCurrentFocus) pxCurrentFocus = m_pxTabFocusDialog;

	Asura_Dialog* pxNewFocus = 0;
	bool bAfterCurrent = false;

	//Search for the control closest to being after the current one that can take tab focus
	//or the one closest to the start of the list if no other is found.
	//(Because of the way controls get added to the list , going 'Backwards' involves going
	//forwards through the list).
	//RAR 27/07/04

	Asura_Dialog* pxDialog = m_pxDialogList;
	while(pxDialog)
	{

		if(pxDialog->CanTakeTabFocus())
		{
			if(bAfterCurrent)
			{
				//This is the first acceptable control after the current one.. so go with it
				pxNewFocus = pxDialog;
				break;
			}
			else if(!pxNewFocus)
			{
				//This is the first acceptable control in the list... so keep track of it in case
				//no other is found after the current control
				pxNewFocus = pxDialog;
			}

		}

		if(pxDialog == pxCurrentFocus)
		{
			bAfterCurrent = true;
		}

		pxDialog = pxDialog->m_pxNextDialog;


	}
	if(pxNewFocus) pxNewFocus->TakeTabFocus();
}

Asura_Dialog* Asura_Overlay::GetCurrentFocusDialog()
{

	Asura_Gadget* pxFocusGadget = Asura_Overlay_System::GetFocusGadget();
	if(!pxFocusGadget) return 0;

	//search for the dialog owning this gadget
	Asura_Dialog* pxDialog = m_pxDialogList;
	while(pxDialog)
	{
		Asura_Gadget* pxGadget = pxDialog->m_pxGadgetList;
		while(pxGadget)
		{
			if(pxGadget == pxFocusGadget) return pxDialog;
			pxGadget = pxGadget->m_pxNextGadget;
		}

		pxDialog = pxDialog->m_pxNextDialog;
	}

	return 0;

}

void Asura_Overlay::SetCurrentParentDialog(Asura_Dialog* pxParentDialog)
{
	if(pxParentDialog)
	{
		if(m_iNumCurrentParentDialog)
		{
			Asura_Assert(0,"SetCurrentParentDialog should only be used to set a new tab dialog if none is currently set");
			return;
		}
		PushCurrentParentDialog(pxParentDialog);
	}
	else if(m_iNumCurrentParentDialog>0)
	{
		if(m_iNumCurrentParentDialog>1)
		{
			Asura_Assert(0,"SetCurrentParentDialog should only be used to clear the current tab dialog when only one has been set");			
			return;
		}
		PopCurrentParentDialog();
	}
}

void Asura_Overlay::PushCurrentParentDialog(Asura_Dialog* pxParentDialog)
{
	if(pxParentDialog == 0)
	{
		Asura_Assert(pxParentDialog,"Shouldn't be adding a null pointer via this function");
		return;
	}
	if(m_iNumCurrentParentDialog == ASURA_OVERLAY_MAX_NESTED_TAB_DIALOG)
	{
		Asura_Assert(0, "Too many nested tab dialogs");
		return;
	}

	Asura_Assert(pxParentDialog->m_pxOverlay == this,"Parent dialog must be added to this overlay first");


	m_apxCurrentParentDialog[m_iNumCurrentParentDialog] = pxParentDialog;
	m_iNumCurrentParentDialog++;
}

void Asura_Overlay::PopCurrentParentDialog()
{
	if(m_iNumCurrentParentDialog == 0)
	{
		Asura_Assert(0,"Stack of current tab dialogs is empty");
		return;
	}
	m_iNumCurrentParentDialog--;
	m_apxCurrentParentDialog[m_iNumCurrentParentDialog] = 0;
}


void Asura_Overlay::DeleteSingleDialog(Asura_Dialog* pxDialog)
{
	if(pxDialog == GetCurrentFocusDialog())
	{
		Asura_Overlay_System::ReleaseFocus();
	}

	//remove any references to the deleted dialog
	for(Asura_Dialog* pxDialogIt = m_pxDialogList; pxDialogIt; pxDialogIt = pxDialogIt->m_pxNextDialog)
	{
		pxDialogIt->RemoveReferencesToDialog(pxDialog);
	}

	pxDialog->DeleteChildDialogs();

	//search for the dialog in the list
	Asura_Dialog** ppxNextDialog = &m_pxDialogList;
	while(*ppxNextDialog && *ppxNextDialog != pxDialog)
	{
		ppxNextDialog =	&(*ppxNextDialog)->m_pxNextDialog;
	}

	if(!*ppxNextDialog)
	{
		Asura_Assert(0,"Failed to find the dialog being deleted");
		return;
	}

	//update the linked list to remove the deleted dialog
	*ppxNextDialog = pxDialog->m_pxNextDialog;
	pxDialog->m_pxNextDialog = 0;
    	
	delete pxDialog;
}

void Asura_Overlay::RadioButtonPressed(const class Asura_Gadget* pxPressedGadget)
{
	for(Asura_Dialog* pxDialog = m_pxDialogList;pxDialog;pxDialog = pxDialog->m_pxNextDialog)
	{
		pxDialog->RadioButtonPressed(pxPressedGadget);
	}
}

void Asura_Overlay::SetMinimised(bool bMinimise,bool bInstant)
{
	if(IsMinimised() != bMinimise)
	{
		m_uMinimisedState = bMinimise ? ASURA_OVERLAY_MINIMISED_ATBOTTOM : ASURA_OVERLAY_MINIMISED_NOTMINIMISED;
		if(bInstant)
		{
			//move instantly to the minimised/unminimised position
			if(m_uMinimisedState == ASURA_OVERLAY_MINIMISED_ATBOTTOM)
			{
				m_xOrigin.x = 0.0f;
				m_xOrigin.y = Asura_Overlay_System::GetMinimisedOverlayY();
			}
			else
			{
				m_xOrigin = m_xPosition;
			}
		}
	}
}

void Asura_Overlay::SetHideFlag(u_int uFlags,bool bSet)
{
	if(bSet)
	{
		m_uHideFlags |= uFlags;
	}
	else
	{
		m_uHideFlags &=~ uFlags;
	}
}

void Asura_Overlay::AddContextMenuItems(Tools_ContextMenu& xMenu, Tools_ContextMenu_Context& xContext)
{
	if(m_pfnAddContextMenuItems)
	{
		xContext.m_pOverlayUserData = m_pAddContextMenuItems_UserData;
		m_pfnAddContextMenuItems(xMenu, xContext);
	}
}

void Asura_Overlay::SetContextMenuCallback(void(*pfnAddContextMenuItems)(Tools_ContextMenu& xMenu, const Tools_ContextMenu_Context& xContext), void* pUserData)
{
	m_pfnAddContextMenuItems = pfnAddContextMenuItems;
	m_pAddContextMenuItems_UserData = pUserData;
}

void Asura_Overlay::UpdateExternalMinimisedState()
{
	if(m_puExternalMinimisedState) *m_puExternalMinimisedState = m_uMinimisedState;
	if(m_pbExternalMinimisedFlag) *m_pbExternalMinimisedFlag = IsMinimised();
}

u_int Asura_Overlay::GetAllowedResizeFlags() const
{
	u_int uFlags = 0;
	
	for(Asura_DialogIterator xIt(m_pxDialogList); !xIt.Done(); xIt.Next())
	{
		uFlags |= xIt.GetCurrent()->GetAllowedResizeFlags();
	}
	return uFlags;
}

void Asura_Overlay::GetResizeFlagsForCursorPosition(u_int& uOutputFlags, Asura_Dialog*& pxResizedDialog) const
{
	uOutputFlags = 0;
	pxResizedDialog = 0;

	//get the cursor position in pixels
	int iCursorX,iCursorY;
	Asura_Cursor::GetScreenSpaceCoords(iCursorX, iCursorY);
	Asura_Vector_2 xCursorPos((float)iCursorX, (float)iCursorY);

	const float fTol = 5.0f;
	
	if(m_bResizeUsingDialogBorder)
	{
		for(Asura_DialogIterator xIt(m_pxDialogList); !xIt.Done(); xIt.Next())
		{
			Asura_Dialog* pxDialog = xIt.GetCurrent();
			if(pxDialog->IsHidden()) continue;
			//This control resizable?
			//Can only do the max extents when doing it this way.
			u_int uAllowedFlags = pxDialog->GetAllowedResizeFlags() & (ASURA_OVERLAY_RESIZE_MAXX|ASURA_OVERLAY_RESIZE_MAXY);
			if(!uAllowedFlags) continue;

			Asura_Vector_2 xParentOffset = xIt.GetParentOffset();

			//get the max coords of the dialog in pixels
			float fMinX = (m_xPosition.x + pxDialog->m_xOffset.x + xParentOffset.x) * ASURA_TEXT_OUTPUT_FONT_WIDTH;
			float fMinY = (m_xPosition.y + pxDialog->m_xOffset.y + xParentOffset.y) * ASURA_TEXT_OUTPUT_FONT_HEIGHT;
			float fMaxX = (m_xPosition.x + pxDialog->m_xMaxCoord.x + xParentOffset.x) * ASURA_TEXT_OUTPUT_FONT_WIDTH;
			float fMaxY = (m_xPosition.y + pxDialog->m_xMaxCoord.y + xParentOffset.y) * ASURA_TEXT_OUTPUT_FONT_HEIGHT;

			u_int uFlags = 0;
			if(fMinX <= iCursorX && iCursorX < fMaxX+fTol && fMinY <= iCursorY && iCursorY < fMaxY+fTol)
			{
				if(iCursorX >= fMaxX && iCursorX < fMaxX+fTol && (uAllowedFlags & ASURA_OVERLAY_RESIZE_MAXX))
				{
					uFlags |= ASURA_OVERLAY_RESIZE_MAXX;
				}
				if(iCursorY >= fMaxY && iCursorY < fMaxY+fTol && (uAllowedFlags & ASURA_OVERLAY_RESIZE_MAXY))
				{
					uFlags |= ASURA_OVERLAY_RESIZE_MAXY;
				}
			}

			if(uFlags)
			{
				uOutputFlags = uFlags;
				pxResizedDialog = pxDialog;
				return;
			}

		}
	}
	else
	{
		//get the extents of the overlay in pixels
		Asura_Bounding_Rectangle xRect;
		xRect.MinX = m_xPosition.x * ASURA_TEXT_OUTPUT_FONT_WIDTH;
		xRect.MaxX = (m_xPosition.x + m_fWidth) * ASURA_TEXT_OUTPUT_FONT_WIDTH;
		xRect.MinY = m_xPosition.y * ASURA_TEXT_OUTPUT_FONT_HEIGHT;
		xRect.MaxY = (m_xPosition.y + m_fHeight) * ASURA_TEXT_OUTPUT_FONT_HEIGHT;


		if(!xRect.Contains(xCursorPos)) return;

		u_int uFlags = 0;
		if((xCursorPos.x - xRect.MaxX) >= -fTol)
		{
			uFlags |= ASURA_OVERLAY_RESIZE_MAXX;
		}
		else if((xCursorPos.x - xRect.MinX) < fTol)
		{
			uFlags |= ASURA_OVERLAY_RESIZE_MINX;
		}

		if((xCursorPos.y - xRect.MaxY) >= -fTol)
		{
			uFlags |= ASURA_OVERLAY_RESIZE_MAXY;
		}
		else if((xCursorPos.y - xRect.MinY) < fTol)
		{
			uFlags |= ASURA_OVERLAY_RESIZE_MINY;
		}

		uOutputFlags = uFlags & GetAllowedResizeFlags();
	}
}

void Asura_Overlay::UpdateDragAndResizeState()
{
	
	
	u_int uPotentialResizeFlags = 0;
	Asura_Dialog* pxPotentialResizedDialog = 0;
	
	if(!m_uMinimisedState)
	{
		if(m_bResizeUsingDialogBorder || m_bResizeUsingOverlayBorder)
		{
			GetResizeFlagsForCursorPosition(uPotentialResizeFlags, pxPotentialResizedDialog);
		}
	}

	
	if(Asura_UserInput::DebouncedKeyPressed(ASURA_KEYID_MOUSEBUTTON1) && HasCursorOnTop() && (pxPotentialResizedDialog || !IsCursorOverADialog()))
	{
		m_bIsBeingDragged = false;

		if(uPotentialResizeFlags)
		{
			StartResize(pxPotentialResizedDialog, uPotentialResizeFlags);
		}
		else if(m_bCanBeMovedByUser && !m_bOnPanel)
		{
			m_bIsBeingDragged = true;
		}
	}
	else if(!Asura_UserInput::KeyPressed(ASURA_KEYID_MOUSEBUTTON1))
	{
		//end drag/resize
		m_bIsBeingDragged = false;
		m_uResizeFlags = 0;
		m_pxResizedDialog = 0;
	}

	if(uPotentialResizeFlags || m_uResizeFlags)
	{
		SetResizeCursor(m_uResizeFlags ? m_uResizeFlags : uPotentialResizeFlags);
	}
}

void Asura_Overlay::StartResize(Asura_Dialog* pxDialogToResize, u_int uResizeFlags)
{
	m_pxResizedDialog = pxDialogToResize;
	m_uResizeFlags = uResizeFlags;
	m_bIsBeingDragged = 0;
	m_xResizeDelta = Asura_Maths::ZeroVector2;

	//prepare controls for resizing
	if(m_pxResizedDialog)
	{
		m_pxResizedDialog->StartResize(m_uResizeFlags);
	}
	else
	{
		for(Asura_DialogIterator xIt(m_pxDialogList); !xIt.Done(); xIt.Next())
		{
			xIt.GetCurrent()->StartResize(m_uResizeFlags);
		}
	}
}

void Asura_Overlay::UpdateResize()
{
	float fX,fY;
	Asura_Cursor::GetScreenDeltas(fX,fY);

	if(m_uResizeFlags & ASURA_OVERLAY_RESIZE_MINX)
	{
		m_xResizeDelta.x -= fX;
	}
	else if(m_uResizeFlags & ASURA_OVERLAY_RESIZE_MAXX)
	{
		m_xResizeDelta.x += fX;
	}

	if(m_uResizeFlags & ASURA_OVERLAY_RESIZE_MINY)
	{
		m_xResizeDelta.y -= fY;
	}
	else if(m_uResizeFlags & ASURA_OVERLAY_RESIZE_MAXY)
	{
		m_xResizeDelta.y += fY;
	}

	//allow the controls to respond to the resizing
	if(m_pxResizedDialog)
	{
		m_pxResizedDialog->UpdateResize(m_uResizeFlags, m_xResizeDelta);
	}
	else
	{
		for(Asura_DialogIterator xIt(m_pxDialogList); !xIt.Done(); xIt.Next())
		{
			xIt.GetCurrent()->UpdateResize(m_uResizeFlags, m_xResizeDelta);
		}
	}

	FlagForResize();
}

void Asura_Overlay::SetResizeCursor(u_int uResizeFlags)
{
	switch(uResizeFlags)
	{
		case ASURA_OVERLAY_RESIZE_MINX :
		case ASURA_OVERLAY_RESIZE_MAXX :
			Tools_WindowsCursor::SetCursor(TOOLS_CURSOR_RESIZE_H);
			break;
		case ASURA_OVERLAY_RESIZE_MINY :
		case ASURA_OVERLAY_RESIZE_MAXY :
			Tools_WindowsCursor::SetCursor(TOOLS_CURSOR_RESIZE_V);
			break;
		case ASURA_OVERLAY_RESIZE_MINX|ASURA_OVERLAY_RESIZE_MINY :
		case ASURA_OVERLAY_RESIZE_MAXX|ASURA_OVERLAY_RESIZE_MAXY :
			Tools_WindowsCursor::SetCursor(TOOLS_CURSOR_RESIZE_TL_BR);
			break;
		case ASURA_OVERLAY_RESIZE_MINX|ASURA_OVERLAY_RESIZE_MAXY :
		case ASURA_OVERLAY_RESIZE_MAXX|ASURA_OVERLAY_RESIZE_MINY :
			Tools_WindowsCursor::SetCursor(TOOLS_CURSOR_RESIZE_TR_BL);
			break;
		default :
			Asura_Assert(0,"Unexpected resize flag combination.");
			break;
	}
}

void Asura_Overlay::MakeDialogsRelativeToResizableDialog()
{
	//search for a resizable dialog
	Asura_Dialog* pxResizableDialog = 0;
	for(Asura_Dialog* pxDialog = m_pxDialogList; pxDialog; pxDialog = pxDialog->m_pxNextDialog)
	{
		if(pxDialog->GetAllowedResizeFlags() && !pxDialog->IsResizeDialog())
		{
			pxResizableDialog = pxDialog;
			break;
		}
	}
	if(!pxResizableDialog) return;

	//update all the dialogs before that in the list (which will have been created after it)
	for(Asura_Dialog* pxDialog = m_pxDialogList; pxDialog != pxResizableDialog; pxDialog = pxDialog->m_pxNextDialog)
	{
		if(!pxDialog->IsUsingRelativePosition() && !pxDialog->IsWindowsDialog())
		{
			pxDialog->SetRelativePosition(pxResizableDialog, true, false);
		}
	}

}


#endif // #ifdef ASURA_TOOLS
