#include "Core/Asura.h"

#include "Cutscene/Tools_Cutscene_Track.h"

#include "Cutscene/Asura_Cutscene_Track.h"
#include "Cutscene/Asura_Cutscene_Actor.h"
#include "Interface/Asura_Overlay_System.h"
#include "Interface/Asura_Overlay.h"
#include "Interface/Asura_Dialog.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Camera/Asura_Camera.h"
#include "Core/Asura_Timers.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_Viewport.h"
#include "Rendering/Asura_Direct3D.h"
#include "Rendering/Asura_Direct3D_Shaders.h"
#include "Animation/Asura_Animation.h"
#include "Animation/Asura_Hierarchy.h"
#include "Core/Asura_UserInput.h"

#include "Load&Save/Tools_SaveAsura.h"
#include "Cutscene/Tools_CutsceneActor_Object.h"
#include "Objects/Tools_Object_System.h"
#include "Tools_Overlays.h"
#include "Misc/Tools_Timed_Messages.h"
#include "Cutscene/Tools_Cutscene_Dialog.h"
#include "Cutscene/Tools_Mode_Cutscene.h"
#include "Cutscene/Tools_Cutscene_Timeline.h"
#include "Cutscene/Tools_Cutscene_Object.h"
#include "Interface/Tools_ContextMenu_System.h"
#include "Objects/Tools_ObjectSystem_Param.h"


#include "Debugging/Asura_Assert.h"

TOOLS_DECLARE_SHARED_PARAM(bool,	0x93D71AD4,	TrackPoint_Straight)
TOOLS_DECLARE_SHARED_PARAM(bool,	0x02115E0E,	TrackPoint_Stationary)


//////////////////////////////////////////////////////////////////////////////////////////////////
/*----------------------**
** Tools_Cutscene_Track **
**----------------------*/
int Tools_Cutscene_Track::s_iNextTrackID = 1;
static int g_iPreviousScreenWidth = 0;//used for detecting changes in screen size



Tools_Cutscene_Track::Tools_Cutscene_Track(Asura_Cutscene_Track* pxTrack,Tools_CutsceneActor_Object* pxActorObject,int iTrackID)
{

	m_bAsuraTrackInvalid = false;
	m_pxTrack = pxTrack;
	m_pxActorObject = pxActorObject;
	m_bBeingDestroyed = false;
	m_iTrackID = iTrackID ? iTrackID : s_iNextTrackID++;
	m_uTrackFlagsBackup = m_pxTrack->m_uTrackFlags;

    m_bIsActorPositionTrack = (iTrackID < 0) ? false : true;

	m_iNumRenderVertices = 0;
	m_pusRenderIndeces = 0;
	m_pxRenderVertices = 0;
	m_uRenderFrameStamp = 0;

	m_bSelected = false;

    //create track point objects for each point in the track
	int numberOfPoints = m_pxTrack->GetNumberOfPoints();
	Asura_Vector_3* points = m_pxTrack->GetPoints();
	for(int i = 0; i < numberOfPoints; i++)
	{
		Tools_CutsceneTrackPoint_Object* pxTrackPoint = new Tools_CutsceneTrackPoint_Object(points[i],pxTrack->PointArrivalTimes[i],pxTrack->PointFlags[i],this);
		m_xPointObjects.add_entry(pxTrackPoint);

		pxTrack->GetOrientationAtPoint(i,pxTrackPoint->Orientation);
		pxTrackPoint->OrientationBackup = pxTrackPoint->Orientation;
		pxTrackPoint->Duration = pxTrack->PointDurations ? pxTrack->PointDurations[i] : 0;
		pxTrackPoint->DurationBackup = pxTrack->PointDurations ? pxTrack->PointDurations[i] : 0;
	}
	
	UpdatePointIndeces();

	//add to the parents list of tracks
    if (m_bIsActorPositionTrack && m_pxActorObject)
    {
		m_pxActorObject->Tracks.add_entry(this);

    }

	m_bSetCutscenesModified = true;
}

Tools_Cutscene_Track::~Tools_Cutscene_Track()
{
	while(m_xPointObjects.size())
	{
		Tools_Object_System::DeleteObject(m_xPointObjects.first_entry());
	}

	if( m_pxActorObject )
	{
		m_pxActorObject->NotifyToolsTrackDeleted(this);
	}

	delete [] m_pusRenderIndeces;
	delete [] m_pxRenderVertices;
}

void Tools_Cutscene_Track::UpdatePointIndeces()
{
	int index = 0;
	for(LIF<Tools_CutsceneTrackPoint_Object*> plif(&m_xPointObjects);!plif.done();plif.next())
	{
		plif()->Index = index++;
	}
}

// (AAM - 22.10.2004) Changed return type. Now returns true if the track needed updating, false o/w
bool Tools_Cutscene_Track::UpdateAsuraTrack()
{
	if(!m_bAsuraTrackInvalid) return false;
	if(m_bBeingDestroyed) return false;
	Tools_Cutscene_Timeline::Invalidate();
	Tools_Mode_Cutscene::RefreshPlayback();

	//make sure the points are sorted by time
	SortPointList();

	if(m_pxTrack->m_uTrackFlags & TrackFlag_Circular)
	{
		//make sure the last point is snapped to the first point
		m_xPointObjects.last_entry()->Position = m_xPointObjects.first_entry()->Position;
		m_xPointObjects.last_entry()->Orientation = m_xPointObjects.first_entry()->Orientation; 
	}

	LIF<Tools_CutsceneTrackPoint_Object*> plif(&m_xPointObjects);

	if(m_pxTrack->m_uTrackFlags != m_uTrackFlagsBackup)
	{
		if(CutsceneUndo) CutsceneUndo->Log_Track_Changed(this);
	}

	//check for any changes that need to be logged for undo
	for(;!plif.done();plif.next())
	{
		if(plif()->BackupVariablesAreDifferent())
		{
			if(CutsceneUndo) CutsceneUndo->Log_TrackPoint_Changed(plif());
		}
		//update enabled status on overlay controls
		plif()->UpdateOverlayControls();
	}

	//build an array of points
	Asura_Vector_3* points = new Asura_Vector_3[m_xPointObjects.size()];
	for(plif.restart();!plif.done();plif.next())
	{
		points[plif()->Index] = plif()->Position;
	}

	//update the Asura_Track with these points
	m_pxTrack->SetPoints(m_xPointObjects.size(),points);
	delete [] points;

	bool bNeedDurationArray = false;
	for(plif.restart();!plif.done();plif.next())
	{
		m_pxTrack->PointArrivalTimes[plif()->Index] = plif()->Time;
		m_pxTrack->SetOrientationAtPoint(plif()->Index,plif()->Orientation);
		if(plif()->SectionStraight)
		{
			m_pxTrack->PointFlags[plif()->Index] |= TrackPointFlag_SectionStraight;
		}
		if(plif()->m_bStationaryFlag)
		{
			m_pxTrack->PointFlags[plif()->Index] |= TrackPointFlag_Stationary;
		}
		if(plif()->Duration) bNeedDurationArray = true;
	}

	if(bNeedDurationArray)
	{
		Asura_ResourcePushType(ASURA_RESOURCE_CUTSCENE);
		{
			//allocate duration array , and fill in
			Asura_ResourceAllocateArray(m_pxTrack->PointDurations, m_xPointObjects.size());
			for(plif.restart();!plif.done();plif.next())
			{
				m_pxTrack->PointDurations[plif()->Index] = plif()->Duration;
			}
		}
		Asura_ResourcePopType();
	}
	

	m_pxTrack->CalculateLengths();

	if( m_bSetCutscenesModified )
	{
		Tools_SaveAsura::SetCutscenesModified();
	}

	//invalidate the render data
	m_iNumRenderVertices = 0;
	delete [] m_pusRenderIndeces;
	m_pusRenderIndeces = 0;
	delete [] m_pxRenderVertices;
	m_pxRenderVertices = 0;

	//invalidate the render data
	m_iNumRenderVertices = 0;
	delete [] m_pusRenderIndeces;
	m_pusRenderIndeces = 0;
	delete [] m_pxRenderVertices;
	m_pxRenderVertices = 0;

	m_bAsuraTrackInvalid = false;

	return true;
} 

