#include "PajaTypes.h"
#include "Vector3C.h"
#include "Mesh3DC.h"
#include "ControllerC.h"
#include "ContVector3C.h"
#include "ContFloatC.h"
#include "ContQuatC.h"
#include <vector>
#include "ScenegraphItemI.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 );
}



ScenegraphItemI::ScenegraphItemI() :
	m_pMesh( 0 ),
	m_pPosCont( 0 ),
	m_pRotCont( 0 ),
	m_pScaleCont( 0 ),
	m_pScaleRotCont( 0 ),
	m_pVisCont( 0 ),
	m_bBBoxEmpty( false ),
	m_bVisible( true )
{
	// empty
}


ScenegraphItemI::~ScenegraphItemI()
{
	delete m_pPosCont;
	delete m_pRotCont;
	delete m_pScaleCont;
	delete m_pScaleRotCont;
	delete m_pVisCont;

	for( uint32 i = 0; i < m_rChilds.size(); i++ )
		delete m_rChilds[i];
}


void
ScenegraphItemI::set_mesh( Mesh3DC* pMesh )
{
	m_pMesh = pMesh;
}

Mesh3DC*
ScenegraphItemI::get_mesh()
{
	return m_pMesh;
}

void
ScenegraphItemI::set_position( const Vector3C& rPos )
{
	m_rPosition = rPos;
}


const
Vector3C&
ScenegraphItemI::get_position()
{
	return m_rPosition;
}

void
ScenegraphItemI::set_scale( const Vector3C& rScale )
{
	m_rScale = rScale;
}

const
Vector3C&
ScenegraphItemI::get_scale()
{
	return m_rScale;
}

void
ScenegraphItemI::set_rotation( const QuatC& rRot )
{
	m_rRotation = rRot;
}

const
QuatC&
ScenegraphItemI::get_rotation()
{
	return m_rRotation;
}

void
ScenegraphItemI::set_scale_rotation( const QuatC& rRot )
{
	m_rScaleRot = rRot;
}

const
QuatC&
ScenegraphItemI::get_scale_rotation()
{
	return m_rScaleRot;
}

void
ScenegraphItemI::get_bounds( Vector3C& rMin, Vector3C& rMax )
{
	if( m_pMesh )
		m_pMesh->get_bounds( rMin, rMax );

	rMin = rMax = Vector3C( 0, 0, 0 );
}

void
ScenegraphItemI::eval_state( int32 i32Time )
{
	if( m_pPosCont )
		m_rPosition = m_pPosCont->get_value( i32Time );
	if( m_pScaleCont )
		m_rScale = m_pScaleCont->get_value( i32Time );
	if( m_pScaleRotCont )
		m_rScaleRot = m_pScaleRotCont->get_value( i32Time );
	if( m_pRotCont )
		m_rRotation = m_pRotCont->get_value( i32Time );
	if( m_pVisCont ) {
		float32	f32Vis = m_pVisCont->get_value( i32Time );
		m_bVisible = f32Vis >= 0 ? true : false;
	}
}

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

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

void
ScenegraphItemI::set_rotation_controller( ContQuatC* pCont )
{
	m_pRotCont = pCont;
}

ContQuatC*
ScenegraphItemI::get_rotation_controller()
{
	return m_pRotCont;
}

void
ScenegraphItemI::set_scale_controller( ContVector3C* pCont )
{
	m_pScaleCont = pCont;
}

ContVector3C*
ScenegraphItemI::get_scale_controller()
{
	return m_pScaleCont;
}

void
ScenegraphItemI::set_scale_rotation_controller( ContQuatC* pCont )
{
	m_pScaleRotCont = pCont;
}

ContQuatC*
ScenegraphItemI::get_scale_rotation_controller()
{
	return m_pScaleRotCont;
}

void
ScenegraphItemI::set_visibility_controller( ContFloatC* pCont )
{
	m_pVisCont = pCont;
}

ContFloatC*
ScenegraphItemI::get_visibility_controller()
{
	return m_pVisCont;
}

bool
ScenegraphItemI::get_visibility()
{
	return m_bVisible;
}


void
ScenegraphItemI::add_child( ScenegraphItemI* pChild )
{
	if( pChild )
		m_rChilds.push_back( pChild );
}

uint32
ScenegraphItemI::get_child_count()
{
	return m_rChilds.size();
}

ScenegraphItemI*
ScenegraphItemI::get_child( uint32 ui32Index )
{
	if( ui32Index >= 0 && ui32Index < m_rChilds.size() )
		return m_rChilds[ui32Index];
	return 0;
}

void
ScenegraphItemI::remove_child( uint32 ui32Index )
{
	if( ui32Index >= 0 && ui32Index < m_rChilds.size() )
		m_rChilds.erase( m_rChilds.begin() + ui32Index );
}

void
ScenegraphItemI::set_id( uint32 ui32ID )
{
	m_ui32ID = ui32ID;
}

