

#include <assert.h>
#include "ScenegraphItemI.h"
#include "PajaTypes.h"
#include "MeshC.h"


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


MeshC::MeshC() :
	m_pPosCont( 0 ),
	m_pRotCont( 0 ),
	m_pScaleCont( 0 ),
	m_pScaleRotCont( 0 ),
	m_pTextureHandle( 0 )
{
	// empty
}

MeshC::~MeshC()
{
	delete m_pPosCont;
	delete m_pRotCont;
	delete m_pScaleCont;
	delete m_pScaleRotCont;
}

int32
MeshC::get_type()
{
	return CGITEM_MESH;
}

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


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

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

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

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

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

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

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


void
MeshC::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 );
}

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

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

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

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

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

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

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

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

void
MeshC::add_index( uint32 ui32Index )
{
	m_rIndices.push_back( ui32Index );
}

void
MeshC::add_vert( const Vector3C& rVec )
{
	m_rVertices.push_back( rVec );
}

void
MeshC::add_norm( const Vector3C& rNorm )
{
	m_rNormals.push_back( rNorm );
}

void
MeshC::add_texcoord_index( uint32 ui32Index )
{
	m_rTexIndices.push_back( ui32Index );
}

void
MeshC::add_texcoord( const Vector2C& rTex )
{
	m_rTexCoords.push_back( rTex );
}

uint32
MeshC::get_index_count() const
{
	return m_rIndices.size();
}

uint32
MeshC::get_texcoord_index_count() const
{
	return m_rTexIndices.size();
}

uint32
MeshC::get_vert_count() const
{
	return m_rVertices.size();
}

uint32
MeshC::get_norm_count() const
{
	return m_rNormals.size();
}

uint32
MeshC::get_texcoord_count() const
{
	return m_rTexCoords.size();
}

uint32
MeshC::get_index( uint32 ui32Index )
{
	assert( ui32Index < m_rIndices.size() );
	return m_rIndices[ui32Index];
}

uint32
MeshC::get_texcoord_index( uint32 ui32Index )
{
	assert( ui32Index < m_rTexIndices.size() );
	return m_rTexIndices[ui32Index];
}

const Vector3C&
MeshC::get_vert( uint32 ui32Index )
{
	assert( ui32Index < m_rVertices.size() );
	return m_rVertices[ui32Index];
}

const Vector3C&
MeshC::get_norm( uint32 ui32Index )
{
	assert( ui32Index < m_rNormals.size() );
	return m_rNormals[ui32Index];
}

const Vector2C&
MeshC::get_texcoord( uint32 ui32Index )
{
	assert( ui32Index < m_rTexCoords.size() );
	return m_rTexCoords[ui32Index];
}

// material
void
MeshC::set_texture( FileHandleC* pHandle )
{
	m_pTextureHandle = pHandle;
}

FileHandleC*
MeshC::get_texture() const
{
	return m_pTextureHandle;
}

void
MeshC::set_ambient( const ColorC &rAmbient )
{
	m_rAmbient = rAmbient;
}

void
MeshC::set_diffuse( const ColorC &rDiffuse )
{
	m_rDiffuse = rDiffuse;
}

void
MeshC::set_specular( const ColorC &rSpecular )
{
	m_rSpecular = rSpecular;
}

void
MeshC::set_shininess( const float32 f32Shininess )
{
	m_f32Shininess = f32Shininess;
}

const ColorC&
MeshC::get_ambient() const
{
	return m_rAmbient;
}

const ColorC&
MeshC::get_diffuse() const
{
	return m_rDiffuse;
}

const ColorC&
MeshC::get_specular() const
{
	return m_rSpecular;
}

float32
MeshC::get_shininess()
{
	return m_f32Shininess;
}


enum MeshChunksE {
	CHUNK_MESH_BASE				= 0x1000,
	CHUNK_MESH_STATIC			= 0x1001,
	CHUNK_MESH_GEOMETRY			= 0x1002,
	CHUNK_MESH_MATERIAL			= 0x1003,
	CHUNK_MESH_CONT_POS			= 0x2000,
	CHUNK_MESH_CONT_ROT			= 0x2001,
	CHUNK_MESH_CONT_SCALE		= 0x2002,
	CHUNK_MESH_CONT_SCALEROT	= 0x2003,
};

const uint32	MESH_VERSION_1 = 1;
const uint32	MESH_VERSION = 2;