Tools_CutsceneTrackPoint_Object* Tools_Cutscene_Track::AddPoint(int index,bool bUsingAddBefore,const Asura_Vector_3* pxPos /*= 0*/, int iPointID /*=0*/)
{
	Asura_Assert(index>=0 && index<=m_xPointObjects.size(),"");

	const Asura_Matrix_3x3* pxMat = 0;
	if(!pxPos)
	{
		pxPos = Asura_Camera::GetPosition();
		//use the orientation from the camera
		pxMat = Asura_Camera::GetOrientation();
	}
	//create a new track point at the camera
 	Tools_CutsceneTrackPoint_Object* pointObject = new Tools_CutsceneTrackPoint_Object(*pxPos,0,0,this,iPointID);
	if(pxMat) pointObject->Orientation = *pxMat;

	//insert the point into the list at the appropriate point
	Tools_CutsceneTrackPoint_Object* pxPrevPoint = 0;

	float time = 0;
	LIF<Tools_CutsceneTrackPoint_Object*> plif(&m_xPointObjects);
	for(;!plif.done();plif.next())
	{
		if(plif()->Index>=index)
		{
			if(!pxPrevPoint)
			{
				//first point in track... see if there is a previous track to get the previous point from
				int iTrackIndex = m_bIsActorPositionTrack ? GetTrackIndex() : 0;
				if(iTrackIndex > 0)
				{
					Tools_Cutscene_Track* pxPrevTrack = m_pxActorObject->Tracks[iTrackIndex-1];
					Asura_Assert(pxPrevTrack->m_xPointObjects.size(),"");

					pxPrevPoint = pxPrevTrack->m_xPointObjects.last_entry();
				}
			}

			if(bUsingAddBefore)
			{
				//put one second before the point that this was being added before
				time = plif()->Time - 1.0f;
				if(time < 0.0f) time = 0.0f;
				if(pxPrevPoint && time < pxPrevPoint->Time) time = pxPrevPoint->Time;
			}
			else
			{
				//put one second after the point that this was being added after
				time = pxPrevPoint ? (pxPrevPoint->Time + 1.0f) : 0.0f;
				if(time > plif()->Time) time = plif()->Time;
			}
			
			m_xPointObjects.add_entry_before(pointObject,plif());
			break;
		}
		pxPrevPoint = plif();
	}
	if(plif.done())
	{
		time = pxPrevPoint ? pxPrevPoint->Time+1.0f : 0.0f;
		m_xPointObjects.add_entry(pointObject);
	}
	

	pointObject->Time = time;

	//correct the indeces
	UpdatePointIndeces();

	//log the creation of this point
	if(CutsceneUndo) CutsceneUndo->Log_TrackPoint_Created(pointObject);

	//and select the new object
	if(!CutsceneUndo || !CutsceneUndo->IsUndoInProcess())
	{
		Tools_Object_System::SelectObject(pointObject);
	}

	m_bAsuraTrackInvalid = true;

	if( m_bSetCutscenesModified )
	{
		Tools_SaveAsura::SetCutscenesModified();
	}

	return pointObject;
}


void Tools_Cutscene_Track::Destroy()
{
	if(m_bBeingDestroyed) return;
	m_bBeingDestroyed = true;//avoid problems from this funciton being called again

	Tools_Mode_Cutscene::RefreshPlayback();
	
	//destroy any remaining points in the track
	while(m_xPointObjects.size())
	{
		m_xPointObjects.first_entry()->Destroy();
	}

	if(CutsceneUndo) CutsceneUndo->Log_Track_Destroyed(this);

	//remove this track from the actor , and delete
	if( m_pxActorObject )
	{
		m_pxActorObject->RemoveTrackFromActor(m_pxTrack);
	}

	const bool bSetCutscenesModified = m_bSetCutscenesModified;

	delete m_pxTrack;
	m_pxTrack = NULL;

	delete this;

	if( bSetCutscenesModified )
	{
		Tools_SaveAsura::SetCutscenesModified();
	}
}

void Tools_Cutscene_Track::Rename(const char* name /*= NULL*/)
{
	Asura_String trackName;
	if(name)
	{
		trackName = name;
	}
	else
	{
		//ask for a new name
   		GetString_Overlay overlay("Rename track",m_pxTrack->TrackName.GetString());
		if(!overlay.DoModal()) return;
		trackName = overlay.GetString() ? overlay.GetString() : "";
	}

	if(CutsceneUndo) CutsceneUndo->Log_Track_Rename(this);

	m_pxTrack->TrackName = trackName;

	//if one of the points in this track is selected , then we need to update the overlay
	for(LIF<Tools_CutsceneTrackPoint_Object*> plif(&m_xPointObjects);!plif.done();plif.next())
	{
		if(plif()->IsSelected())
		{
			plif()->DeleteOverlays();
			plif()->CreateOverlays();
			break;
		}
	}

	if( m_bSetCutscenesModified )
	{
		Tools_SaveAsura::SetCutscenesModified();
	}

	if (m_bIsActorPositionTrack)
    {
        if(!name &&  trackName.GetLength() && GetTrackIndex()==0 && m_pxActorObject)
	    {
		    //ask if the actor should be renamed as well
		    YesNo_Overlay overlay("Would you like to give the actor the same name?");
		    if(overlay.DoModal())
		    {
			    m_pxActorObject->Rename(trackName.GetString());
		    }
	    }
    }
}

void Tools_Cutscene_Track::SetTimeForPoints(int firstIndex,int lastIndex,float finalTime)
{
	if(firstIndex>=lastIndex) return;
	//make sure asura_track is up to data
	UpdateAsuraTrack();

	//get distance between the points
	float totalDistance = m_pxTrack->GetDistanceAtPoint(lastIndex) - m_pxTrack->GetDistanceAtPoint(firstIndex);
	if(totalDistance<=0) totalDistance = 1;

	//get the difference in time
	float startTime = m_pxTrack->PointArrivalTimes[firstIndex];
	if(m_pxTrack->PointDurations) startTime += m_pxTrack->PointDurations[firstIndex];
	float timeDiff = finalTime - startTime;
	if(timeDiff<0)  timeDiff = 0;

	//calculate times for intermediate points
	for(int i= firstIndex+1;i<lastIndex;i++)
	{
		float distance = m_pxTrack->GetDistanceAtPoint(i)-m_pxTrack->GetDistanceAtPoint(firstIndex);
		m_pxTrack->PointArrivalTimes[i] = startTime+(distance/totalDistance)*timeDiff;
		if(m_pxTrack->PointDurations) m_pxTrack->PointDurations[i] = 0;
	}
	m_pxTrack->PointArrivalTimes[lastIndex] = finalTime;

	//update times stored in the track points
	for(LIF<Tools_CutsceneTrackPoint_Object*> plif(&m_xPointObjects);!plif.done();plif.next())
	{
		plif()->Time = m_pxTrack->PointArrivalTimes[plif()->Index];
		if(m_pxTrack->PointDurations)
		{
			plif()->Duration = m_pxTrack->PointDurations[plif()->Index];
		}
			
	}
	m_bAsuraTrackInvalid = true;

	if( m_bSetCutscenesModified )
	{
		Tools_SaveAsura::SetCutscenesModified();
	}

	Tools_Cutscene_Timeline::Invalidate();
}

void Tools_Cutscene_Track::RenderSpline()
{
	//make sure not already rendered this frame
	u_int uFrame = Asura_Timers::GetFrameNumber();
	if(uFrame == m_uRenderFrameStamp) return;
	m_uRenderFrameStamp = uFrame;

	UpdateAsuraTrack();

	if(!m_iNumRenderVertices)
	{
		//need to recalculate the vertex positions for the line
		float length = m_pxTrack->GetLength();
		if(!length) return;

		//determine the number of vertices to draw
		m_iNumRenderVertices = (int)(length/.2f)+2;

		m_iNumRenderVertices = Asura_Maths::Min(m_iNumRenderVertices,65000);

		m_pusRenderIndeces = new u_short[m_iNumRenderVertices];
		m_pxRenderVertices = new Tools_D3DTrackVertex[m_iNumRenderVertices];

		//initialise the positions and indeces
		for(int i=0;i<m_iNumRenderVertices;i++)
		{
			m_pxTrack->GetPositionFromDistance(length*(i+1)/m_iNumRenderVertices,&m_pxRenderVertices[i].xPos);
			m_pxRenderVertices[i].xPos.y-=.01f;//raise slightly so that tracks on the ground are visible

			m_pusRenderIndeces[i] = (u_short)i;
		}
	}

	//update the vertex colours
	for( int i = 0; i < m_iNumRenderVertices; i++ )
	{
		//pick a colour
		float fCol1 = ( Asura_Maths::Sine( i / 5.0f - Asura_Timers::GetLocalUnscaledTime() * 5.0f ) );
		fCol1 = (fCol1+1)*127.0f;
		float fCol2 = 255.0f-fCol1;

		if(m_bSelected)
		{
			// red/green
			m_pxRenderVertices[i].uColour = 0xff000000+(Asura_Maths::FloatToInt(fCol1)<<16)+(Asura_Maths::FloatToInt(fCol2)<<8);
		}
		else
		{
			// blue/green
			m_pxRenderVertices[i].uColour = 0xff000000+(Asura_Maths::FloatToInt(fCol1)<<0)+(Asura_Maths::FloatToInt(fCol2)<<8);
		}
	}


	//render the lines
	Asura_Render::CurrentStates.RequestTextureOpMode(ASURA_TEXTUREOP_SINGLE_PASS);
	Asura_Render::CurrentStates.RequestTextureIndex(0,ASURA_TEXTURE_NONE);

	Asura_Direct3D_Shaders::RequestFixedFunctionVertexShader(D3DFVF_XYZ|D3DFVF_DIFFUSE);


	Asura_Direct3D::D3DDevice->DrawIndexedPrimitiveUP
		(
		D3DPT_LINESTRIP,
		0,
		m_iNumRenderVertices,
		m_iNumRenderVertices-1,
		(void*)m_pusRenderIndeces,
		D3DFMT_INDEX16,
		(void*)m_pxRenderVertices,
		sizeof(Tools_D3DTrackVertex)
		);


}

