///////////////////////////////////////////////////////////////////////////////
//
// File: Tools_LineEdit_Object.cpp
//
// Created by Martin G.Harris (15/9/2006)
// 
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
// Tools object for editing a line by positioning the start and end points.
// Currently these are used to set camera rails for the camera volume system,
// but they may have other applications.
//
///////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"
#include "Objects/Tools_LineEdit_Object.h"

#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Overlay.h"
#include "Interface/Asura_Overlay_System.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Text_Output.h"

#include "Tools_Overlays.h"
#include "Objects/Tools_Object_System.h"

// Constants
const float fTOOLS_LINEEDIT_COORD_MAX  = 9999.0f;
const float fTOOLS_LINEEDIT_COORD_STEP = 1.0f;

// Static data
Tools_LineEdit_Object* Tools_LineEdit_Object::s_pxThisLineEditObject = 0;
Asura_Vector_2 Tools_LineEdit_Object::s_xOverlayPosition(10.0f, 10.0f);

///////////////////////////
// Tools_LineEdit_Object //
///////////////////////////
Tools_LineEdit_Object::Tools_LineEdit_Object(Asura_Vector_3* pxStartPos,
											 Asura_Vector_3* pxEndPos,
											 const char* pszName /*= 0*/,
											 const char* pszStartMessage /*= 0*/,
											 const char* pszEndMessage /*= 0*/,
											 bool* pbModifiedFlag /*= 0*/)
: Tools_Object(TOT_LineEdit)
, m_uStartObject(0)
, m_uEndObject(0)
, m_uLineColour(0xffffffff)
, m_pxStartPos(pxStartPos)
, m_pxEndPos(pxEndPos)
, m_pbModifiedFlag(pbModifiedFlag)
, m_bUpdatedVectorDialog(false)
{
	// Create the object at the start of the line.
	Tools_LineEdit_EndPoint_Object* pxStartObject = new Tools_LineEdit_EndPoint_Object(pxStartPos, pszName, pszStartMessage, pbModifiedFlag, GetToolsID(), true);
	if (pxStartObject)
	{
		m_uStartObject = pxStartObject->GetID();
	}

	// Create the object at the other end of the line.
	Tools_LineEdit_EndPoint_Object* pxEndObject = new Tools_LineEdit_EndPoint_Object(pxEndPos, pszName, pszEndMessage, pbModifiedFlag, GetToolsID(), false);
	if (pxEndObject)
	{
		m_uEndObject = pxEndObject->GetID();
	}

	m_xName = pszName ? pszName : "Line Edit Object";

	// Set up mesh.
	Mesh = Asura_Mesh_System::GetMesh("ToolsShape_CutsceneTrackPoint");
}

Tools_LineEdit_Object::~Tools_LineEdit_Object()
{
	// Delete the start and end objects too.
	// Do this carefully to avoid recursive deletion problems.
	const u_int uStart = m_uStartObject;
	const u_int uEnd   = m_uEndObject;

	m_uStartObject = 0;
	m_uEndObject   = 0;

	Tools_PositionEdit_Object::Delete(uStart);
	Tools_PositionEdit_Object::Delete(uEnd);

	// Clear up overlays if not alrady done.
	OnDeselection();
}

////////////
// Render //
////////////
void Tools_LineEdit_Object::Render()
{
	PARENT::Render();

	// Render the line.
	if (m_pxStartPos && m_pxEndPos)
	{
		Asura_Render::RenderLine(*m_pxStartPos, *m_pxEndPos, m_uLineColour);
	}
}

///////////////////////////////////
// UpdateToolsPositionEditObject //
///////////////////////////////////
// Update the position of the tools object.
void Tools_LineEdit_Object::UpdateToolsPositionEditObject(u_int uID,
														  const Asura_Vector_3* pxPosition)
{
	if (!pxPosition) return;
	
	Tools_PositionEdit_Object* pxStartObject = Tools_PositionEdit_Object::Find(uID);
	if (pxStartObject)
	{
		pxStartObject->Position = *pxPosition;
	}
}


