#include "PajaTypes.h"
#include "Vector3C.h"
#include "ControllerC.h"
#include "CameraC.h"
#include "ContFloatC.h"
#include "ContVector3C.h"
#include "KeyC.h"

using namespace Composition;
using namespace PajaTypes;
using namespace FileIO;


static
void
TRACE( const char* szFormat, ...  )
{
	char	szMsg[256];
	va_list	rList;
	va_start( rList, szFormat );
	_vsnprintf( szMsg, 255, szFormat, rList );
	va_end( rList );
	OutputDebugString( szMsg );
}



CameraC::CameraC() :
	m_rPos( 0, 0, 0 ),
	m_rTgt( 0, 0, 1 ),
	m_f32Roll( 0 ),
	m_f32FOV( 60 / 180 * M_PI  ),
	m_f32NearPlane( 0 ),
	m_f32FarPlane( 1 ),
	m_pPosCont( 0 ),
	m_pTgtCont( 0 ),
	m_pRollCont( 0 ),
	m_pFOVCont( 0 ),
	m_pNearPlaneCont( 0 ),
	m_pFarPlaneCont( 0 )
{
	// empty
}

CameraC::~CameraC()
{
	delete m_pPosCont;
	delete m_pTgtCont;
	delete m_pRollCont;
	delete m_pFOVCont;
	delete m_pNearPlaneCont;
	delete m_pFarPlaneCont;
}

void
CameraC::set_position( const Vector3C& rPos )
{
	m_rPos = rPos;
}

const
Vector3C&
CameraC::get_position()
{
	return m_rPos;
}

void
CameraC::set_target_position( const Vector3C& rPos )
{
	m_rTgt = rPos;
}

const
Vector3C&
CameraC::get_target_position()
{
	return m_rTgt;
}

void
CameraC::set_roll( const float32 f32Val )
{
	m_f32Roll = f32Val;
}

const
float32
CameraC::get_roll()
{
	return m_f32Roll;
}

void
CameraC::set_fov( const float32 f32Val )
{
	m_f32FOV = f32Val;
}

const
float32
CameraC::get_fov()
{
	return m_f32FOV;
}

void
CameraC::set_near_plane( const float32 f32Val )
{
	m_f32NearPlane = f32Val;
}

const
float32
CameraC::get_near_plane()
{
	return m_f32NearPlane;
}

void
CameraC::set_far_plane( const float32 f32Val )
{
	m_f32FarPlane = f32Val;
}

const
float32
CameraC::get_far_plane()
{
	return m_f32FarPlane;
}

void
CameraC::eval_state( int32 i32Time )
{
	if( m_pPosCont )
		m_rPos = m_pPosCont->get_value( i32Time ); 

	if( m_pTgtCont )
		m_rTgt = m_pTgtCont->get_value( i32Time ); 

	if( m_pRollCont )
		m_f32Roll = m_pRollCont->get_value( i32Time ); 

	if( m_pFOVCont )
		m_f32FOV = m_pFOVCont->get_value( i32Time ); 

	if( m_pNearPlaneCont )
		m_f32NearPlane = m_pNearPlaneCont->get_value( i32Time ); 

	if( m_pFarPlaneCont )
		m_f32FarPlane = m_pFarPlaneCont->get_value( i32Time ); 
}

void
CameraC::set_position_controller( ContVector3C* pCont )
{
	m_pPosCont = pCont;
}

ContVector3C*
CameraC::get_position_controller()
{
	return m_pPosCont;
}

void
CameraC::set_target_position_controller( ContVector3C* pCont )
{
	m_pTgtCont = pCont;
}

ContVector3C*
CameraC::get_target_position_controller()
{
	return m_pTgtCont;
}

void
CameraC::set_roll_controller( ContFloatC* pCont )
{
	m_pRollCont = pCont;
}

ContFloatC*
CameraC::get_roll_controller()
{
	return m_pRollCont;
}

void
CameraC::set_fov_controller( ContFloatC* pCont )
{
	m_pFOVCont = pCont;
}

ContFloatC*
CameraC::get_fov_controller()
{
	return m_pFOVCont;
}

void
CameraC::set_near_plane_controller( ContFloatC* pCont )
{
	m_pNearPlaneCont = pCont;
}

ContFloatC*
CameraC::get_near_plane_controller()
{
	return m_pNearPlaneCont;
}

void
CameraC::set_far_plane_controller( ContFloatC* pCont )
{
	m_pFarPlaneCont = pCont;
}