int Tools_Cutscene_Track::GetTrackIndex()
{
    Asura_Assert(m_bIsActorPositionTrack, "GetTrackIndex() called for track which is not in track array");

	//work out which track in the array this is
	int index = 0;

	if( m_pxActorObject )
	{
		for(LIF<Tools_Cutscene_Track*> tlif(&m_pxActorObject->Tracks);!tlif.done();tlif.next(),index++)
		{
			if(tlif() == this) break;
		}
		Asura_Assert(index<m_pxActorObject->Tracks.size(),"");
	}

	return index;
}

void Tools_Cutscene_Track::SortPointList()
{
	//Search for any points that are out of order
	Tools_CutsceneTrackPoint_Object* pxPrevPoint = 0;
	bool bAnyChange = false;
	for(LIF<Tools_CutsceneTrackPoint_Object*> xPointLIF(&m_xPointObjects); !xPointLIF.done();)
	{
		Tools_CutsceneTrackPoint_Object* pxPoint = xPointLIF();
		if(pxPrevPoint && pxPrevPoint->Time > pxPoint->Time)
		{
			//This point should be earlier than the previous.
			//The list up to this point should be sorted , so remove this point and search the start of the 
			//list again for the correct place to insert it
			xPointLIF.delete_current();
			for(LIF<Tools_CutsceneTrackPoint_Object*> xInsertPointLIF(&m_xPointObjects); !xInsertPointLIF.done(); xInsertPointLIF.next())
			{
				if(pxPoint->Time < xInsertPointLIF()->Time)
				{
					m_xPointObjects.add_entry_before(pxPoint, xInsertPointLIF());
					break;
				}
			}
			bAnyChange = true;
		}
		else
		{
			pxPrevPoint = pxPoint;
			xPointLIF.next();
		}
	}

	if(bAnyChange)
	{
		//Update the point indexes
		int iIndex = 0;
		for(LIF<Tools_CutsceneTrackPoint_Object*> xPointLIF(&m_xPointObjects); !xPointLIF.done(); xPointLIF.next())
		{
			xPointLIF()->Index = iIndex++;
		}

	}
}


//////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------------**
** Tools_CutsceneTrackPoint_Object **
**---------------------------------*/

//static member variables
Asura_Vector_2					Tools_CutsceneTrackPoint_Object::OverlayPosition = Asura_Vector_2(3.0f,20.0f);
Tools_CutsceneTrackPoint_Object*     Tools_CutsceneTrackPoint_Object::ThisPoint = NULL; 
bool Tools_CutsceneTrackPoint_Object::OverlayOpen = true;
int Tools_CutsceneTrackPoint_Object::s_iNextPointID = 1;


Tools_CutsceneTrackPoint_Object::Tools_CutsceneTrackPoint_Object(const Asura_Vector_3& xPosition,float fTime,int iPointFlags,Tools_Cutscene_Track* pxTrack, int iPointID /*=0*/)
:Tools_Object(TOT_CutsceneTrackPoint)
{
	Position = xPosition;
	Orientation.Identity();
	m_iPointID = iPointID ? iPointID : s_iNextPointID++;
	Track = pxTrack;
	Index = -1;
	Time = fTime;
	Duration = 0;
	m_uTimelineSelectionFlags = 0;
	SectionStraight = (iPointFlags & TrackPointFlag_SectionStraight) ? true : false;
	m_bStationaryFlag = (iPointFlags & TrackPointFlag_Stationary) ? true : false;
	m_bSelectedIndexChanged = false;

	SetMesh("ToolsShape_CutsceneTrackPoint");

	m_bSetCutscenesModified = true;

	UpdateBackupVariables();
}


Tools_CutsceneTrackPoint_Object::~Tools_CutsceneTrackPoint_Object()
{
	Track->m_xPointObjects.delete_entry(this);
}


void Tools_CutsceneTrackPoint_Object::Render()
{
	Track->RenderSpline();
	
	Tools_Object::Render();
}






void Tools_CutsceneTrackPoint_Object::UpdatePositionInAsuraData(bool updateLinkedObjects /*=true*/)
{
	Track->m_bAsuraTrackInvalid = true;

	if( m_bSetCutscenesModified )
	{
		Tools_SaveAsura::SetCutscenesModified();
	}

	if(!updateLinkedObjects) return;

	//If a circular track , make sure first and last points are the same
	if(Track->m_pxTrack->m_uTrackFlags & TrackFlag_Circular)	
	{
		Tools_CutsceneTrackPoint_Object* pxOtherPoint = 0;
		if(IsFirstPoint())
		{
			pxOtherPoint = Track->m_xPointObjects.last_entry();
		}
		else if (IsLastPoint())
		{
			pxOtherPoint = Track->m_xPointObjects.first_entry();
		}
		
		if(!pxOtherPoint) return;

		if( this!=pxOtherPoint && !(CutsceneUndo && CutsceneUndo->IsUndoInProcess()) )
		{
			pxOtherPoint->Position = Position;
			pxOtherPoint->Orientation = Orientation;
			pxOtherPoint->UpdatePositionInAsuraData(false);
		}

	}
	else if(!IsFirstPoint())
	{
		return;
	}

	//if this is the first point in the first track ,snap the actor object to the same position
    if (!Track->m_bIsActorPositionTrack) return;
	if(Track->GetTrackIndex()) return;


	if( Track->m_pxActorObject )
	{
		Tools_CutsceneActor_Object* actorObject = Track->m_pxActorObject;

		//except possibly during cutscene playback
		if(Tools_Mode_Cutscene::s_pxThis->CutsceneIsPlaying())
		{
			if(!actorObject->IsHierarchyActor() && !actorObject->IsShapeActor()) return;
		}

		actorObject->Position = Position;
		actorObject->Orientation = Orientation;
		actorObject->UpdatePositionInAsuraData(false);
	}
}

const char* Tools_CutsceneTrackPoint_Object::GetName()
{
	static char buffer[100];
	if(Track->m_pxTrack->TrackName.GetString())
		sprintf(buffer,"%s : Point%d",Track->m_pxTrack->TrackName.GetString(),Index);
	else
		sprintf(buffer,"Point%d",Index);

	return buffer;
}

void Tools_CutsceneTrackPoint_Object::Destroy()
{
	
	//log the destruction of this point
	if(CutsceneUndo) CutsceneUndo->Log_TrackPoint_Destroyed(this);
	
	//need to take a copy of 'Track' , to do some stuff after this is deleted
	Tools_Cutscene_Track* track = Track;
	const bool bSetCutscenesModified = m_bSetCutscenesModified;

	Tools_Object::Destroy();

	track->m_bAsuraTrackInvalid = true;
	track->UpdatePointIndeces();

	if(!CutsceneUndo || !CutsceneUndo->IsUndoInProcess())
	{
		if(!track->m_xPointObjects.size())
		{
			track->Destroy();
		}
	}

	if( bSetCutscenesModified )
	{
		Tools_SaveAsura::SetCutscenesModified();
	}
}

void Tools_CutsceneTrackPoint_Object::OnSelection()
{
	//show the overlay for this object
	Track->m_bSelected = true;
	ThisPoint = this;
	CreateOverlays();
}

void Tools_CutsceneTrackPoint_Object::OnDeselection()
{
	//get rid of the overlay
	DeleteOverlays();
	ThisPoint = NULL;
	Track->m_bSelected = false;
}

void Tools_CutsceneTrackPoint_Object::UpdateWhileSelected()
{
	if(!Asura_Overlay_System::OverlaysHaveKeyboardFocus())
	{
		//P : Add point after/before current point
		if (Asura_UserInput::DebouncedKeyPressed('p'))
		{
			if(Asura_UserInput::KeyPressed(ASURA_KEYID_SHIFT))
			{
				AddPointBefore();
			}
			else
			{
				AddPointAfter();
			}
		}

		//I : Align track point to track
		if (Asura_UserInput::DebouncedKeyPressed('i'))
		{
			AlignWithTrack();
		}
	
	}

	int iScreenWidth = Asura_Viewport::GetWidth();
	if(iScreenWidth != g_iPreviousScreenWidth)
	{
		g_iPreviousScreenWidth = iScreenWidth;

		//screen size has changed , may need to reposition some overlays
		//re-create overlay by toggling off and on
		DeleteOverlays();
		CreateOverlays();
		

	}

	if(m_bSelectedIndexChanged)
	{
		m_bSelectedIndexChanged = false;
		//select the chosen point
		m_iSelectedIndex = Asura_Maths::Min(m_iSelectedIndex,Track->m_xPointObjects.size()-1);

		if(m_iSelectedIndex != Index)
		{
			Tools_Object* pxObject = Track->m_xPointObjects[m_iSelectedIndex];
			Tools_Object_System::SelectObject(pxObject);
			return;
		}
	}
}


void Tools_CutsceneTrackPoint_Object::ToggleOverlay(void)
{
	OverlayOpen = !OverlayOpen;

	if(ThisPoint)
	{
		if (OverlayOpen)
		{
			ThisPoint->CreateOverlays();
		}
		else
		{
			ThisPoint->DeleteOverlays();
		}
	}
}
bool Tools_CutsceneTrackPoint_Object::OverlayIsOn(void)
{
	return OverlayOpen;
}