uint32
ScenegraphItemI::get_id() const
{
	return m_ui32ID;
}

uint32
ScenegraphItemI::get_mesh_id() const
{
	return m_ui32MeshID;
}

enum CGItemChunksE {
	CHUNK_CGITEM_BASE			= 0x1001,
	CHUNK_CGITEM_CONT_POS		= 0x2000,
	CHUNK_CGITEM_CONT_ROT		= 0x2001,
	CHUNK_CGITEM_CONT_SCALE		= 0x2002,
	CHUNK_CGITEM_CONT_SCALEROT	= 0x2003,
	CHUNK_CGITEM_CONT_VIS		= 0x2004,
	CHUNK_CGITEM_CHILD			= 0x3000,
};

const uint32	CGITEM_VERSION_1 = 1;
const uint32	CGITEM_VERSION = 2;

uint32
ScenegraphItemI::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	uint32	ui32Temp;
	float32	f32Temp3[3];
	float32	f32Temp4[4];

	eval_state( 0 );

	pSave->begin_chunk( CHUNK_CGITEM_BASE, CGITEM_VERSION );
		// mesh
		if( m_pMesh )
			ui32Temp = m_pMesh->get_id();
		else
			ui32Temp = 0xffffffff;
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
		// position
		f32Temp3[0] = m_rPosition[0];
		f32Temp3[1] = m_rPosition[1];
		f32Temp3[2] = m_rPosition[2];
		ui32Error = pSave->write( f32Temp3, sizeof( f32Temp3 ) );
		// rotation
		f32Temp4[0] = m_rRotation[0];
		f32Temp4[1] = m_rRotation[1];
		f32Temp4[2] = m_rRotation[2];
		f32Temp4[3] = m_rRotation[3];
		ui32Error = pSave->write( f32Temp4, sizeof( f32Temp4 ) );
		// scale
		f32Temp3[0] = m_rScale[0];
		f32Temp3[1] = m_rScale[1];
		f32Temp3[2] = m_rScale[2];
		ui32Error = pSave->write( f32Temp3, sizeof( f32Temp3 ) );
		// scale rot
		f32Temp4[0] = m_rScaleRot[0];
		f32Temp4[1] = m_rScaleRot[1];
		f32Temp4[2] = m_rScaleRot[2];
		f32Temp4[3] = m_rScaleRot[3];
		ui32Error = pSave->write( f32Temp4, sizeof( f32Temp4 ) );
	pSave->end_chunk();

	// pos controller
	if( m_pPosCont ) {
		pSave->begin_chunk( CHUNK_CGITEM_CONT_POS, CGITEM_VERSION );
			m_pPosCont->save( pSave );
		pSave->end_chunk();
	}

	// rot controller
	if( m_pRotCont ) {
		pSave->begin_chunk( CHUNK_CGITEM_CONT_ROT, CGITEM_VERSION );
			m_pRotCont->save( pSave );
		pSave->end_chunk();
	}

	// scale controller
	if( m_pScaleCont ) {
		pSave->begin_chunk( CHUNK_CGITEM_CONT_SCALE, CGITEM_VERSION );
			m_pScaleCont->save( pSave );
		pSave->end_chunk();
	}

	// scale rot controller
	if( m_pScaleRotCont ) {
		pSave->begin_chunk( CHUNK_CGITEM_CONT_SCALEROT, CGITEM_VERSION );
			m_pScaleRotCont->save( pSave );
		pSave->end_chunk();
	}

	// vis controller
	if( m_pVisCont ) {
		pSave->begin_chunk( CHUNK_CGITEM_CONT_VIS, CGITEM_VERSION );
			m_pVisCont->save( pSave );
		pSave->end_chunk();
	}

	return ui32Error;
}