////////////
// Update //
////////////
void Tools_LineEdit_Object::Update()
{
	PARENT::Update();

	// Update the position of this tools object.
	if (m_pxStartPos && m_pxEndPos)
	{
		Position = (*m_pxStartPos + *m_pxEndPos)/2.0f;
	}

	if (m_bUpdatedVectorDialog)
	{
		// Vector numerical dialogue has been updated. Move the tools objects.
		UpdateToolsPositionEditObject(m_uStartObject, m_pxStartPos);	
		UpdateToolsPositionEditObject(m_uEndObject,   m_pxEndPos);

		// Set the flags.
		if (m_pbModifiedFlag)
		{
			*m_pbModifiedFlag = true;
		}
		m_bUpdatedVectorDialog = false;
	}

	// Line up all the position edit objects to point along the line from start to end.
	if (m_pxStartPos && m_pxEndPos)
	{
		Asura_Vector_3 xDirection = *m_pxEndPos - *m_pxStartPos;
		if (xDirection.Normalise())
		{
			// Set up middle object.
			Asura_Maths::OrientationFromDirection(Orientation, xDirection);

			// Set up start object.
			Tools_PositionEdit_Object* pxStartObject = Tools_PositionEdit_Object::Find(m_uStartObject);
			if (pxStartObject)
			{
				pxStartObject->Orientation = Orientation;
			}

			// Set up end object.
			Tools_PositionEdit_Object* pxEndObject = Tools_PositionEdit_Object::Find(m_uEndObject);
			if (pxEndObject)
			{
				pxEndObject->Orientation = Orientation;
			}
		}
	}
}

///////////////
// PrintInfo //
///////////////
void Tools_LineEdit_Object::PrintInfo(float& fX, float& fY)
{
	PARENT::PrintInfo(fX, fY);
	Asura_Text_Output::Print(fX, fY++, 0xffffffff, 0, "Delete this object when finished");
}

///////////////////
// OnReselection //
///////////////////
void Tools_LineEdit_Object::OnReselection()
{
	OnSelection();
}

/////////////////
// OnSelection //
/////////////////
void Tools_LineEdit_Object::OnSelection()
{
	// On selection, select both the start and end points.
	Tools_PositionEdit_Object* pxEnd = Tools_PositionEdit_Object::Find(m_uEndObject);
	if (pxEnd)
	{
		if (!Tools_Object_System::SelectedObjectList.contains(pxEnd))
		{
			Tools_Object_System::SelectObject(pxEnd, true);
		}
	}
	Tools_PositionEdit_Object* pxStart = Tools_PositionEdit_Object::Find(m_uStartObject);
	if (pxStart)
	{
		if (!Tools_Object_System::SelectedObjectList.contains(pxStart))
		{
			Tools_Object_System::SelectObject(pxStart, true);
		}
	}

	// Reselect this.
	if (Tools_Object_System::SelectedObject != this)
	{
		Tools_Object_System::SelectObject(this, true);
	}

	if ( !Asura_Overlay_System::DoesOverlayExist( TOOLS_OVERLAY_LINE_EDIT ) )
	{
		CreateOverlay();
		s_pxThisLineEditObject = this;
	}
}

///////////////////
// OnDeselection //
///////////////////
void Tools_LineEdit_Object::OnDeselection()
{
	if (s_pxThisLineEditObject == this)
	{
		s_pxThisLineEditObject = 0;
		if ( Asura_Overlay_System::DoesOverlayExist(TOOLS_OVERLAY_LINE_EDIT) )
		{
			DeleteOverlay();
		}
	}
}

///////////////////
// CreateOverlay //
///////////////////
void Tools_LineEdit_Object::CreateOverlay()
{
	Asura_Overlay* pxOverlay = new Asura_Overlay(GetTypeDescription(), false, &s_xOverlayPosition);
	if (!pxOverlay) return;

	Asura_Vector_2 xOffset = Asura_Vector_2(1.0f,3.0f);

	AddVectorDialogToOverlay(	pxOverlay,
								m_pxStartPos,
								"Start",
								xOffset,
								-fTOOLS_LINEEDIT_COORD_MAX, fTOOLS_LINEEDIT_COORD_MAX,
								fTOOLS_LINEEDIT_COORD_STEP,
								"%.2f",
								&m_bUpdatedVectorDialog );

	AddVectorDialogToOverlay(	pxOverlay,
								m_pxEndPos,
								"End",
								xOffset,
								-fTOOLS_LINEEDIT_COORD_MAX, fTOOLS_LINEEDIT_COORD_MAX,
								fTOOLS_LINEEDIT_COORD_STEP,
								"%.2f",
								&m_bUpdatedVectorDialog );
	
	pxOverlay->m_uID = TOOLS_OVERLAY_LINE_EDIT;
	pxOverlay->m_xOrigin = s_xOverlayPosition;

	// Allow any objects inheriting off this to add extra items to the overlay.
	AddToOverlay(pxOverlay, xOffset);

 	Asura_Overlay_System::SubmitNewOverlay(pxOverlay, false);
}

///////////////////
// DeleteOverlay //
///////////////////
void Tools_LineEdit_Object::DeleteOverlay()
{
	Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_LINE_EDIT);
}