void Tools_CutsceneTrackPoint_Object::CreateOverlays()
{
	CreatePanelOverlay();
	CreateMainOverlay();
}

void Tools_CutsceneTrackPoint_Object::CreatePanelOverlay()
{
	if(!OverlayOpen) return;


	//create the overlay to go in the panel at the bottom of the screen
	Asura_Overlay* newOverlay;
	static Asura_Vector_2 PanelOverlayPosition;
	PanelOverlayPosition.x = 0;
	PanelOverlayPosition.y = (Asura_Viewport::GetHeight()/14.0f)-5.0f;
	newOverlay = new Asura_Overlay(GetName(),false,&PanelOverlayPosition);
	newOverlay->m_bOnPanel = true;
	
	Asura_Dialog* newDialog;
	Asura_Vector_2 offset;

	offset.x = 1.0f;
	offset.y = 2.0f;
	newDialog = new Asura_SharedTickbox_Dialog("Straight",offset,&g_xParam_TrackPoint_Straight);
	newDialog->SetHelpText("Track should follow a straight line to next track point.");
	newOverlay->AddDialog(newDialog);

	offset.y = 3.5f;
	newDialog = new Asura_SharedTickbox_Dialog("Stationary",offset,&g_xParam_TrackPoint_Stationary);
	newDialog->SetIsEnabledFunction(TrackHasSmoothSpeed);
	newDialog->SetHelpText("Forces the speed to be zero at this point.\nOnly has any effect if track is using smoothed speeds.");
	newOverlay->AddDialog(newDialog);

	
	offset.x = 14.0f;
	offset.y = 2.0f;
	newDialog = new Tools_CutsceneTime_Dialog("Time",offset,&Time,0,1000,0.1f,&Track->m_bAsuraTrackInvalid);
	newDialog->SetHelpText("Time at which the actor should reach this point.");
	newOverlay->AddDialog(newDialog);

	offset.y = 3.5f;
	newDialog = new Tools_CutsceneTime_Dialog("Duration",offset,&Duration,0,1000,0.1f,&Track->m_bAsuraTrackInvalid);
	newDialog->SetHelpText("Time that the actor should stay stationary at this point , before moving to next.");
	newOverlay->AddDialog(newDialog);
	

#if 0
	offset.x = 36.0f;
	offset.y = 2.0f;
	newDialog = new Asura_Function_Dialog("Add After",offset,AddPointAfter);
	newOverlay->AddDialog(newDialog);

	offset.y = 3.5f;
	newDialog = new Asura_Function_Dialog("Add Before",offset,AddPointBefore);
	newOverlay->AddDialog(newDialog);
#else
	offset.x = 36.0f;
	offset.y = 0.5f;
	newDialog = new Asura_Dialog("Add :",offset);
	newOverlay->AddDialog(newDialog);

	offset.y = 2.0f;
	newDialog = new Asura_Function_Dialog("After",offset,AddPointAfter);
	newDialog->SetHelpText("Create a new track point after this one.\nPoint will be created at the camera position.");
	newOverlay->AddDialog(newDialog);

	offset.y = 3.5f;
	newDialog = new Asura_Function_Dialog("Before",offset,AddPointBefore);
	newDialog->SetHelpText("Create a new track point before this one.\nPoint will be created at the camera position.");
	newOverlay->AddDialog(newDialog);

	offset.x += 10.0f;
	offset.y = 0.5f;
	newDialog = new Asura_Dialog("Duplicate :",offset);
	newOverlay->AddDialog(newDialog);

	offset.y = 2.0f;
	newDialog = new Asura_Function_Dialog("After",offset,DuplicatePointAfter);
	newDialog->SetHelpText("Create a new track point after this one.\nPoint will be created in the same position as this one.");
	newOverlay->AddDialog(newDialog);

	offset.y = 3.5f;
	newDialog = new Asura_Function_Dialog("Before",offset,DuplicatePointBefore);
	newDialog->SetHelpText("Create a new track point before this one.\nPoint will be created in the same position as this one.");
	newOverlay->AddDialog(newDialog);

#endif


	newOverlay->m_uID = TOOLS_OVERLAY_CUTSCENE_PANEL;
	Asura_Overlay_System::SubmitNewOverlay(newOverlay);
}

void Tools_CutsceneTrackPoint_Object::CreateMainOverlay()
{
	if(!OverlayOpen) return;


	Asura_Overlay* newOverlay;
	Asura_Dialog* newDialog;
	Asura_Vector_2 offset;

	//create a floating overlay with the remaining controls
	newOverlay = new Asura_Overlay(GetName(),false,&OverlayPosition);
	
	offset = Asura_Vector_2(1.0f,3.0f);

	newDialog = new Asura_Function_Dialog("Rename",offset,RenameTrack);
	newDialog->SetHelpText("Rename track.");
	newOverlay->AddDialog(newDialog);
	offset.y+=Asura_Dialog::DefaultSpacing();

	newDialog = new Asura_Function_Dialog("Adjust Times",offset,AdjustTimes);
	newDialog->SetHelpText("Alter the times of multiple track points at once.\nUsed for setting a constant velocity over a section of track.");
	newOverlay->AddDialog(newDialog);
	offset.y+=Asura_Dialog::DefaultSpacing();

	m_bSelectedIndexChanged = false;
	m_iSelectedIndex = Index;
	newDialog = new Asura_Numerical_Dialog("Select Point",offset,&m_iSelectedIndex,0,Track->m_xPointObjects.size()-1,1,"%d",&m_bSelectedIndexChanged);
	newDialog->SetHelpText("Used for selecting other points in the track.");
	newOverlay->AddDialog(newDialog);
	offset.y+=Asura_Dialog::DefaultSpacing();

	newDialog = new Asura_FlagTickbox_Dialog("Interpolate To Next Track",offset,(int*)&Track->m_pxTrack->m_uTrackFlags,TrackFlag_InterpolateToNextTrack,&Track->m_bAsuraTrackInvalid);
	newDialog->SetHelpText("Interpolate from the end of this track to the start of the next one.");
	newOverlay->AddDialog(newDialog);
	offset.y+=Asura_Dialog::DefaultSpacing();

	newDialog = new Asura_FlagTickbox_Dialog("Circular Track",offset,(int*)&Track->m_pxTrack->m_uTrackFlags,TrackFlag_Circular,&Track->m_bAsuraTrackInvalid);
	newDialog->SetHelpText("Forces the last track point to be in the same position as the first.\nAlso makes the cutve smooth from the last to first point.");
	newOverlay->AddDialog(newDialog);
	offset.y+=Asura_Dialog::DefaultSpacing();

	newDialog = new Asura_FlagTickbox_Dialog("Loop Track",offset,(int*)&Track->m_pxTrack->m_uTrackFlags,TrackFlag_Loop,&Track->m_bAsuraTrackInvalid);
	newDialog->SetHelpText("Loop track once end has been reached.");
	newOverlay->AddDialog(newDialog);
	offset.y+=Asura_Dialog::DefaultSpacing();

	newDialog = new Asura_FlagTickbox_Dialog("Orientation From Points",offset,(int*)&Track->m_pxTrack->m_uTrackFlags,TrackFlag_OrientationFromPoints,&Track->m_bAsuraTrackInvalid);
	newDialog->SetHelpText("Orientation of actor is taken from the track point objects (instead of using a direction pointing down the track).");
	newDialog->m_iControlID = DialogID_OrientationFromPoints;
	newOverlay->AddDialog(newDialog);
	offset.y+=Asura_Dialog::DefaultSpacing();

	newDialog = new Asura_FlagTickbox_Dialog("Roll From Points",offset,(int*)&Track->m_pxTrack->m_uTrackFlags,TrackFlag_RollFromPoints,&Track->m_bAsuraTrackInvalid);
	newDialog->SetHelpText("The roll value of the point objects is used by the track.");
	newDialog->m_iControlID = DialogID_RollFromPoints;
	newOverlay->AddDialog(newDialog);
	offset.y+=Asura_Dialog::DefaultSpacing();

	newDialog = new Asura_FlagTickbox_Dialog("Pitch From Track Dir",offset,(int*)&Track->m_pxTrack->m_uTrackFlags,TrackFlag_PitchAlongTrack,&Track->m_bAsuraTrackInvalid);
	newDialog->SetHelpText("The pitch value of the point objects is used by the track. (Normally an actor will always have 0 pitch).");
	newDialog->m_iControlID = DialogID_PitchAlongTrack;
	newOverlay->AddDialog(newDialog);
	offset.y+=Asura_Dialog::DefaultSpacing();

	newDialog = new Asura_FlagTickbox_Dialog("Increased Speed Accuracy",offset,(int*)&Track->m_pxTrack->m_uTrackFlags,TrackFlag_IncreasedSpeedAccuracy,&Track->m_bAsuraTrackInvalid);
	newDialog->SetHelpText("Adjusts the parameterisation of the curve so that the speed is closer to being constant within each section.\nUsing this requires a fair bit of extra memory , so should be avoided unless required.");
	newOverlay->AddDialog(newDialog);
	offset.y+=Asura_Dialog::DefaultSpacing();

	newDialog = new Asura_FlagTickbox_Dialog("Smooth Speeds",offset,(int*)&Track->m_pxTrack->m_uTrackFlags,TrackFlag_SmoothSpeed,&Track->m_bAsuraTrackInvalid);
	newDialog->SetHelpText("Speeds are changed smoothly along the track , instead of using contant speeds for each section.\nIf used then the 'stationary' point flag can be used on some points to force the speed to reach zero at those points.\n");
	newOverlay->AddDialog(newDialog);
	offset.y+=Asura_Dialog::DefaultSpacing();


	//set things up so that right clicking on the overlay will be equivalent to right clicking on the object
	//RAR 06/11/2007
	newOverlay->SetContextMenuCallback(Tools_Object_System::AddContextMenuItemsFromOverlay, this);

	newOverlay->m_uID = TOOLS_OVERLAY_SELECTEDOBJECT;
	Asura_Overlay_System::SubmitNewOverlay(newOverlay,false);

	UpdateOverlayControls();

}