uint32
MeshC::save( SaveC* pSave )
{
	uint32	ui32Error = IO_OK;
	uint32	ui32Temp;
	float32	f32Temp2[2];
	float32	f32Temp3[3];
	float32	f32Temp4[4];
	uint8	ui8Color[4];
	uint32	i;

	eval_state( 0 );


	// Sace scenegraphitem base.
	pSave->begin_chunk( CHUNK_MESH_BASE, MESH_VERSION );
		ScenegraphItemI::save( pSave );
	pSave->end_chunk();

	// Save static transformation.
	pSave->begin_chunk( CHUNK_MESH_STATIC, MESH_VERSION );
		// 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();

	// Save geometry
	pSave->begin_chunk( CHUNK_MESH_GEOMETRY, MESH_VERSION );
		// indices
		ui32Temp = m_rIndices.size();
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
		for( i = 0; i < m_rIndices.size(); i++ ) {
			ui32Temp = m_rIndices[i];
			ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
		}

		// tex indices
		ui32Temp = m_rTexIndices.size();
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
		for( i = 0; i < m_rTexIndices.size(); i++ ) {
			ui32Temp = m_rTexIndices[i];
			ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
		}

		// vertices
		ui32Temp = m_rVertices.size();
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
		for( i = 0; i < m_rVertices.size(); i++ ) {
			f32Temp3[0] = m_rVertices[i][0];
			f32Temp3[1] = m_rVertices[i][1];
			f32Temp3[2] = m_rVertices[i][2];
			ui32Error = pSave->write( f32Temp3, sizeof( f32Temp3 ) );
		}

		// normals
		ui32Temp = m_rNormals.size();
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
		for( i = 0; i < m_rNormals.size(); i++ ) {
			f32Temp3[0] = m_rNormals[i][0];
			f32Temp3[1] = m_rNormals[i][1];
			f32Temp3[2] = m_rNormals[i][2];
			ui32Error = pSave->write( f32Temp3, sizeof( f32Temp3 ) );
		}

		// texcoords
		ui32Temp = m_rTexCoords.size();
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
		for( i = 0; i < m_rTexCoords.size(); i++ ) {
			f32Temp2[0] = m_rTexCoords[i][0];
			f32Temp2[1] = m_rTexCoords[i][1];
			ui32Error = pSave->write( f32Temp2, sizeof( f32Temp2 ) );
		}
	pSave->end_chunk();

	pSave->begin_chunk( CHUNK_MESH_MATERIAL, MESH_VERSION );
		//ambient
		ui8Color[0] = (uint8)(m_rAmbient[0] * 255.0f);
		ui8Color[1] = (uint8)(m_rAmbient[1] * 255.0f);
		ui8Color[2] = (uint8)(m_rAmbient[2] * 255.0f);
		ui8Color[3] = (uint8)(m_rAmbient[3] * 255.0f);
		ui32Error = pSave->write( ui8Color, 4 );

		//diffuse
		ui8Color[0] = (uint8)(m_rDiffuse[0] * 255.0f);
		ui8Color[1] = (uint8)(m_rDiffuse[1] * 255.0f);
		ui8Color[2] = (uint8)(m_rDiffuse[2] * 255.0f);
		ui8Color[3] = (uint8)(m_rDiffuse[3] * 255.0f);
		ui32Error = pSave->write( ui8Color, 4 );

		//specular
		ui8Color[0] = (uint8)(m_rSpecular[0] * 255.0f);
		ui8Color[1] = (uint8)(m_rSpecular[1] * 255.0f);
		ui8Color[2] = (uint8)(m_rSpecular[2] * 255.0f);
		ui8Color[3] = (uint8)(m_rSpecular[3] * 255.0f);
		ui32Error = pSave->write( ui8Color, 4 );

		// shininess
		ui32Error = pSave->write( &m_f32Shininess, sizeof( m_f32Shininess ) );

		//
		// When a file is saved, each filehandle is given an ID (a running from the
		// forst handle). Later on load time that ID can be used to tell the Demopaja
		// system to patch the filehandle after the whle file is loaded. ID value of
		// -1 (0xffffffff) can be used to indicate no handle.
		//
		if( m_pTextureHandle )
			ui32Temp = m_pTextureHandle->get_id();
		else
			ui32Temp = 0xffffffff;
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
	pSave->end_chunk();

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

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

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

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

	return ui32Error;
}