uint32
ScenegraphItemI::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	float32	f32Temp3[3];
	float32	f32Temp4[4];

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

		switch( pLoad->get_chunk_id() ) {
		case CHUNK_CGITEM_BASE:
			{
				if( pLoad->get_chunk_version() == CGITEM_VERSION ||
					pLoad->get_chunk_version() == CGITEM_VERSION_1 ) {
					// mesh
					ui32Error = pLoad->read( &m_ui32MeshID, sizeof( m_ui32MeshID ) );

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

					// rotation
					ui32Error = pLoad->read( f32Temp4, sizeof( f32Temp4 ) );
					m_rRotation[0] = f32Temp4[0];
					m_rRotation[1] = f32Temp4[1];
					m_rRotation[2] = f32Temp4[2];
					m_rRotation[3] = f32Temp4[3];

					// scale
					ui32Error = pLoad->read( f32Temp3, sizeof( f32Temp3 ) );
					m_rScale[0] = f32Temp3[0];
					m_rScale[1] = f32Temp3[1];
					m_rScale[2] = f32Temp3[2];

					// scale rot
					ui32Error = pLoad->read( f32Temp4, sizeof( f32Temp4 ) );
					m_rScaleRot[0] = f32Temp4[0];
					m_rScaleRot[1] = f32Temp4[1];
					m_rScaleRot[2] = f32Temp4[2];
					m_rScaleRot[3] = f32Temp4[3];
				}
			}
			break;

		case CHUNK_CGITEM_CONT_POS:
			{
				if( pLoad->get_chunk_version() == CGITEM_VERSION ) {
					ContVector3C*	pCont = new ContVector3C;
					ui32Error = pCont->load( pLoad );
					m_pPosCont = pCont;
				}
				else if( pLoad->get_chunk_version() == CGITEM_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_pPosCont = pVecCont;
					}

					pCont->release();
				}
			}
			break;

		case CHUNK_CGITEM_CONT_ROT:
			{
				if( pLoad->get_chunk_version() == CGITEM_VERSION ) {
					ContQuatC*	pCont = new ContQuatC;
					ui32Error = pCont->load( pLoad );
					m_pRotCont = pCont;
				}
				else if( pLoad->get_chunk_version() == CGITEM_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;

						ContQuatC*	pQuatCont = new ContQuatC( ui32Type );

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

						QuatC	rPrevQuat;
						bool	bFirst = true;

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

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

							QuatC		rQuat( f32Values[0], f32Values[1], f32Values[2], f32Values[3] );
							QuatC		rNewQuat;
							Vector3C	rAxis;
							float32		f32Angle;

							KeyQuatC*	pQuatKey = pQuatCont->add_key();


							if( bFirst ) {
								rNewQuat = rQuat;
								bFirst = false;
							}
							else
								rNewQuat = rPrevQuat.inverse() * rQuat;

							rPrevQuat = rQuat;

							rNewQuat.to_axis_angle( rAxis, f32Angle );

							pQuatKey->set_time( pKey->get_time() );
							pQuatKey->set_axis( rAxis );
							pQuatKey->set_angle( f32Angle );
							pQuatKey->set_tens( pKey->get_tens() );
							pQuatKey->set_cont( pKey->get_cont() );
							pQuatKey->set_bias( pKey->get_bias() );
							pQuatKey->set_ease_in( pKey->get_ease_in() );
							pQuatKey->set_ease_out( pKey->get_ease_out() );
						}

						pQuatCont->sort_keys();
						pQuatCont->prepare();

						m_pRotCont = pQuatCont;
					}

					pCont->release();
				}
			}
			break;

		case CHUNK_CGITEM_CONT_SCALE:
			{
				if( pLoad->get_chunk_version() == CGITEM_VERSION ) {
					ContVector3C*	pCont = new ContVector3C;
					ui32Error = pCont->load( pLoad );
					m_pScaleCont = pCont;
				}
				else if( pLoad->get_chunk_version() == CGITEM_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_pScaleCont = pVecCont;
					}

					pCont->release();
				}
			}
			break;

		case CHUNK_CGITEM_CONT_SCALEROT:
			{
				if( pLoad->get_chunk_version() == CGITEM_VERSION ) {
					ContQuatC*	pCont = new ContQuatC;
					ui32Error = pCont->load( pLoad );
					m_pScaleRotCont = pCont;
				}
				else if( pLoad->get_chunk_version() == CGITEM_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;

						ContQuatC*	pQuatCont = new ContQuatC( ui32Type );

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

						QuatC	rPrevQuat;
						bool	bFirst = true;

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

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

							QuatC		rQuat( f32Values[0], f32Values[1], f32Values[2], f32Values[3] );
							QuatC		rNewQuat;
							Vector3C	rAxis;
							float32		f32Angle;

							KeyQuatC*	pQuatKey = pQuatCont->add_key();


							if( bFirst ) {
								rNewQuat = rQuat;
								bFirst = false;
							}
							else
								rNewQuat = rPrevQuat.inverse() * rQuat;

							rPrevQuat = rQuat;

							rNewQuat.to_axis_angle( rAxis, f32Angle );

							pQuatKey->set_time( pKey->get_time() );
							pQuatKey->set_axis( rAxis );
							pQuatKey->set_angle( f32Angle );
							pQuatKey->set_tens( pKey->get_tens() );
							pQuatKey->set_cont( pKey->get_cont() );
							pQuatKey->set_bias( pKey->get_bias() );
							pQuatKey->set_ease_in( pKey->get_ease_in() );
							pQuatKey->set_ease_out( pKey->get_ease_out() );
						}

						pQuatCont->sort_keys();
						pQuatCont->prepare();

						m_pScaleRotCont = pQuatCont;
					}

					pCont->release();
				}
			}
			break;

		case CHUNK_CGITEM_CONT_VIS:
			{
				if( pLoad->get_chunk_version() == CGITEM_VERSION ) {
					ContFloatC*	pCont = new ContFloatC;
					ui32Error = pCont->load( pLoad );
					m_pVisCont = pCont;
				}
				else if( pLoad->get_chunk_version() == CGITEM_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_pVisCont = 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;
}