void Tools_CutsceneTrackPoint_Object::DeleteOverlays()
{
	Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_SELECTEDOBJECT);
	Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_CUTSCENE_PANEL);
}

bool Tools_CutsceneTrackPoint_Object::TrackHasSmoothSpeed()
{
	Asura_Assert(ThisPoint,"");
	return (ThisPoint->Track->m_pxTrack->m_uTrackFlags & TrackFlag_SmoothSpeed) ? true : false;
}


void Tools_CutsceneTrackPoint_Object::AddPointAfter()
{
	Asura_Assert(ThisPoint,"");
	ThisPoint->Track->AddPoint(ThisPoint->Index+1,false);
}

void Tools_CutsceneTrackPoint_Object::AddPointBefore()
{
	Asura_Assert(ThisPoint,"");
	ThisPoint->Track->AddPoint(ThisPoint->Index,true);
}

void Tools_CutsceneTrackPoint_Object::DuplicatePointAfter()
{
	Asura_Assert(ThisPoint,"");
	ThisPoint->Track->AddPoint(ThisPoint->Index+1,false,&ThisPoint->Position);
}

void Tools_CutsceneTrackPoint_Object::DuplicatePointBefore()
{
	Asura_Assert(ThisPoint,"");
	ThisPoint->Track->AddPoint(ThisPoint->Index,true,&ThisPoint->Position);
}



void Tools_CutsceneTrackPoint_Object::AdjustTimes()
{
	Asura_Assert(ThisPoint,"");

	if(ThisPoint->Track->m_xPointObjects.size()<2)
	{
		Tools_Timed_Message_System::AddMessage(0xff0000,"Need a track with at least 2 points");
		return;
	}

	Track_Times_Overlay overlay(ThisPoint->Track,ThisPoint->Index);
	overlay.DoModal();

}


void Tools_CutsceneTrackPoint_Object::RenameTrack()
{
	Asura_Assert(ThisPoint,"");

	ThisPoint->Track->Rename();
}

void Tools_CutsceneTrackPoint_Object::UpdateBackupVariables()
{
	PositionBackup = Position;
	OrientationBackup = Orientation;
	TimeBackup = Time;
	DurationBackup = Duration;
	SectionStraightBackup = SectionStraight;
	m_bStationaryFlagBackup = m_bStationaryFlag;
}

bool Tools_CutsceneTrackPoint_Object::BackupVariablesAreDifferent()
{
	if(PositionBackup != Position ||
	   OrientationBackup.e11 != Orientation.e11 ||
	   OrientationBackup.e12 != Orientation.e12 ||
	   OrientationBackup.e13 != Orientation.e13 ||
	   OrientationBackup.e21 != Orientation.e21 ||
	   OrientationBackup.e22 != Orientation.e22 ||
	   OrientationBackup.e23 != Orientation.e23 ||
	   OrientationBackup.e31 != Orientation.e31 ||
	   OrientationBackup.e32 != Orientation.e32 ||
	   OrientationBackup.e33 != Orientation.e33 ||
	   TimeBackup != Time ||
	   DurationBackup != Duration ||
	   SectionStraightBackup != SectionStraight ||
	   m_bStationaryFlagBackup != m_bStationaryFlag)
	
	{
		return true;
	}
	return false;
}

const char* Tools_CutsceneTrackPoint_Object::GetNameOfChangedVariable() const
{
	if(PositionBackup != Position)
	{
		return "Position";
	}
	if(OrientationBackup != Orientation)
	{
		return "Orientation";
	}
	if(TimeBackup != Time)
	{
		return "Time";
	}
	if(DurationBackup != Duration)
	{
		return "Duration";
	}
	if(SectionStraightBackup != SectionStraight)
	{
		return "Section Straight";
	}
	if(m_bStationaryFlagBackup != m_bStationaryFlag)
	{
		return "Stationary";
	}
	return 0;
}

void Tools_CutsceneTrackPoint_Object::AddContextMenuItems(class Tools_ContextMenu& xMenu, const class Tools_ContextMenu_Context& xContext)
{
	Tools_Object::AddContextMenuItems(xMenu, xContext);

	Tools_ContextMenu_Item* pxItem;

	Tools_ContextMenu_Section* pxSection = xMenu.GetSection(TOOLS_CONTEXTMENU_SECTION_SELECTION);
	pxSection->AddSeparator();

	//Add option for selecting/deselecting points in the track
	u_int uMode;
	const char* szText1;
	const char* szText2;
	const char* szText3;
	if(xContext.m_bShiftHeld)
	{
		szText1 = "Deselect track";
		szText2 = "Deselect track after";
		szText3 = "Deselect track before";
		uMode = 0;
	}
	else if(xContext.m_bControlHeld)
	{
		szText1 = "Select track";
		szText2 = "Select track after";
		szText3 = "Select track before";
		uMode = 1;
	}
	else
	{
		szText1 = "Select track";
		szText2 = "Select track after";
		szText3 = "Select track before";
		uMode = 2;
	}

	pxItem = new Tools_ContextMenu_Item_Function(szText1, ContextMenu_SelectTrack, this, uMode);
	pxItem->SetHelpText("Selects/deselects all points in this track");
	pxItem->SetID("Select track");
	pxSection->AddItem(pxItem);

	pxItem = new Tools_ContextMenu_Item_Function(szText2, ContextMenu_SelectTrackAfterPoint, this, uMode);
	pxItem->SetHelpText("Selects/deselects all points in the track after the point clicked on");
	pxItem->SetID("Select track after");
	pxSection->AddItem(pxItem);

	pxItem = new Tools_ContextMenu_Item_Function(szText3, ContextMenu_SelectTrackBeforePoint, this, uMode);
	pxItem->SetHelpText("Selects/deselects all points in the track before the point clicked on");
	pxItem->SetID("Select track before");
	pxSection->AddItem(pxItem);

	

}

void Tools_CutsceneTrackPoint_Object::ListObjectsForTrack(List<Tools_Object*>& xObjectList)
{
	//add all the points in this track
	for(LIF<Tools_CutsceneTrackPoint_Object*> plif(&Track->m_xPointObjects);!plif.done();plif.next())
	{
		xObjectList.add_entry(plif());
	}
	//put this point at the end of the list , to aid with selection
	xObjectList.delete_entry(this);
	xObjectList.add_entry(this);
}


void Tools_CutsceneTrackPoint_Object::ContextMenu_SelectTrack(void* pObject, u_int uMode)
{
	Tools_Object* pxObject = (Tools_Object*) pObject;

	Asura_Assert(pxObject && pxObject->Type == TOT_CutsceneTrackPoint,"");
	Tools_CutsceneTrackPoint_Object* pxPointObject = (Tools_CutsceneTrackPoint_Object*)pxObject;

	//list all objects in this track
	List<Tools_Object*> xSelectionList;
	pxPointObject->ListObjectsForTrack(xSelectionList);

	//And (de)select them
	if(uMode == 0)
	{
		Tools_Object_System::DeselectObjects(xSelectionList);
	}
	else
	{
		Tools_Object_System::SelectObjects(xSelectionList, uMode == 1);
	}
}


void Tools_CutsceneTrackPoint_Object::ContextMenu_SelectTrackAfterPoint(void* pObject, u_int uMode)
{
	Tools_Object* pxObject = (Tools_Object*) pObject;

	Asura_Assert(pxObject && pxObject->Type == TOT_CutsceneTrackPoint,"");
	Tools_CutsceneTrackPoint_Object* pxPointObject = (Tools_CutsceneTrackPoint_Object*)pxObject;

	//list all the points in this track starting at the selected point
	List<Tools_Object*> xSelectionList;
	for(int i=pxPointObject->Index+1;i<pxPointObject->Track->m_xPointObjects.size();i++)
	{
		xSelectionList.add_entry(pxPointObject->Track->m_xPointObjects[i]);
	}
	xSelectionList.add_entry(pxPointObject);

	//And (de)select them
	if(uMode == 0)
	{
		Tools_Object_System::DeselectObjects(xSelectionList);
	}
	else
	{
		Tools_Object_System::SelectObjects(xSelectionList, uMode == 1);
	}
}