ContFloatC*
CameraC::get_far_plane_controller()
{
	return m_pFarPlaneCont;
}

void
CameraC::set_name( const char* szName )
{
	m_sName = szName;
}

const char*
CameraC::get_name()
{
	return m_sName.c_str();
}

enum CameraChunksE {
	CHUNK_CAMERA_BASE			= 0x1001,
	CHUNK_CAMERA_CONT_POS		= 0x2000,
	CHUNK_CAMERA_CONT_TGT		= 0x2001,
	CHUNK_CAMERA_CONT_ROLL		= 0x2002,
	CHUNK_CAMERA_CONT_FOV		= 0x2003,
	CHUNK_CAMERA_CONT_NEARPLANE	= 0x2004,
	CHUNK_CAMERA_CONT_FARPLANE	= 0x2005,
};

const uint32	CAMERA_VERSION_1 = 1;
const uint32	CAMERA_VERSION = 2;


uint32
CameraC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;

	std::string	sStr;
	float32	f32Temp[3];

	// camera base (static)
	pSave->begin_chunk( CHUNK_CAMERA_BASE, CAMERA_VERSION );
		// write name
		sStr = m_sName;
		if( sStr.size() > 255 )
			sStr.resize( 255 );
		ui32Error = pSave->write_str( sStr.c_str() );

		// position
		f32Temp[0] = m_rPos[0];
		f32Temp[1] = m_rPos[1];
		f32Temp[2] = m_rPos[2];
		ui32Error = pSave->write( f32Temp, sizeof( f32Temp ) );

		// target pos
		f32Temp[0] = m_rTgt[0];
		f32Temp[1] = m_rTgt[1];
		f32Temp[2] = m_rTgt[2];
		ui32Error = pSave->write( f32Temp, sizeof( f32Temp ) );

		// roll
		ui32Error = pSave->write( &m_f32Roll, sizeof( m_f32Roll ) );

		// FOV
		ui32Error = pSave->write( &m_f32FOV, sizeof( m_f32FOV ) );

		// near plane
		ui32Error = pSave->write( &m_f32NearPlane, sizeof( m_f32NearPlane ) );

		// far plane
		ui32Error = pSave->write( &m_f32FarPlane, sizeof( m_f32FarPlane ) );

	pSave->end_chunk();

	// controllers
	if( m_pPosCont ) {
		pSave->begin_chunk( CHUNK_CAMERA_CONT_POS, CAMERA_VERSION );
			m_pPosCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pTgtCont ) {
		pSave->begin_chunk( CHUNK_CAMERA_CONT_TGT, CAMERA_VERSION );
			m_pTgtCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pRollCont ) {
		pSave->begin_chunk( CHUNK_CAMERA_CONT_ROLL, CAMERA_VERSION );
			m_pRollCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pFOVCont ) {
		pSave->begin_chunk( CHUNK_CAMERA_CONT_FOV, CAMERA_VERSION );
			m_pFOVCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pNearPlaneCont ) {
		pSave->begin_chunk( CHUNK_CAMERA_CONT_NEARPLANE, CAMERA_VERSION );
			m_pNearPlaneCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pFarPlaneCont ) {
		pSave->begin_chunk( CHUNK_CAMERA_CONT_FARPLANE, CAMERA_VERSION );
			m_pFarPlaneCont->save( pSave );
		pSave->end_chunk();
	}

	return ui32Error;
}