//////////////////////////////
// AddVectorDialogToOverlay //
//////////////////////////////
void Tools_LineEdit_Object::AddVectorDialogToOverlay(   Asura_Overlay* pxOverlay,
														Asura_Vector_3* pxVector,
														const char* szName,
														Asura_Vector_2& xOffset,
														float fMin, float fMax, float fDelta,
														const char* szPrintFormat,
														bool* pbChanged /*= 0*/ )
{
	if (!pxVector || !pxOverlay) return;

	Asura_Dialog* pxDialogX = new Asura_Numerical_Dialog("X",
											xOffset,
											&pxVector->x,
											fMin, fMax, fDelta,
											szPrintFormat,
											pbChanged);
	pxOverlay->AddDialog(pxDialogX);

	const Asura_Vector_2 xRelOffsetY(pxDialogX->GetMaxCoords().x + Asura_Dialog::SmallSpacing(), xOffset.y);

	Asura_Dialog* pxDialogY = new Asura_Numerical_Dialog("Y",
											xRelOffsetY,
											&pxVector->y,
											fMin, fMax, fDelta,
											szPrintFormat,
											pbChanged);
	
	pxOverlay->AddDialog(pxDialogY);

	const Asura_Vector_2 xRelOffsetZ(pxDialogY->GetMaxCoords().x + Asura_Dialog::SmallSpacing(), xOffset.y);

	Asura_Dialog* pxDialogZ = new Asura_Numerical_Dialog("Z",
											xRelOffsetZ,
											&pxVector->z,
											fMin, fMax, fDelta,
											szPrintFormat,
											pbChanged);
	pxOverlay->AddDialog(pxDialogZ);

	const Asura_Vector_2 xRelOffsetName(pxDialogZ->GetMaxCoords().x + Asura_Dialog::SmallSpacing(), xOffset.y);

	Asura_Dialog* pxDialogName = new Asura_StringDisplay_Dialog(szName, xRelOffsetName);
	pxOverlay->AddDialog(pxDialogName);

	xOffset.y += Asura_Dialog::DefaultSpacing();
}


////////////////////////
// SubObjectDestroyed //
////////////////////////
// A sub-object has been destroyed, deal with it.
void Tools_LineEdit_Object::SubObjectDestroyed(const int iParent, const u_int uDestroyed)
{
	Tools_LineEdit_Object* pxParent = Tools_LineEdit_Object::Find(iParent);
	if (pxParent)
	{
		// Do this carefully to avoid recursive deletion problems.
		bool bFound = false;
		if (uDestroyed == pxParent->m_uEndObject)
		{
			bFound = true;
			pxParent->m_uEndObject = 0;
		}
		else if (uDestroyed == pxParent->m_uStartObject)
		{
			bFound = true;
			pxParent->m_uStartObject = 0;
		}
		if (bFound)
		{
			// One of our sub-objects has been deleted, we should delete ourselves too.
			Tools_Object_System::DeleteObject(pxParent);
		}
	}
}

//////////
// Find //
//////////
// Find the line edit object with this ID, if it still exists.
Tools_LineEdit_Object* Tools_LineEdit_Object::Find(const int iToolsID)
{
	if (!iToolsID) return 0;

	for (LIF<Tools_Object*> xObjectLIF(&Tools_Object_System::ObjectList); !xObjectLIF.done(); xObjectLIF.next())
	{
		if (xObjectLIF()->Type != TOT_LineEdit) continue;

		Tools_LineEdit_Object* pxObject = static_cast<Tools_LineEdit_Object*>(xObjectLIF());

		if (pxObject->GetToolsID() == iToolsID) return pxObject;
	}

	return 0;
}

////////////
// Delete //
////////////
// Deletes the line edit object with this ID, if it still exists.
void Tools_LineEdit_Object::Delete(const int iToolsID)
{
	// Find the object and kill it.
	Tools_LineEdit_Object* pxObject = Find(iToolsID);
	if (pxObject)
	{
		Tools_Object_System::DeleteObject(pxObject);
	}
}

///////////////////////////////////////////////////////////////////////////////
// Tools_LineEdit_EndPoint_Object
///////////////////////////////////////////////////////////////////////////////
///////////////
// PrintInfo //
///////////////
void Tools_LineEdit_EndPoint_Object::PrintInfo(float& fX, float& fY)
{
	PARENT::PrintInfo(fX, fY);
	fY++;

	if (m_pxPosition)
	{
		const char* szText = (m_bStart ? "Start" : "  End");
		Asura_Text_Output::Print(fX, fY++, 0xffffffff, 0, "%s: %+.2f, %+.2f, %+.2f", szText,
								m_pxPosition->x, m_pxPosition->y, m_pxPosition->z);
	}
}