void Tools_CutsceneTrackPoint_Object::ContextMenu_SelectTrackBeforePoint(void* pObject, u_int uMode)
{
	Tools_Object* pxObject = (Tools_Object*) pObject;

	Asura_Assert(pxObject && pxObject->Type == TOT_CutsceneTrackPoint,"");
	Tools_CutsceneTrackPoint_Object* pxPointObject = (Tools_CutsceneTrackPoint_Object*)pxObject;

	//add all the points in this track up to the selected point
	List<Tools_Object*> xSelectionList;
	for(int i=0;i<=pxPointObject->Index;i++)
	{
		xSelectionList.add_entry(pxPointObject->Track->m_xPointObjects[i]);
	}

	
	//And (de)select them
	if(uMode == 0)
	{
		Tools_Object_System::DeselectObjects(xSelectionList);
	}
	else
	{
		Tools_Object_System::SelectObjects(xSelectionList, uMode == 1);
	}
}



void Tools_CutsceneTrackPoint_Object::UpdateOverlayControls()
{
	if(ThisPoint!=this) return;

	unsigned& uTrackFlags = Track->m_pxTrack->m_uTrackFlags;
	if(uTrackFlags & (TrackFlag_RollFromPoints|TrackFlag_PitchAlongTrack))
	{
		uTrackFlags &=~ TrackFlag_OrientationFromPoints;
	}


	//Set the enabled status for each of the flag check boxes
	
	Asura_Overlay* pxOverlay = Asura_Overlay_System::GetOverlay(TOOLS_OVERLAY_SELECTEDOBJECT);
	if(pxOverlay)
	{
		Asura_Dialog* pxDialog;
		
		pxDialog = pxOverlay->GetDialog(DialogID_OrientationFromPoints);
		if(pxDialog)
		{
			pxDialog->SetEnabled(!(uTrackFlags & (TrackFlag_RollFromPoints|TrackFlag_PitchAlongTrack)));
		}

		pxDialog = pxOverlay->GetDialog(DialogID_RollFromPoints);
		if(pxDialog)
		{
			pxDialog->SetEnabled(!(uTrackFlags & (TrackFlag_OrientationFromPoints)));
		}

		pxDialog = pxOverlay->GetDialog(DialogID_PitchAlongTrack);
		if(pxDialog)
		{
			pxDialog->SetEnabled(!(uTrackFlags & (TrackFlag_OrientationFromPoints)));
		}

	}
}

void Tools_CutsceneTrackPoint_Object::AlignWithTrack()
{
	//get the current roll value , so we can keep it
	Asura_Euler_3 xEuler;
	Asura_Matrix_3x3 xRollMat;

	xEuler.FromMatrix(Orientation);
	xEuler.x = 0;
	xEuler.y = 0;
	xEuler.ToMatrix(xRollMat);


	//get the direction of the track
	Asura_Vector_3 xDirection;
	float fDistance = Track->m_pxTrack->GetDistanceAtPoint(Index);
	Track->m_pxTrack->GetDirectionFromDistance(fDistance,&xDirection);

	//and form a matrix from it
	Asura_Matrix_3x3 xMat;
	Asura_Maths::OrientationFromDirection(Orientation,xDirection);

	//reapply the roll
	Orientation = Orientation * xRollMat;

	UpdatePositionInAsuraData(false);

}

bool Tools_CutsceneTrackPoint_Object::GetParameter(Asura_Hash_ID uParamID,class Tools_Parameter* pxParam) const
{
	switch(uParamID)
	{
		case g_uHash_TrackPoint_Straight :
			pxParam->SetBool(SectionStraight);
			break;

		case g_uHash_TrackPoint_Stationary :
			pxParam->SetBool(m_bStationaryFlag);
			break;

		default :
			return false;
	}
	return true;
}

bool Tools_CutsceneTrackPoint_Object::SetParameter(Asura_Hash_ID uParamID,const class Tools_Parameter* pxParam)
{
	switch(uParamID)
	{
		case g_uHash_TrackPoint_Straight :
			SectionStraight = pxParam->GetBool();
			Track->m_bAsuraTrackInvalid = true;
			break;

		case g_uHash_TrackPoint_Stationary :
			m_bStationaryFlag = pxParam->GetBool();
			Track->m_bAsuraTrackInvalid = true;
			break;

		default :
			return false;
	}
	return true;
}

///////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------**
** Track_Times_Overlay **
**---------------------*/

Track_Times_Overlay* Track_Times_Overlay::This = 0;


Track_Times_Overlay::Track_Times_Overlay(Tools_Cutscene_Track* track,int index)
:Tools_Modal_Overlay("Edit Times")
{
	if(!index) index = 1;
	Track = track;
	IndexTo = (float)index;
	AsuraTrack = Track->m_pxTrack;

	TimeChanged = false;
	TimeSinceChanged = false;
	SpeedChanged = false;
	IndexFromChanged = false;
};

	
void Track_Times_Overlay::InitialiseOverlay(class Asura_Overlay* Overlay)
{
	Track->UpdateAsuraTrack();

	//initialise values
	IndexFrom = 0;
	Time = AsuraTrack->PointArrivalTimes[(int)IndexTo];
	float leaveTime = AsuraTrack->PointArrivalTimes[(int)IndexFrom];
	if(AsuraTrack->PointDurations) leaveTime += AsuraTrack->PointDurations[(int)IndexFrom];
	TimeSince = AsuraTrack->PointArrivalTimes[(int)IndexTo]-leaveTime;
	if(TimeSince>0)
	{
		Speed = (AsuraTrack->GetDistanceAtPoint((int)IndexTo)-AsuraTrack->GetDistanceAtPoint((int)IndexFrom))/TimeSince;
	}
	else
	{
		Speed = 1;
	}
	
	//setup the controls on the overlay
	Asura_Vector_2 offset = Asura_Vector_2(1.0f,3.0f);
	Asura_Dialog* newDialog;

	newDialog = new Tools_CutsceneTime_Dialog("Time",offset,&Time,0,1000,0.1f,&TimeChanged);
	newDialog->SetHelpText("Time at which 'To Point' should be reached.");
	Overlay->AddDialog(newDialog);
	offset.y+=1.5;

	newDialog = new Tools_CutsceneTime_Dialog("Time Since",offset,&TimeSince,0,1000,0.1f,&TimeSinceChanged);
	newDialog->SetHelpText("Time taken to go from 'From Point' to 'To Point'.");
	Overlay->AddDialog(newDialog);
	offset.y+=1.5;

	newDialog = new Asura_Numerical_Dialog("Speed",offset,&Speed,0,1000,0.1f,"%.3f",&SpeedChanged);
	newDialog->SetHelpText("Speed in metres per second that should be used between 'From Point' and 'To Point'.");
	Overlay->AddDialog(newDialog);
	offset.y+=1.5;

	if(Track->m_bIsActorPositionTrack && Track->m_pxActorObject && Track->m_pxActorObject->IsHierarchyActor())
	{
		newDialog = new Asura_Function_Dialog("Get Speed From Animation",offset,GetSpeedFromAnimation);
		newDialog->SetHelpText("Sets the speed from the speed set in a specified animation.");
		Overlay->AddDialog(newDialog);
		offset.y+=1.5;
	}

	offset.y+=1;

	newDialog = new Asura_Numerical_Dialog("From Point",offset,&IndexFrom,0,(float)Track->m_xPointObjects.size()-2,1,"%.0f",&IndexFromChanged);
	newDialog->SetHelpText("Sets first point whose time should be edited using the values above.");
	Overlay->AddDialog(newDialog);
	offset.y+=1.5;

	newDialog = new Asura_Numerical_Dialog("To Point",offset,&IndexTo,1,(float)Track->m_xPointObjects.size()-1,1,"%.0f",&IndexToChanged);
	newDialog->SetHelpText("Sets the last point whose time should be edited using the values  above.");
	Overlay->AddDialog(newDialog);
	offset.y+=1.5;

	offset.y+=1.0f;

	newDialog = new Asura_Function_Dialog("Okay",offset,OkaySelected,&Overlay->m_bShouldClose);
	Overlay->AddDialog(newDialog);

	offset.x+=10.0f;

	newDialog = new Asura_Function_Dialog("Cancel",offset,CancelSelected,&Overlay->m_bShouldClose);
	Overlay->AddDialog(newDialog);

	This = this;

}

