/*******************************************************************************
 *	ATI 3D RAGE SDK sample code												   *	
 *																			   *
 *  Knight Demo																   *
 *																			   *
 *  Copyright (c) 1996-1997 ATI Technologies, Inc.  All rights reserved.	   *	
 *																			   *
 * Written by Aaron Orenstein												   *
 *  																		   *
 *	Functions to deal with camera script files                                 *
 *******************************************************************************/
#include "stdwin.h"
#include "Util.h"
#include "Watchers.h"
#include "intersect.h"

#include "script.h"

#include "normalmode.h"

// -----------------------------------------------------------------------------

CameraScript::~CameraScript(void)
{
	delete [] m_pControl;
	m_nControl = 0;
}

CameraScript::CameraScript(char* pFilename)
{
	BOOL success = FALSE;

	fileHandle fh(pFilename, "r");

	CameraControl tmp;
	m_nControl = 0;
	while(ReadScriptLine(tmp, fh))
		m_nControl++;

	m_pControl = new CameraControl[m_nControl];
	DECLARE_POINTER_WATCHER(CameraControl*, m_pControl, success, m_pControl);

	fh.seek(0, SEEK_SET);
	for(int i=0; i<m_nControl; i++)
		VERIFY(ReadScriptLine(m_pControl[i], fh));

	success = TRUE;
}

// -----------------------------------------------------------------------------

BOOL CameraScript::ReadScriptLine(CameraControl& camera, fileHandle& fh)
{
	while(1)
	{
		char buf[256];
		if(!fgets(buf, 256, fh.HANDLE())) return FALSE;

		char* p = strchr(buf, ';');
		if(p)
		{ 
			*p=0;
			p--; 
		}
		else
			p = &buf[strlen(buf)-1];

		while((p >= buf) && isspace(*p)) p--;
		p[1] = 0;

		p=buf;
		while(isspace(*p)) p++;

		if(strlen(p))
		{
			int		count;
			Vector	lookat;
			Vector	from;

			char pathtype;
			int err = sscanf(p, "%c %d < %f , %f , %f > < %f , %f , %f >",
				&pathtype,
				&count,
				&lookat.X(), &lookat.Y(), &lookat.Z(),
				&from.X(), &from.Y(), &from.Z());

			if(err != 8) THROW_EXCEPTION();

			camera.count = count;
			camera.lookAt = lookat;
			camera.position = from;

			switch(pathtype)
			{
			case 'L': camera.pathtype = PATHTYPE_LINEAR; break;
			case 'C': camera.pathtype = PATHTYPE_CURVED; break;
			}

			return TRUE;
		}
	}
}

// -----------------------------------------------------------------------------

void CameraScript::SetupPath(int from, int to)
{
	ASSERT((from >= 0) && (from < m_nControl));
	ASSERT((to >= 0) && (to < m_nControl));

	m_camera = from;

	m_positionA = m_pControl[from].position;
	m_lookAtA = Normalize(m_pControl[from].lookAt);

	m_positionB = m_pControl[to].position;
	m_lookAtB = Normalize(m_pControl[to].lookAt);

	m_tick = 0;

	if(from == to)
	{
		m_count = 0;
		m_pathtype = PATHTYPE_LINEAR;
		return;
	}

	m_pathtype = m_pControl[to].pathtype;
	m_count = m_pControl[to].count;

	if(m_pathtype == PATHTYPE_CURVED)
	{
		float tA, tB;
		Vector positionA1 = m_positionA + m_lookAtA;
		Vector positionB1 = m_positionB + m_lookAtB;
		if(IntersectLine(tA, tB,
			Vector2(m_positionA.X(), m_positionA.Z()), Vector2(positionA1.X(), positionA1.Z()),
			Vector2(m_positionB.X(), m_positionB.Z()), Vector2(positionB1.X(), positionB1.Z())))
		{
			m_center = m_lookAtA * tA + m_positionA;
			m_radiusA = tA;
			m_radiusB = tB;
		}
		else
			m_pathtype = PATHTYPE_LINEAR;
	}
}

// -----------------------------------------------------------------------------

void CameraScript::Init(void)
{
	if(m_nControl == 0)
	{
		m_positionA = Vector(ZERO);
		m_lookAtA = Vector(ZERO);
		m_count = 0;
		m_camera = 0;
		m_pathtype = PATHTYPE_LINEAR;
	}
	else if(m_nControl == 1)
		SetupPath(0, 0);
	else
		SetupPath(0, 1);

	m_tick = 0;
}

// -----------------------------------------------------------------------------

void CameraScript::Tick(void)
{
	if(m_tick < m_count)
		m_tick++;
	else if(m_camera < (m_nControl-1))
	{
		m_camera++;
		if(m_camera < (m_nControl - 1))
			SetupPath(m_camera, m_camera+1);
		else
			SetupPath(m_nControl-1, m_nControl-1);
	}
}

// -----------------------------------------------------------------------------

void CameraScript::Copy(void)
{
	ASSERT((m_tick >= 0) && (m_tick <= m_count));

	if(m_tick == 0)
	{
		NormalMode::m_cameraPosition = m_positionA;
		NormalMode::LookAt(m_lookAtA);
	}
	else if(m_tick == m_count)
	{
		NormalMode::m_cameraPosition = m_positionB;
		NormalMode::LookAt(m_lookAtB);
	}
	else
	{
		float t = (float)m_tick / (float)m_count;
		Vector lookAt = Normalize((m_lookAtB - m_lookAtA) * t + m_lookAtA);
		NormalMode::LookAt(lookAt);
		switch(m_pathtype)
		{
		case PATHTYPE_LINEAR:
			NormalMode::m_cameraPosition = (m_positionB - m_positionA) * t + m_positionA;
			break;

		case PATHTYPE_CURVED:
			{
				float radius = (m_radiusB - m_radiusA) * t + m_radiusA;
				NormalMode::m_cameraPosition = m_center - lookAt * radius;
				NormalMode::m_cameraPosition.Y() = (m_positionB.Y() - m_positionA.Y()) * t + m_positionA.Y();
			}
			break;
		}
	}
}

// -----------------------------------------------------------------------------