uint32
MeshC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	uint32	ui32Temp;
	float32	f32Temp2[2];
	float32	f32Temp3[3];
	float32	f32Temp4[4];
	uint8	ui8Color[4];

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

		switch( pLoad->get_chunk_id() ) {

		case CHUNK_MESH_BASE:
			if( pLoad->get_chunk_version() <= MESH_VERSION )
				ScenegraphItemI::load( pLoad );
			break;

		case CHUNK_MESH_STATIC:
			if( pLoad->get_chunk_version() <= MESH_VERSION ) {

				// 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_MESH_MATERIAL:
			if( pLoad->get_chunk_version() <= MESH_VERSION ) {
				//ambient
				ui32Error = pLoad->read( ui8Color, 4 );
				m_rAmbient.convert_from_uint8( ui8Color[0], ui8Color[1], ui8Color[2], ui8Color[3] );

				//diffuse
				ui32Error = pLoad->read( ui8Color, 4 );
				m_rDiffuse.convert_from_uint8( ui8Color[0], ui8Color[1], ui8Color[2], ui8Color[3] );

				//specular
				ui32Error = pLoad->read( ui8Color, 4 );
				m_rSpecular.convert_from_uint8( ui8Color[0], ui8Color[1], ui8Color[2], ui8Color[3] );

				// shininess
				ui32Error = pLoad->read( &m_f32Shininess, sizeof( m_f32Shininess ) );

				//
				// Load texture handle. On save time each filehandle is given an ID and
				// that ID is saved. When load the ID can be used to ask the system to patch
				// the file pointer.
				//
				ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
				if( ui32Temp != 0xffffffff )
					pLoad->add_file_handle_patch( (void**)&m_pTextureHandle, ui32Temp );
			}
			break;

		case CHUNK_MESH_GEOMETRY:
			if( pLoad->get_chunk_version() <= MESH_VERSION ) {
				uint32	i, ui32Count;
				// indices
				ui32Error = pLoad->read( &ui32Count, sizeof( ui32Count ) );
				for( i = 0; i < ui32Count; i++ ) {
					ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
					add_index( ui32Temp );
				}

				// tex indices
				ui32Error = pLoad->read( &ui32Count, sizeof( ui32Count ) );
				for( i = 0; i < ui32Count; i++ ) {
					ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
					add_texcoord_index( ui32Temp );
				}

				// vertices
				ui32Error = pLoad->read( &ui32Count, sizeof( ui32Count ) );
				for( i = 0; i < ui32Count; i++ ) {
					ui32Error = pLoad->read( f32Temp3, sizeof( f32Temp3 ) );
					add_vert( Vector3C( f32Temp3 ) );
				}

				// normals
				ui32Error = pLoad->read( &ui32Count, sizeof( ui32Count ) );
				for( i = 0; i < ui32Count; i++ ) {
					ui32Error = pLoad->read( f32Temp3, sizeof( f32Temp3 ) );
					add_norm( Vector3C( f32Temp3 ) );
				}

				if( pLoad->get_chunk_version() == MESH_VERSION_1 ) {
					// texcoords
					ui32Error = pLoad->read( &ui32Count, sizeof( ui32Count ) );
					for( i = 0; i < ui32Count; i++ ) {
						ui32Error = pLoad->read( f32Temp2, sizeof( f32Temp2 ) );
						// flip tex coords
						add_texcoord( Vector2C( f32Temp2[0], 1.0f - f32Temp2[1] ) );
					}
				}
				else {
					// texcoords
					ui32Error = pLoad->read( &ui32Count, sizeof( ui32Count ) );
					for( i = 0; i < ui32Count; i++ ) {
						ui32Error = pLoad->read( f32Temp2, sizeof( f32Temp2 ) );
						add_texcoord( Vector2C( f32Temp2 ) );
					}
				}
			}
			break;

		case CHUNK_MESH_CONT_POS:
			if( pLoad->get_chunk_version() <= MESH_VERSION ) {
				ContVector3C*	pCont = new ContVector3C;
				ui32Error = pCont->load( pLoad );
				m_pPosCont = pCont;
			}
			break;

		case CHUNK_MESH_CONT_ROT:
			if( pLoad->get_chunk_version() <= MESH_VERSION ) {
				ContQuatC*	pCont = new ContQuatC;
				ui32Error = pCont->load( pLoad );
				m_pRotCont = pCont;
			}
			break;

		case CHUNK_MESH_CONT_SCALE:
			if( pLoad->get_chunk_version() <= MESH_VERSION ) {
				ContVector3C*	pCont = new ContVector3C;
				ui32Error = pCont->load( pLoad );
				m_pScaleCont = pCont;
			}
			break;

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