void Track_Times_Overlay::Update()
{
	//if any of the paramaters have changed, update the others as needed
	if(TimeChanged)
	{
		float leaveTime = AsuraTrack->PointArrivalTimes[(int)IndexFrom];
		if(AsuraTrack->PointDurations) leaveTime += AsuraTrack->PointDurations[(int)IndexFrom];
		if(Time<=leaveTime)
		{
			Time = AsuraTrack->PointArrivalTimes[(int)IndexFrom];
			TimeSince = 0;
			Speed = 1;
		}
		else
		{
			TimeSince = Time - leaveTime;
			Speed = (AsuraTrack->GetDistanceAtPoint((int)IndexTo)-AsuraTrack->GetDistanceAtPoint((int)IndexFrom))/TimeSince;
		}

		TimeChanged = false;
	}

	if(TimeSinceChanged)
	{
		float leaveTime = AsuraTrack->PointArrivalTimes[(int)IndexFrom];
		if(AsuraTrack->PointDurations) leaveTime += AsuraTrack->PointDurations[(int)IndexFrom];

		Time = leaveTime+TimeSince;
		if(TimeSince>0)
		{
			Speed = (AsuraTrack->GetDistanceAtPoint((int)IndexTo)-AsuraTrack->GetDistanceAtPoint((int)IndexFrom))/TimeSince;
		}
		else
		{
			Speed = 1;
		}
		TimeSinceChanged = 0;
	}

	if(SpeedChanged)
	{
		if(Speed>0)
		{
			float leaveTime = AsuraTrack->PointArrivalTimes[(int)IndexFrom];
			if(AsuraTrack->PointDurations) leaveTime += AsuraTrack->PointDurations[(int)IndexFrom];

			TimeSince = (AsuraTrack->GetDistanceAtPoint((int)IndexTo)-AsuraTrack->GetDistanceAtPoint((int)IndexFrom))/Speed;
			Time = leaveTime+TimeSince;
		}
		SpeedChanged = false;
	}

	if(IndexFromChanged || IndexToChanged)
	{
		if(IndexTo<=IndexFrom)
		{
			if(IndexFromChanged)
			{
				IndexFrom = IndexTo-1;
			}
			else
			{
				IndexTo = IndexFrom+1;
			}
		}
		float leaveTime = AsuraTrack->PointArrivalTimes[(int)IndexFrom];
		if(AsuraTrack->PointDurations) leaveTime += AsuraTrack->PointDurations[(int)IndexFrom];

		Time = AsuraTrack->PointArrivalTimes[(int)IndexTo];
		TimeSince = AsuraTrack->PointArrivalTimes[(int)IndexTo]-leaveTime;
		if(TimeSince>0)
		{
			Speed = (AsuraTrack->GetDistanceAtPoint((int)IndexTo)-AsuraTrack->GetDistanceAtPoint((int)IndexFrom))/TimeSince;
		}
		else
		{
			Speed = 1;
		}

		IndexToChanged = false;
		IndexFromChanged = false;
	}
}

void Track_Times_Overlay::OkaySelected()
{
	This->Update();//in case a change was made to one of the parameters on this frame
	
	This->Track->SetTimeForPoints((int)This->IndexFrom,(int)This->IndexTo,This->Time);
}

void Track_Times_Overlay::CancelSelected()
{
	//do nothing
}

void Track_Times_Overlay::GetSpeedFromAnimation()
{
    Asura_Assert(This->Track->m_bIsActorPositionTrack, "GetSpeedFromAnimation() called for wrong type of track");

	if( This->Track->m_pxActorObject )
	{
		Asura_Hierarchy_Anim* pxAnim = This->Track->m_pxActorObject->ChooseAnim("Anim To Take Speed From:");
		if(pxAnim && pxAnim->Speed)
		{
			This->Speed = pxAnim->Speed;
			This->SpeedChanged = true;
		}
	}
}


//////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------**
** Cutscene Undo **
**---------------*/


/////////TrackPoint_Created
void CutsceneModeUndo::Log_TrackPoint_Created(Tools_CutsceneTrackPoint_Object* pxPointObject)
{
    if (!pxPointObject->Track->m_bIsActorPositionTrack) return;// (MH - 05.11.2002) not doing undo for these tracks yet.
	RootUndoData** ppxUndo = AllocateNextUndoData();
	if(!ppxUndo) return;
	
	*ppxUndo = new CutsceneUndoData_TrackPoint_Created(pxPointObject);
}

CutsceneUndoData_TrackPoint_Created::CutsceneUndoData_TrackPoint_Created(Tools_CutsceneTrackPoint_Object* pxPointObject)
{
    Asura_Assert(pxPointObject->Track->m_bIsActorPositionTrack, "undo not valid for this track");

	if( pxPointObject->Track->m_pxActorObject )
	{
		m_xActorName = pxPointObject->Track->m_pxActorObject->GetName();
	}

	m_iTrackID = pxPointObject->Track->m_iTrackID;
	m_iPointID = pxPointObject->m_iPointID;

	SetDescription("Point Created '%s'",pxPointObject->GetName());
}

RootUndoData* CutsceneUndoData_TrackPoint_Created::ProcessUndoData()
{
	//find the track point object for this
	Tools_CutsceneTrackPoint_Object* pxPointObject = GetTrackPointObject(m_xActorName,m_iTrackID,m_iPointID);
	Asura_Assert(pxPointObject,"");

	//create the reverse undo data
	CutsceneUndoData_TrackPoint_Destroyed* pxUndo = new CutsceneUndoData_TrackPoint_Destroyed(pxPointObject);
	//preserve description though
	pxUndo->SetDescription("%s",GetDescription());
	

	//do the undo deeds
	pxPointObject->Destroy();
	
	return pxUndo;
}

int CutsceneUndoData_TrackPoint_Created::GetPriority() const
{
	return CutsceneModeUndo::PRIORITY_TRACKPOINT_CREATED;
}


/////////TrackPoint_Destroyed
void CutsceneModeUndo::Log_TrackPoint_Destroyed(Tools_CutsceneTrackPoint_Object* pxPointObject)
{
    if (!pxPointObject->Track->m_bIsActorPositionTrack) return;// (MH - 05.11.2002) not doing undo for these tracks yet.
	RootUndoData** ppxUndo = AllocateNextUndoData();
	if(!ppxUndo) return;
	
	*ppxUndo = new CutsceneUndoData_TrackPoint_Destroyed(pxPointObject);
}

CutsceneUndoData_TrackPoint_Destroyed::CutsceneUndoData_TrackPoint_Destroyed(Tools_CutsceneTrackPoint_Object* pxPointObject)
{
    Asura_Assert(pxPointObject->Track->m_bIsActorPositionTrack, "undo not valid for this track");

	if( pxPointObject->Track->m_pxActorObject )
	{
		m_xActorName = pxPointObject->Track->m_pxActorObject->GetName();
	}

	m_iTrackID = pxPointObject->Track->m_iTrackID;
	m_iPointID = pxPointObject->m_iPointID;
	m_iPointIndex = pxPointObject->Index;

	m_xPosition = pxPointObject->Position;
	m_xOrientation = pxPointObject->Orientation;
	m_fTime = pxPointObject->Time;
	m_fDuration = pxPointObject->Duration;
	m_bSectionStraight = pxPointObject->SectionStraight;
	m_bStationaryFlag = pxPointObject->m_bStationaryFlag;

	SetDescription("Point Destroyed '%s'",pxPointObject->GetName());

}

RootUndoData* CutsceneUndoData_TrackPoint_Destroyed::ProcessUndoData()
{
	//find the track for the point
	Tools_Cutscene_Track* pxTrack = GetCutsceneTrack(m_xActorName,m_iTrackID);
	Asura_Assert(pxTrack,"");

	//create the new point
	Tools_CutsceneTrackPoint_Object* pxPointObject = pxTrack->AddPoint(m_iPointIndex,false, NULL, m_iPointID);

	//fill in the details from the undo data
	pxPointObject->Position = m_xPosition;
	pxPointObject->Orientation = m_xOrientation;
	pxPointObject->Time = m_fTime;
	pxPointObject->Duration = m_fDuration;
	pxPointObject->SectionStraight = m_bSectionStraight;
	pxPointObject->m_bStationaryFlag = m_bStationaryFlag;
	pxPointObject->UpdateBackupVariables();
	
	//return the reverse undo data
	CutsceneUndoData_TrackPoint_Created* pxUndo = new CutsceneUndoData_TrackPoint_Created(pxPointObject);
	pxUndo->SetDescription("%s",GetDescription());
	
	return pxUndo;
}

int CutsceneUndoData_TrackPoint_Destroyed::GetPriority() const
{
	return CutsceneModeUndo::PRIORITY_TRACKPOINT_DESTROYED;
}



/////////TrackPoint_Changed
void CutsceneModeUndo::Log_TrackPoint_Changed(Tools_CutsceneTrackPoint_Object* pxPointObject)
{
    if (!pxPointObject->Track->m_bIsActorPositionTrack) return;// (MH - 05.11.2002) not doing undo for these tracks yet.
	RootUndoData** ppxUndo = AllocateNextUndoData();
	if(!ppxUndo) return;
	
	*ppxUndo = new CutsceneUndoData_TrackPoint_Changed(pxPointObject);
}