uint32
CameraC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	char	szStr[256];
	float32	f32Temp[3];

	while( (ui32Error = pLoad->open_chunk()) == IO_OK ) {

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_CAMERA_BASE:
			{
				if( pLoad->get_chunk_version() == CAMERA_VERSION ||
					pLoad->get_chunk_version() == CAMERA_VERSION_1 ) {
					// read name
					ui32Error = pLoad->read_str( szStr );
					m_sName = szStr;

					// position
					ui32Error = pLoad->read( f32Temp, sizeof( f32Temp ) );
					m_rPos[0] = f32Temp[0];
					m_rPos[1] = f32Temp[1];
					m_rPos[2] = f32Temp[2];

					// target pos
					ui32Error = pLoad->read( f32Temp, sizeof( f32Temp ) );
					m_rTgt[0] = f32Temp[0];
					m_rTgt[1] = f32Temp[1];
					m_rTgt[2] = f32Temp[2];

					// roll
					ui32Error = pLoad->read( &m_f32Roll, sizeof( m_f32Roll ) );

					// FOV
					ui32Error = pLoad->read( &m_f32FOV, sizeof( m_f32FOV ) );

					// near plane
					ui32Error = pLoad->read( &m_f32NearPlane, sizeof( m_f32NearPlane ) );

					// far plane
					ui32Error = pLoad->read( &m_f32FarPlane, sizeof( m_f32FarPlane ) );
				}
			}
			break;

		case CHUNK_CAMERA_CONT_POS:
			{
				if( pLoad->get_chunk_version() == CAMERA_VERSION ) {
					ContVector3C*	pCont = new ContVector3C;
					ui32Error = pCont->load( pLoad );
					m_pPosCont = pCont;
				}
				else if( pLoad->get_chunk_version() == CAMERA_VERSION_1 ) {
					ControllerC*	pCont = ControllerC::create_new( CONT_TYPE_VECTOR3 );
					ui32Error = pCont->load( pLoad );

					if( ui32Error == IO_OK ) {
						uint32	ui32Type = KEY_SMOOTH;
						if( pCont->get_key_count() )
							ui32Type = pCont->get_key( 0 )->get_flags() & (KEY_LINEAR | KEY_SMOOTH);
						if( (ui32Type != KEY_SMOOTH) && (ui32Type != KEY_LINEAR) )
							ui32Type = KEY_SMOOTH;

						ContVector3C*	pVecCont = new ContVector3C( ui32Type );

						pVecCont->set_start_ort( pCont->get_start_ort() );
						pVecCont->set_end_ort( pCont->get_end_ort() );

						for( uint32 i = 0; i < pCont->get_key_count(); i++ ) {

							KeyC*	pKey = pCont->get_key( i );
							float32	f32Values[KEY_MAXCHANNELS];
							pKey->get_value( f32Values );

							KeyVector3C*	pVecKey = pVecCont->add_key();

							pVecKey->set_time( pKey->get_time() );

							TRACE( "time: %d (%f %f %f)\n", pKey->get_time(), f32Values[0], f32Values[1], f32Values[2] );

							pVecKey->set_value( Vector3C( f32Values[0], f32Values[1], f32Values[2] ) );
							pVecKey->set_tens( pKey->get_tens() );
							pVecKey->set_cont( pKey->get_cont() );
							pVecKey->set_bias( pKey->get_bias() );
							pVecKey->set_ease_in( pKey->get_ease_in() );
							pVecKey->set_ease_out( pKey->get_ease_out() );
						}

						pVecCont->sort_keys();
						pVecCont->prepare();

						m_pPosCont = pVecCont;
					}

					pCont->release();
				}
			}
			break;

		case CHUNK_CAMERA_CONT_TGT:
			{
				if( pLoad->get_chunk_version() == CAMERA_VERSION ) {
					ContVector3C*	pCont = new ContVector3C;
					ui32Error = pCont->load( pLoad );
					m_pTgtCont = pCont;
				}
				else if( pLoad->get_chunk_version() == CAMERA_VERSION_1 ) {
					ControllerC*	pCont = ControllerC::create_new( CONT_TYPE_VECTOR3 );
					ui32Error = pCont->load( pLoad );

					if( ui32Error == IO_OK ) {
						uint32	ui32Type = KEY_SMOOTH;
						if( pCont->get_key_count() )
							ui32Type = pCont->get_key( 0 )->get_flags() & (KEY_LINEAR | KEY_SMOOTH);
						if( (ui32Type != KEY_SMOOTH) && (ui32Type != KEY_LINEAR) )
							ui32Type = KEY_SMOOTH;

						ContVector3C*	pVecCont = new ContVector3C( ui32Type );

						pVecCont->set_start_ort( pCont->get_start_ort() );
						pVecCont->set_end_ort( pCont->get_end_ort() );

						for( uint32 i = 0; i < pCont->get_key_count(); i++ ) {

							KeyC*	pKey = pCont->get_key( i );
							float32	f32Values[KEY_MAXCHANNELS];
							pKey->get_value( f32Values );

							KeyVector3C*	pVecKey = pVecCont->add_key();

							pVecKey->set_time( pKey->get_time() );
							pVecKey->set_value( Vector3C( f32Values[0], f32Values[1], f32Values[2] ) );
							pVecKey->set_tens( pKey->get_tens() );
							pVecKey->set_cont( pKey->get_cont() );
							pVecKey->set_bias( pKey->get_bias() );
							pVecKey->set_ease_in( pKey->get_ease_in() );
							pVecKey->set_ease_out( pKey->get_ease_out() );
						}

						pVecCont->sort_keys();
						pVecCont->prepare();

						m_pTgtCont = pVecCont;
					}

					pCont->release();
				}
			}
			break;

		case CHUNK_CAMERA_CONT_ROLL:
			{
				if( pLoad->get_chunk_version() == CAMERA_VERSION ) {
					ContFloatC*	pCont = new ContFloatC;
					ui32Error = pCont->load( pLoad );
					m_pRollCont = pCont;
				}
				else if( pLoad->get_chunk_version() == CAMERA_VERSION_1 ) {
					ControllerC*	pCont = ControllerC::create_new( CONT_TYPE_VECTOR3 );
					ui32Error = pCont->load( pLoad );

					if( ui32Error == IO_OK ) {
						uint32	ui32Type = KEY_SMOOTH;
						if( pCont->get_key_count() )
							ui32Type = pCont->get_key( 0 )->get_flags() & (KEY_LINEAR | KEY_SMOOTH);
						if( (ui32Type != KEY_SMOOTH) && (ui32Type != KEY_LINEAR) )
							ui32Type = KEY_SMOOTH;

						ContFloatC*	pFloatCont = new ContFloatC( ui32Type );

						pFloatCont->set_start_ort( pCont->get_start_ort() );
						pFloatCont->set_end_ort( pCont->get_end_ort() );

						for( uint32 i = 0; i < pCont->get_key_count(); i++ ) {

							KeyC*	pKey = pCont->get_key( i );
							float32	f32Values[KEY_MAXCHANNELS];
							pKey->get_value( f32Values );

							KeyFloatC*	pFloatKey = pFloatCont->add_key();

							pFloatKey->set_time( pKey->get_time() );
							pFloatKey->set_value( f32Values[0] );
							pFloatKey->set_tens( pKey->get_tens() );
							pFloatKey->set_cont( pKey->get_cont() );
							pFloatKey->set_bias( pKey->get_bias() );
							pFloatKey->set_ease_in( pKey->get_ease_in() );
							pFloatKey->set_ease_out( pKey->get_ease_out() );
						}

						pFloatCont->sort_keys();
						pFloatCont->prepare();

						m_pRollCont = pFloatCont;
					}

					pCont->release();
				}
			}
			break;

		case CHUNK_CAMERA_CONT_FOV:
			{
				if( pLoad->get_chunk_version() == CAMERA_VERSION ) {
					ContFloatC*	pCont = new ContFloatC;
					ui32Error = pCont->load( pLoad );
					m_pFOVCont = pCont;
				}
				else if( pLoad->get_chunk_version() == CAMERA_VERSION_1 ) {
					ControllerC*	pCont = ControllerC::create_new( CONT_TYPE_VECTOR3 );
					ui32Error = pCont->load( pLoad );

					if( ui32Error == IO_OK ) {
						uint32	ui32Type = KEY_SMOOTH;
						if( pCont->get_key_count() )
							ui32Type = pCont->get_key( 0 )->get_flags() & (KEY_LINEAR | KEY_SMOOTH);
						if( (ui32Type != KEY_SMOOTH) && (ui32Type != KEY_LINEAR) )
							ui32Type = KEY_SMOOTH;

						ContFloatC*	pFloatCont = new ContFloatC( ui32Type );

						pFloatCont->set_start_ort( pCont->get_start_ort() );
						pFloatCont->set_end_ort( pCont->get_end_ort() );

						for( uint32 i = 0; i < pCont->get_key_count(); i++ ) {

							KeyC*	pKey = pCont->get_key( i );
							float32	f32Values[KEY_MAXCHANNELS];
							pKey->get_value( f32Values );

							KeyFloatC*	pFloatKey = pFloatCont->add_key();

							pFloatKey->set_time( pKey->get_time() );
							pFloatKey->set_value( f32Values[0] );
							pFloatKey->set_tens( pKey->get_tens() );
							pFloatKey->set_cont( pKey->get_cont() );
							pFloatKey->set_bias( pKey->get_bias() );
							pFloatKey->set_ease_in( pKey->get_ease_in() );
							pFloatKey->set_ease_out( pKey->get_ease_out() );
						}

						pFloatCont->sort_keys();
						pFloatCont->prepare();

						m_pFOVCont = pFloatCont;
					}

					pCont->release();
				}
			}
			break;

		case CHUNK_CAMERA_CONT_NEARPLANE:
			{
				if( pLoad->get_chunk_version() == CAMERA_VERSION ) {
					ContFloatC*	pCont = new ContFloatC;
					ui32Error = pCont->load( pLoad );
					m_pNearPlaneCont = pCont;
				}
				else if( pLoad->get_chunk_version() == CAMERA_VERSION_1 ) {
					ControllerC*	pCont = ControllerC::create_new( CONT_TYPE_VECTOR3 );
					ui32Error = pCont->load( pLoad );

					if( ui32Error == IO_OK ) {
						uint32	ui32Type = KEY_SMOOTH;
						if( pCont->get_key_count() )
							ui32Type = pCont->get_key( 0 )->get_flags() & (KEY_LINEAR | KEY_SMOOTH);
						if( (ui32Type != KEY_SMOOTH) && (ui32Type != KEY_LINEAR) )
							ui32Type = KEY_SMOOTH;

						ContFloatC*	pFloatCont = new ContFloatC( ui32Type );

						pFloatCont->set_start_ort( pCont->get_start_ort() );
						pFloatCont->set_end_ort( pCont->get_end_ort() );

						for( uint32 i = 0; i < pCont->get_key_count(); i++ ) {

							KeyC*	pKey = pCont->get_key( i );
							float32	f32Values[KEY_MAXCHANNELS];
							pKey->get_value( f32Values );

							KeyFloatC*	pFloatKey = pFloatCont->add_key();

							pFloatKey->set_time( pKey->get_time() );
							pFloatKey->set_value( f32Values[0] );
							pFloatKey->set_tens( pKey->get_tens() );
							pFloatKey->set_cont( pKey->get_cont() );
							pFloatKey->set_bias( pKey->get_bias() );
							pFloatKey->set_ease_in( pKey->get_ease_in() );
							pFloatKey->set_ease_out( pKey->get_ease_out() );
						}

						pFloatCont->sort_keys();
						pFloatCont->prepare();

						m_pNearPlaneCont = pFloatCont;
					}

					pCont->release();
				}
			}
			break;

		case CHUNK_CAMERA_CONT_FARPLANE:
			{
				if( pLoad->get_chunk_version() == CAMERA_VERSION ) {
					ContFloatC*	pCont = new ContFloatC;
					ui32Error = pCont->load( pLoad );
					m_pFarPlaneCont = pCont;
				}
				else if( pLoad->get_chunk_version() == CAMERA_VERSION_1 ) {
					ControllerC*	pCont = ControllerC::create_new( CONT_TYPE_VECTOR3 );
					ui32Error = pCont->load( pLoad );

					if( ui32Error == IO_OK ) {
						uint32	ui32Type = KEY_SMOOTH;
						if( pCont->get_key_count() )
							ui32Type = pCont->get_key( 0 )->get_flags() & (KEY_LINEAR | KEY_SMOOTH);
						if( (ui32Type != KEY_SMOOTH) && (ui32Type != KEY_LINEAR) )
							ui32Type = KEY_SMOOTH;

						ContFloatC*	pFloatCont = new ContFloatC( ui32Type );

						pFloatCont->set_start_ort( pCont->get_start_ort() );
						pFloatCont->set_end_ort( pCont->get_end_ort() );

						for( uint32 i = 0; i < pCont->get_key_count(); i++ ) {

							KeyC*	pKey = pCont->get_key( i );
							float32	f32Values[KEY_MAXCHANNELS];
							pKey->get_value( f32Values );

							KeyFloatC*	pFloatKey = pFloatCont->add_key();

							pFloatKey->set_time( pKey->get_time() );
							pFloatKey->set_value( f32Values[0] );
							pFloatKey->set_tens( pKey->get_tens() );
							pFloatKey->set_cont( pKey->get_cont() );
							pFloatKey->set_bias( pKey->get_bias() );
							pFloatKey->set_ease_in( pKey->get_ease_in() );
							pFloatKey->set_ease_out( pKey->get_ease_out() );
						}

						pFloatCont->sort_keys();
						pFloatCont->prepare();

						m_pFarPlaneCont = pFloatCont;
					}

					pCont->release();
				}
			}
			break;

		default:
			assert( 0 );
		}

		pLoad->close_chunk();

		if( ui32Error != IO_OK && ui32Error != IO_END ) {
			return ui32Error;
		}
	}

	if( ui32Error != IO_OK && ui32Error != IO_END ) {
		return ui32Error;
	}

	return IO_OK;
}