CutsceneUndoData_TrackPoint_Changed::CutsceneUndoData_TrackPoint_Changed(Tools_CutsceneTrackPoint_Object* pxPointObject)
{
    Asura_Assert(pxPointObject->Track->m_bIsActorPositionTrack, "undo not valid for this track");

	if( pxPointObject->Track->m_pxActorObject )
	{
		m_xActorName = pxPointObject->Track->m_pxActorObject->GetName();
	}

	m_iTrackID = pxPointObject->Track->m_iTrackID;
	m_iPointID = pxPointObject->m_iPointID;
	m_iPointIndex = pxPointObject->Index;

	m_xPosition = pxPointObject->PositionBackup;
	m_xOrientation = pxPointObject->OrientationBackup;
	m_fTime = pxPointObject->TimeBackup;
	m_fDuration = pxPointObject->DurationBackup;
	m_bSectionStraight = pxPointObject->SectionStraightBackup;
	m_bStationaryFlag = pxPointObject->m_bStationaryFlagBackup;

	//fill in desc of change
	const char* pszVarName = pxPointObject->GetNameOfChangedVariable();
	if(pszVarName)
		SetDescription("Point Changed (%s) '%s'",pszVarName,pxPointObject->GetName());
	else
		SetDescription("Point Changed  '%s'",pxPointObject->GetName());
	
	//changes have been noted , so update the backup variables
	pxPointObject->UpdateBackupVariables();

	pxPointObject->UpdatePositionInAsuraData(true);
}

RootUndoData* CutsceneUndoData_TrackPoint_Changed::ProcessUndoData()
{
	Tools_CutsceneTrackPoint_Object* pxPointObject = GetTrackPointObject(m_xActorName,m_iTrackID,m_iPointID);
	Asura_Assert(pxPointObject,"");

	//update backup variables , so they can be used for the reverse undo data
	pxPointObject->UpdateBackupVariables();

	//fill in the details from the undo data
	pxPointObject->Position = m_xPosition;
	pxPointObject->Orientation = m_xOrientation;
	pxPointObject->Time = m_fTime;
	pxPointObject->Duration = m_fDuration;
	pxPointObject->SectionStraight = m_bSectionStraight;
	pxPointObject->m_bStationaryFlag = m_bStationaryFlag;


	pxPointObject->Track->m_bAsuraTrackInvalid = true;
	
	return new CutsceneUndoData_TrackPoint_Changed(pxPointObject);
}

int CutsceneUndoData_TrackPoint_Changed::GetPriority() const
{
	return CutsceneModeUndo::PRIORITY_TRACKPOINT_CHANGED;
}



/////////Track_Created
void CutsceneModeUndo::Log_Track_Created(Tools_Cutscene_Track* track)
{
    if (!track->m_bIsActorPositionTrack) return;// (MH - 05.11.2002) not doing undo for these tracks yet.
	RootUndoData** undo = AllocateNextUndoData();
	if(!undo) return;
	
	*undo = new CutsceneUndoData_Track_Created(track);
}

CutsceneUndoData_Track_Created::CutsceneUndoData_Track_Created(Tools_Cutscene_Track* track)
{
    Asura_Assert(track->m_bIsActorPositionTrack, "undo not valid for this track");

	if( track->m_pxActorObject )
	{
		ActorName = track->m_pxActorObject->GetName();
	}

	TrackID = track->m_iTrackID;

	SetDescription("Track Created '%s'",track->m_pxTrack->TrackName.GetString());

}

RootUndoData* CutsceneUndoData_Track_Created::ProcessUndoData()
{
	//find the track  for this
	Tools_Cutscene_Track* track = GetCutsceneTrack(ActorName,TrackID);
	Asura_Assert(track,"");

	//create the reverse undo data
	CutsceneUndoData_Track_Destroyed* undo = new CutsceneUndoData_Track_Destroyed(track);
	undo->SetDescription("%s",GetDescription());
	
	//do the undo deeds
	Asura_Assert(!track->m_xPointObjects.size(),"");//all points should have been destroyed by other undo actions
	track->Destroy();
	
	return undo;
}

int CutsceneUndoData_Track_Created::GetPriority() const
{
	return CutsceneModeUndo::PRIORITY_TRACK_CREATED;
}


/////////Track_Destroyed
void CutsceneModeUndo::Log_Track_Destroyed(Tools_Cutscene_Track* track)
{
    if (!track->m_bIsActorPositionTrack) return;// (MH - 05.11.2002) not doing undo for these tracks yet.
	RootUndoData** undo = AllocateNextUndoData();
	if(!undo) return;
	
	*undo = new CutsceneUndoData_Track_Destroyed(track);
}

CutsceneUndoData_Track_Destroyed::CutsceneUndoData_Track_Destroyed(Tools_Cutscene_Track* track)
{
    Asura_Assert(track->m_bIsActorPositionTrack, "undo not valid for this track");

	if( track->m_pxActorObject )
	{
		ActorName = track->m_pxActorObject->GetName();
	}

	TrackID = track->m_iTrackID;

	TrackName = track->m_pxTrack->TrackName;
	m_uTrackFlags = track->m_pxTrack->m_uTrackFlags;

	SetDescription("Track Destroyed '%s'",TrackName.GetString());

}

RootUndoData* CutsceneUndoData_Track_Destroyed::ProcessUndoData()
{
	//find the actor for this
	Tools_CutsceneActor_Object* actorObject = GetActorObject(ActorName);
	Asura_Assert(actorObject,"");

	//create the track
	Tools_Cutscene_Track* track = actorObject->CreateTrack(TrackID);

	//fill in details from the undo data
	track->m_pxTrack->TrackName = TrackName;
	track->m_pxTrack->m_uTrackFlags = m_uTrackFlags;
	track->m_uTrackFlagsBackup = m_uTrackFlags;
	
	CutsceneUndoData_Track_Created* pxUndo = new CutsceneUndoData_Track_Created(track);
	pxUndo->SetDescription("%s",GetDescription());
	return pxUndo;
}

int CutsceneUndoData_Track_Destroyed::GetPriority() const
{
	return CutsceneModeUndo::PRIORITY_TRACK_DESTROYED;
}




/////////Track_Rename
void CutsceneModeUndo::Log_Track_Rename(Tools_Cutscene_Track* track)
{
    if (!track->m_bIsActorPositionTrack) return;// (MH - 05.11.2002) not doing undo for these tracks yet.
	RootUndoData** undo = AllocateNextUndoData();
	if(!undo) return;
	
	*undo = new CutsceneUndoData_Track_Rename(track);
}

CutsceneUndoData_Track_Rename::CutsceneUndoData_Track_Rename(Tools_Cutscene_Track* track)
{
    Asura_Assert(track->m_bIsActorPositionTrack, "undo not valid for this track");

	if( track->m_pxActorObject )
	{
		ActorName = track->m_pxActorObject->GetName();
	}

	TrackID = track->m_iTrackID;

	TrackName = track->m_pxTrack->TrackName;

	SetDescription("Track Renamed '%s'",TrackName.GetString());

}

RootUndoData* CutsceneUndoData_Track_Rename::ProcessUndoData()
{
	//find the track 
	Tools_Cutscene_Track* track = GetCutsceneTrack(ActorName,TrackID);
	Asura_Assert(track,"");

	//store the name for the reverse undo
	Asura_String TempName = track->m_pxTrack->TrackName;

	//update the tracks name
	track->Rename(TrackName.GetString());

	//convert the undo data
	TrackName = TempName;

	return this;
}

int CutsceneUndoData_Track_Rename::GetPriority() const
{
	return CutsceneModeUndo::PRIORITY_TRACK_RENAMED;
}

/////////Track_Changed
void CutsceneModeUndo::Log_Track_Changed(Tools_Cutscene_Track* pxTrack)
{
    if (!pxTrack->m_bIsActorPositionTrack) return;// (MH - 05.11.2002) not doing undo for these tracks yet.
	RootUndoData** undo = AllocateNextUndoData();
	if(!undo) return;
	
	*undo = new CutsceneUndoData_Track_Changed(pxTrack);
}

CutsceneUndoData_Track_Changed::CutsceneUndoData_Track_Changed(Tools_Cutscene_Track* pxTrack)
{
    Asura_Assert(pxTrack->m_bIsActorPositionTrack, "undo not valid for this track");

	if( pxTrack->m_pxActorObject )
	{
		m_xActorName = pxTrack->m_pxActorObject->GetName();
	}

	m_iTrackID = pxTrack->m_iTrackID;

	m_uTrackFlags = pxTrack->m_uTrackFlagsBackup;
	pxTrack->m_uTrackFlagsBackup = pxTrack->m_pxTrack->m_uTrackFlags;

	SetDescription("Track Changed (Flags) '%s'",pxTrack->m_pxTrack->TrackName.GetString());
}

RootUndoData* CutsceneUndoData_Track_Changed::ProcessUndoData()
{
	//find the track 
	Tools_Cutscene_Track* pxTrack = GetCutsceneTrack(m_xActorName,m_iTrackID);
	Asura_Assert(pxTrack,"");

	unsigned uTemp = pxTrack->m_pxTrack->m_uTrackFlags;

	//update the tracks flags
	pxTrack->m_pxTrack->m_uTrackFlags = m_uTrackFlags;
	pxTrack->m_uTrackFlagsBackup = m_uTrackFlags;
	pxTrack->m_bAsuraTrackInvalid = true;


	//convert the undo data
	m_uTrackFlags = uTemp;

	return this;
}

int CutsceneUndoData_Track_Changed::GetPriority() const
{
	return CutsceneModeUndo::PRIORITY_TRACK_CHANGED;
}

