#include "PajaTypes.h"
#include "ColorC.h"
#include "ImportableI.h"
#include "FileIO.h"
#include "ScenegraphItemI.h"
#include "QuatC.h"
#include "Vector2C.h"
#include "Vector3C.h"
#include "ContVector3C.h"
#include "ContQuatC.h"
#include "ContBoolC.h"
#include "ContFloatC.h"
#include "ControllerC.h"
#include "MaterialC.h"
#include "ParticleSystemC.h"
#include "noise.h"
#include "WSMObjectC.h"

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


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 );
}



// 3ds MAX constant
#define TIME_TICKSPERSEC	4800
#define RAINSIZEFACTOR (float(TIME_TICKSPERSEC)/120.0f)
#define PARTICLE_SEED	0x8d6a65bc
#define VEL_SCALE	(-0.01f*1200.0f/float(TIME_TICKSPERSEC))
#define VAR_SCALE	(0.01f*1200.0f/float(TIME_TICKSPERSEC))


ParticleSystemC::ParticleSystemC() :
	m_ui32ParticleType( PARTICLE_RAIN ),
	m_f32Width( 0 ),
	m_f32Height( 0 ),
	m_bVis( true ),
	m_pPosCont( 0 ),
	m_pRotCont( 0 ),
	m_pVisCont( 0 ),
	m_i32StepSize( 0 ),
	m_i32MaxParticleCount( 100 ),
	m_f32DropSize( 2.0f ),
	m_f32Speed( 10.0f ),
	m_i32StartTime( 0 ),
	m_i32LifeTime( 0 ),
	m_bConstantBirth( false ),
	m_f32TumbleScale( 1.0f ),
	m_f32Tumble( 0.0f ),
	m_f32Variation( 0.0f ),
	m_i32ValidTime( 0 ),
	m_i32LastBirthTime( 0 ),
	m_pDropSizeCont( 0 ),
	m_pSpeedCont( 0 ),
	m_pStartTimeCont( 0 ),
	m_pLifeTimeCont( 0 ),
	m_pTumbleScaleCont( 0 ),
	m_pTumbleCont( 0 ),
	m_pVariationCont( 0 ),
	m_pBirthRateCont( 0 ),
	m_pWidthCont( 0 ),
	m_pHeightCont( 0 ),
	m_pTextureHandle( 0 ),
	m_f32Transparency( 1.0f ),
	m_f32SelfIllum( 0.0f ),
	m_ui32TransparencyType( TRANSPARENCY_FILTER )
{
	// empty
}

ParticleSystemC::~ParticleSystemC()
{
	delete m_pPosCont;
	delete m_pRotCont;
	delete m_pVisCont;

	delete m_pDropSizeCont;
	delete m_pSpeedCont;
	delete m_pStartTimeCont;
	delete m_pLifeTimeCont;
	delete m_pTumbleScaleCont;
	delete m_pTumbleCont;
	delete m_pVariationCont;
	delete m_pBirthRateCont;
	delete m_pWidthCont;
	delete m_pHeightCont;
}

int32
ParticleSystemC::get_type()
{
	return CGITEM_PARTICLESYSTEM;
}

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

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

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

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

void
ParticleSystemC::eval_state( int32 i32Time )
{
	if( m_pVisCont )
		m_bVis = m_pVisCont->get_value( i32Time );

	if( m_pPosCont )
		m_rPosition = m_pPosCont->get_value( i32Time );
	if( m_pRotCont )
		m_rRotation = m_pRotCont->get_value( i32Time );

	// parameters
	if( m_pDropSizeCont )
		m_f32DropSize = m_pDropSizeCont->get_value( i32Time );
	if( m_pSpeedCont )
		m_f32Speed = m_pSpeedCont->get_value( i32Time );
	if(	m_pStartTimeCont )
		m_i32StartTime = (int32)m_pStartTimeCont->get_value( i32Time );
	if( m_pLifeTimeCont )
		m_i32LifeTime = (int32)m_pLifeTimeCont->get_value( i32Time );
	if( m_pTumbleScaleCont )
		m_f32TumbleScale = m_pTumbleScaleCont->get_value( i32Time );
	if( m_pTumbleCont )
		m_f32Tumble = m_pTumbleCont->get_value( i32Time );
	if( m_pVariationCont )
		m_f32Variation = m_pVariationCont->get_value( i32Time );
	if( m_pBirthRateCont )
		m_f32BirthRate = m_pBirthRateCont->get_value( i32Time ); // * (float32)m_i32MaxParticleCount;
	if( m_pWidthCont )
		m_f32Width = m_pWidthCont->get_value( i32Time );
	if( m_pHeightCont )
		m_f32Height = m_pHeightCont->get_value( i32Time );


	Matrix3C	rRotMat;
	QuatC		rRot( m_rRotation );

	rRotMat.set_rot( rRot );

	// Compose matrix
	m_rTM = rRotMat;
	m_rTM[3] = m_rPosition;

	if( m_pParent )
		m_rTM *= m_pParent->get_tm();
}

void
ParticleSystemC::eval_geom( int32 i32Time )
{
	update_particles( i32Time );
}

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

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

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

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

void
ParticleSystemC::set_vis_controller( ContBoolC* pCont )
{
	m_pVisCont = pCont;
}

ContBoolC*
ParticleSystemC::get_vis_controller()
{
	return m_pVisCont;
}

bool
ParticleSystemC::get_visible() const
{
	return m_bVis;
}

float32
ParticleSystemC::get_width() const
{
	return m_f32Width;
}

void
ParticleSystemC::set_width( float32 f32Width )
{
	m_f32Width = f32Width;
}

float32
ParticleSystemC::get_height() const
{
	return m_f32Height;
}

void
ParticleSystemC::set_height( float32 f32Height )
{
	m_f32Height = f32Height;
}

uint32
ParticleSystemC::get_particle_type() const
{
	return m_ui32ParticleType;
}

void
ParticleSystemC::set_particle_type( uint32 ui32Type )
{
	m_ui32ParticleType = ui32Type;
}


void
ParticleSystemC::update_dependencies( std::vector<ScenegraphItemI*> rScenegraph )
{
	uint32	i, j;
	// find parent
	if( !m_sParentName.empty() ) {
		m_pParent = 0;
		for( i = 0; i < rScenegraph.size(); i++ ) {
			if( m_sParentName.compare( rScenegraph[i]->get_name() ) == 0 ) {
				m_pParent = rScenegraph[i];
				break;
			}
		}
	}

	// find modifiers
	for( i = 0; i < m_rModifiers.size(); i++ ) {
		for( j = 0; j < rScenegraph.size(); j++ ) {

			if( m_rModifiers[i].m_sName.compare( rScenegraph[j]->get_name() ) == 0 ) {

				if( rScenegraph[j]->get_type() == CGITEM_WSMOBJECT ) {

					WSMObjectC* pObj = (WSMObjectC*)rScenegraph[j];

					if( pObj->get_force_field() ) {
//						TRACE( "force: %s\n", pObj->get_name() );
						m_rModifiers[i].m_pObject = pObj;
						m_rForceFields.push_back( pObj->get_force_field() );
					}
					else if( pObj->get_collision_object() ) {
//						TRACE( "collision: %s\n", pObj->get_name() );
						m_rModifiers[i].m_pObject = pObj;
						m_rCollisionObjs.push_back( pObj->get_collision_object() );
					}
				}

				break;
			}
		}
	}
}



uint32
ParticleSystemC::get_particle_count()
{
	return m_rPartAges.size();
}

float32*
ParticleSystemC::get_particle_position_ptr()
{
	return &(m_rPartPositions[0]);
}

float32*
ParticleSystemC::get_particle_velocity_ptr()
{
	return &(m_rPartVelocities[0]);
}

int32*
ParticleSystemC::get_particle_age_ptr()
{
	return &(m_rPartAges[0]);
}

void
ParticleSystemC::set_step_size( int32 i32Step )
{
	m_i32StepSize = i32Step;
}

int32
ParticleSystemC::get_step_size() const
{
	return m_i32StepSize;
}

void
ParticleSystemC::set_max_particle_count( int32 i32Max )
{
	m_i32MaxParticleCount = i32Max;
}

int32
ParticleSystemC::get_max_particle_count() const
{
	return m_i32MaxParticleCount;
}

void
ParticleSystemC::set_drop_size( float32 f32Size )
{
	m_f32DropSize = f32Size;
}

float32
ParticleSystemC::get_drop_size() const
{
	return m_f32DropSize;
}

void
ParticleSystemC::set_speed( float32 f32Speed )
{
	m_f32Speed = f32Speed;
}

float32
ParticleSystemC::get_speed() const
{
	return m_f32Speed;
}

void
ParticleSystemC::set_start_time( int32 i32Time )
{
	m_i32StartTime = i32Time;
}

int32
ParticleSystemC::get_start_time() const
{
	return m_i32StartTime;
}

void
ParticleSystemC::set_life_time( int32 i32Life )
{
	m_i32LifeTime = i32Life;
}

int32
ParticleSystemC::get_life_time() const
{
	return m_i32LifeTime;
}

void
ParticleSystemC::set_constant_birth_rate( bool bState )
{
	m_bConstantBirth = bState;
}

bool
ParticleSystemC::get_constant_birth_rate() const
{
	return m_bConstantBirth;
}

void
ParticleSystemC::set_birth_rate( float32 f32Rate )
{
	m_f32BirthRate = f32Rate;
}

float32
ParticleSystemC::get_birth_rate() const
{
	return m_f32BirthRate;
}


void
ParticleSystemC::set_tumble_scale( float32 f32Scale )
{
	m_f32TumbleScale = f32Scale;
}

float32
ParticleSystemC::get_tumble_scale() const
{
	return m_f32TumbleScale;
}

void
ParticleSystemC::set_tumble( float32 f32Val )
{
	m_f32Tumble = f32Val;
}

float32
ParticleSystemC::get_tumble() const
{
	return m_f32Tumble;
}

void
ParticleSystemC::set_variation( float32 f32Val )
{
	m_f32Variation = f32Val;
}

float32
ParticleSystemC::get_variation() const
{
	return m_f32Variation;
}

void
ParticleSystemC::set_drop_size_cont( ContFloatC* pCont )
{
	m_pDropSizeCont = pCont;
}

void
ParticleSystemC::set_speed_cont( ContFloatC* pCont )
{
	m_pSpeedCont = pCont;
}

void
ParticleSystemC::set_start_time_cont( ContFloatC* pCont )
{
	m_pStartTimeCont = pCont;
}

void
ParticleSystemC::set_life_time_cont( ContFloatC* pCont )
{
	m_pLifeTimeCont = pCont;
}

void
ParticleSystemC::set_tumble_scale_cont( ContFloatC* pCont )
{
	m_pTumbleScaleCont = pCont;
}

void
ParticleSystemC::set_tumble_cont( ContFloatC* pCont )
{
	m_pTumbleCont = pCont;
}

void
ParticleSystemC::set_variation_cont( ContFloatC* pCont )
{
	m_pVariationCont = pCont;
}

void
ParticleSystemC::set_birthrate_cont( ContFloatC* pCont )
{
	m_pBirthRateCont = pCont;
}

void
ParticleSystemC::set_width_cont( ContFloatC* pCont )
{
	m_pWidthCont = pCont;
}

void
ParticleSystemC::set_height_cont( ContFloatC* pCont )
{
	m_pHeightCont = pCont;
}

void
ParticleSystemC::birth_particle( int32 i32BT, int32 i32Index )
{
//	TRACE( "ParticleSystemC::birth_particle\n" );

	float32		f32InitVel, f32Var;
	float32		f32Width, f32Height;
	Vector3C	rPos, rVel;
	Matrix3C	rTM = get_tm();

	f32InitVel = get_speed();
	f32Width = get_width();
	f32Height = get_height();
	f32Var = get_variation();

	f32InitVel *= VEL_SCALE; // UI Scaling

	rVel = Vector3C( 0.0f, f32InitVel, 0.0f );
	if( f32Var != 0.0f ) {
		f32Var *= VAR_SCALE;
		rVel[0] = -f32Var + float32( rand() ) / float32( RAND_MAX ) * 2.0f * f32Var;
		rVel[1] = f32InitVel - f32Var + float32( rand()) / float32( RAND_MAX ) * 2.0f * f32Var;
		rVel[2] = -f32Var + float32( rand() ) / float32( RAND_MAX ) * 2.0f * f32Var;
	}

	int32	i32Index3 = i32Index * 3;


	m_rPartAges[i32Index] = 0;
	m_rPartVelocities[i32Index3 + 0] = rVel[0] * rTM[0][0] + rVel[1] * rTM[1][0] + rVel[2] * rTM[2][0];
	m_rPartVelocities[i32Index3 + 1] = rVel[0] * rTM[0][1] + rVel[1] * rTM[1][1] + rVel[2] * rTM[2][1];
	m_rPartVelocities[i32Index3 + 2] = rVel[0] * rTM[0][2] + rVel[1] * rTM[1][2] + rVel[2] * rTM[2][2];

	rPos[0] = -f32Width / 2.0f + float32( rand() ) / float( RAND_MAX ) * f32Width;;
	rPos[1] = 0.0f;
	rPos[2] = -f32Height / 2.0f + float32( rand() ) / float( RAND_MAX ) * f32Height;

	rPos *= rTM;

	m_rPartPositions[i32Index3 + 0] = rPos[0];
	m_rPartPositions[i32Index3 + 1] = rPos[1];
	m_rPartPositions[i32Index3 + 2] = rPos[2];

//	TRACE( "birth: %d pos( %f, %f, %f), vel(%f, %f, %f)\n", i32Index, rPos[0], rPos[1], rPos[2], rVel[0], rVel[1], rVel[2] );
}

int32
ParticleSystemC::count_live()
{
	int32	i32Count = 0;
	for( uint32 i = 0; i < m_rPartAges.size(); i++ ) {
		if( m_rPartAges[i] >= 0 )
			i32Count++;
	}
	return i32Count;
}

void
ParticleSystemC::compute_particle_start( int32 i32T0 )
{
//	TRACE( "ParticleSystemC::compute_particle_start\n" );

	int32	i32Count = get_max_particle_count();

	m_rPartPositions.resize( i32Count * 3 );
	m_rPartVelocities.resize( i32Count * 3 );
	m_rPartAges.resize( i32Count );

	for( uint32 i = 0; i < m_rPartAges.size(); i++ )
		m_rPartAges[i] = -1;

	m_i32ValidTime = i32T0;
	m_i32LastBirthTime = i32T0;
}

void
ParticleSystemC::kill_all_particles()
{
	for( uint32 i = 0; i < m_rPartAges.size(); i++ )
		m_rPartAges[i] = -1;
}

void
ParticleSystemC::update_particles( int32 i32T )
{

	int32		i32T0, i32Life, i32DT, i32OneFrame = m_i32StepSize;
	int32		i, j, i32Total, i32Birth = 0;
	float32		f32BirthRate, f32BirthRateFactor = 1.0f;
	float32		f32DropSize;
	Vector3C	rForce;

	i32T0 = get_start_time();
	i32Life = get_life_time();
	f32DropSize = get_drop_size();

	i32Total = get_max_particle_count();

	if( i32Life <= 0 )
		i32Life = 1;

	if( get_constant_birth_rate() ) {
		f32BirthRate = (float32)i32Total / (float32)i32Life;
	}
	
	if( i32T < i32T0 ) {
		// Before the start time, nothing is happening
		kill_all_particles();
		m_i32ValidTime = i32T;
		return;
	}
	
	if( !get_particle_count() || i32T < m_i32ValidTime || m_i32ValidTime < i32T0 ) {

		// Set the particle system to the initial condition
		compute_particle_start( i32T0 );
	}

	int32	i32ParticleCount = get_particle_count();

	while( m_i32ValidTime < i32T ) {

		eval_state( m_i32ValidTime );

		int32	i32Born = 0;		
		
		// Compute our step size
		if( m_i32ValidTime % m_i32StepSize != 0 ) {
			i32DT = m_i32StepSize - m_i32ValidTime % m_i32StepSize;
		}
		else {
			i32DT = m_i32StepSize;
		}

		if( (m_i32ValidTime + i32DT) > i32T ) {
			i32DT = i32T - m_i32ValidTime;
		}
/*		if( (m_i32ValidTime - i32T) > m_i32StepSize ) {
			i32DT = m_i32StepSize;
		}
		else {
			i32DT = i32T - m_i32ValidTime;
		}*/

		
		// Increment time
		m_i32ValidTime += i32DT;
		
		
		// Compute the number of particles that should be born
//		bool	bFullFrame = (m_i32ValidTime % i32OneFrame == 0 );
//		if( bFullFrame ) {

		bool	bFullFrame = false;

		if( m_i32ValidTime > (m_i32LastBirthTime + m_i32StepSize) ) {
			m_i32LastBirthTime += m_i32StepSize;
			bFullFrame = true;
			if( !get_constant_birth_rate() ) {
				f32BirthRate = get_birth_rate();
			}
			i32Birth = int32( (m_i32StepSize) * f32BirthRate * f32BirthRateFactor); //int32( (m_i32ValidTime - i32T0) * f32BirthRate * f32BirthRateFactor) - int32( (m_i32ValidTime - i32T0 - i32DT) * f32BirthRate * f32BirthRateFactor);
		}		
		
		// First increment age and kill off old particles
		for( j = 0; j < i32ParticleCount; j++ ) {
			if( m_rPartAges[j] < 0 )
				continue;
			m_rPartAges[j] += i32DT;
			if( m_rPartAges[j] >= i32Life )
				m_rPartAges[j] = -1;
		}
		
		
		// We want to seed the random number generator for all particles
		// born at this time. Construct the seed based on this time by
		// pulling out each of the 4 bytes, Perm() each one, and put
		// them back in the opposite byte order.
		int32 seed1 = (m_i32ValidTime * 1200) / TIME_TICKSPERSEC;
		int32 seed2 = seed1 >> 8;
		int32 seed3 = seed2 >> 8;
		int32 seed4 = seed2 >> 8;
		seed1 &= 0xFF;
		seed2 &= 0xFF;
		seed3 &= 0xFF;
		seed4 &= 0xFF;
		srand( (Perm( seed1 ) << 24) + (Perm( seed2 ) << 16) + (Perm( seed3 ) << 8) + Perm( seed4 ) + int( PARTICLE_SEED ) );
		
		// Next, birth particles at the birth rate
		for( j = 0; j < i32ParticleCount; j++ ) {
			if( i32Born >= i32Birth )
				break;
			if( m_rPartAges[j] < 0 ) {
				birth_particle( m_i32ValidTime, j );
				i32Born++;
			}
		}
		
		// update modifiers
		for( i = 0; i < m_rModifiers.size(); i++ ) {
			if( m_rModifiers[i].m_pObject )
				m_rModifiers[i].m_pObject->eval_state( m_i32ValidTime );
		}
		
		// Apply forces to modify velocity
		if( bFullFrame ) {
			for( i = 0; i < m_rForceFields.size(); i++ ) {		
				int32	i32Idx = 0;
				for( j = 0; j < i32ParticleCount; j++ ) {
					if( m_rPartAges[j] >= 0 ) {
						rForce = m_rForceFields[i]->force( m_i32ValidTime, &m_rPartPositions[i32Idx], &m_rPartVelocities[i32Idx], j );
						m_rPartVelocities[i32Idx + 0] += rForce[0] * float32( m_i32StepSize ); //i32DT );
						m_rPartVelocities[i32Idx + 1] += rForce[1] * float32( m_i32StepSize ); //i32DT );
						m_rPartVelocities[i32Idx + 2] += rForce[2] * float32( m_i32StepSize ); //i32DT );
					}
					i32Idx += 3;
				}
			}
		}


		// Increment the positions
		int32	i32Idx = 0;
		for( j = 0; j < i32ParticleCount; j++ ) {
			if( m_rPartAges[j] >= 0 ) {
			
				// Check for collisions
				bool bCollide = false;
				for( i = 0; i < m_rCollisionObjs.size(); i++ ) {
					if( m_rCollisionObjs[i]->check_collision(
							m_i32ValidTime, &m_rPartPositions[i32Idx], &m_rPartVelocities[i32Idx], float( i32DT ), j ) ) {
						bCollide = true;
						break;
					}
				}
				
				// If we didn't collide, then increment.
				if( !bCollide ) {
					m_rPartPositions[i32Idx + 0] += m_rPartVelocities[i32Idx + 0] * float32( i32DT );
					m_rPartPositions[i32Idx + 1] += m_rPartVelocities[i32Idx + 1] * float32( i32DT );
					m_rPartPositions[i32Idx + 2] += m_rPartVelocities[i32Idx + 2] * float32( i32DT );
				}
			}
			i32Idx += 3;
		}
	}
}


int32
ParticleSystemC::get_particle_life( int32 i32T, int32 i32Index )
{
	return m_i32LifeTime;
}

Vector3C
ParticleSystemC::get_particle_position( int32 i32T, int32 i32Index )
{
	return Vector3C( m_rPartPositions[i32Index * 3 + 0], m_rPartPositions[i32Index * 3 + 1], m_rPartPositions[i32Index * 3 + 2] );
}

Vector3C
ParticleSystemC::get_particle_velocity( int32 i32T, int32 i32Index )
{
	return Vector3C( m_rPartVelocities[i32Index * 3 + 0], m_rPartVelocities[i32Index * 3 + 1], m_rPartVelocities[i32Index * 3 + 2] );
}


void
ParticleSystemC::add_ws_modifier( const char* szName )
{
	WSModifierS	rMod;
	rMod.m_sName = szName;
	m_rModifiers.push_back( rMod );
}


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

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

void
ParticleSystemC::set_color( const ColorC &rColor )
{
	m_rColor = rColor;
}

void
ParticleSystemC::set_transparency( const float32 f32Trans )
{
	m_f32Transparency = f32Trans;
}

void
ParticleSystemC::set_selfillum( const float32 f32SelfIllum )
{
	m_f32SelfIllum = f32SelfIllum;
}

void
ParticleSystemC::set_transparency_type( uint32 ui32Type )
{
	m_ui32TransparencyType = ui32Type;
}

const ColorC&
ParticleSystemC::get_color() const
{
	return m_rColor;
}

float32
ParticleSystemC::get_transparency() const
{
	return m_f32Transparency;
}

float32
ParticleSystemC::get_selfillum() const
{
	return m_f32SelfIllum;
}

uint32
ParticleSystemC::get_transparency_type() const
{
	return m_ui32TransparencyType;
}


/*
	ContVector3C*		m_pPosCont;
	ContQuatC*			m_pRotCont;
	ContBoolC*			m_pVisCont;
	ContFloatC*			m_pDropSizeCont;
	ContFloatC*			m_pSpeedCont;
	ContFloatC*			m_pStartTimeCont;
	ContFloatC*			m_pLifeTimeCont;
	ContFloatC*			m_pTumbleScaleCont;
	ContFloatC*			m_pTumbleCont;
	ContFloatC*			m_pVariationCont;
	ContFloatC*			m_pBirthRateCont;
	ContFloatC*			m_pWidthCont;
	ContFloatC*			m_pHeightCont;
*/
enum ParticleChunksE {
	CHUNK_PARTICLE_STATIC			= 0x1000,
	CHUNK_PARTICLE_MATERIAL			= 0x1001,
	CHUNK_PARTICLE_BASE				= 0x1002,
	CHUNK_PARTICLE_POS_CONT			= 0x2000,
	CHUNK_PARTICLE_ROT_CONT			= 0x2001,
	CHUNK_PARTICLE_VIS_CONT			= 0x2002,
	CHUNK_PARTICLE_DROPSIZE_CONT	= 0x2003,
	CHUNK_PARTICLE_SPEED_CONT		= 0x2004,
	CHUNK_PARTICLE_START_CONT		= 0x2005,
	CHUNK_PARTICLE_LIFE_CONT		= 0x2006,
	CHUNK_PARTICLE_TUMBLE_CONT		= 0x2007,
	CHUNK_PARTICLE_TUMBLESCALE_CONT	= 0x2008,
	CHUNK_PARTICLE_VARIATION_CONT	= 0x2009,
	CHUNK_PARTICLE_BIRTHRATE_CONT	= 0x200a,
	CHUNK_PARTICLE_WIDTH_CONT		= 0x200b,
	CHUNK_PARTICLE_HEIGHT_CONT		= 0x200c,
	CHUNK_PARTICLE_MODIFIER			= 0x3000,
};

const uint32	PARTICLE_VERSION = 1;


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

	std::string	sStr;
	float32		f32Temp3[3];
	float32		f32Temp4[4];
	uint8		ui8Tmp;
	uint32		ui32Temp;
	uint8		ui8Color[4];

	eval_state( 0 );

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

	// base
	pSave->begin_chunk( CHUNK_PARTICLE_STATIC, PARTICLE_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 ) );

		ui32Error = pSave->write( &m_ui32ParticleType, sizeof( m_ui32ParticleType ) );
		ui32Error = pSave->write( &m_i32StepSize, sizeof( m_i32StepSize ) );
		ui32Error = pSave->write( &m_i32MaxParticleCount, sizeof( m_i32MaxParticleCount ) );
		ui32Error = pSave->write( &m_f32DropSize, sizeof( m_f32DropSize ) );
		ui32Error = pSave->write( &m_f32Speed, sizeof( m_f32Speed ) );
		ui32Error = pSave->write( &m_i32StartTime, sizeof( m_i32StartTime ) );
		ui32Error = pSave->write( &m_i32LifeTime, sizeof( m_i32LifeTime ) );
		ui32Error = pSave->write( &m_f32TumbleScale, sizeof( m_f32TumbleScale ) );
		ui32Error = pSave->write( &m_f32Tumble, sizeof( m_f32Tumble ) );
		ui32Error = pSave->write( &m_f32Variation, sizeof( m_f32Variation ) );
		ui32Error = pSave->write( &m_f32BirthRate, sizeof( m_f32BirthRate ) );
		ui32Error = pSave->write( &m_f32Width, sizeof( m_f32Width ) );
		ui32Error = pSave->write( &m_f32Height, sizeof( m_f32Height ) );
		ui8Tmp = m_bConstantBirth ? 0xff : 0;
		ui32Error = pSave->write( &ui8Tmp, sizeof( ui8Tmp ) );

	pSave->end_chunk();

	// material
	pSave->begin_chunk( CHUNK_PARTICLE_MATERIAL, PARTICLE_VERSION );
		//color
		ui8Color[0] = (uint8)(m_rColor[0] * 255.0f);
		ui8Color[1] = (uint8)(m_rColor[1] * 255.0f);
		ui8Color[2] = (uint8)(m_rColor[2] * 255.0f);
		ui8Color[3] = (uint8)(m_rColor[3] * 255.0f);
		ui32Error = pSave->write( ui8Color, 4 );
		// transparency
		ui32Error = pSave->write( &m_f32Transparency, sizeof( m_f32Transparency ) );
		// transparencytype
		ui32Error = pSave->write( &m_ui32TransparencyType, sizeof( m_ui32TransparencyType ) );
		// selfillum
		ui32Error = pSave->write( &m_f32SelfIllum, sizeof( m_f32SelfIllum ) );

		// Tex handle
		if( m_pTextureHandle ) {
			ui32Temp = m_pTextureHandle->get_id();
		}
		else {
			ui32Temp = 0xffffffff;
		}
//		TRACE( "tex ID %d\n", ui32Temp );
		ui32Error = pSave->write( &ui32Temp, sizeof( ui32Temp ) );
	pSave->end_chunk();


	if( m_pPosCont ) {
		pSave->begin_chunk( CHUNK_PARTICLE_POS_CONT, PARTICLE_VERSION );
			ui32Error = m_pPosCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pRotCont ) {
		pSave->begin_chunk( CHUNK_PARTICLE_ROT_CONT, PARTICLE_VERSION );
			ui32Error = m_pRotCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pBirthRateCont ) {
		pSave->begin_chunk( CHUNK_PARTICLE_BIRTHRATE_CONT, PARTICLE_VERSION );
			ui32Error = m_pBirthRateCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pDropSizeCont ) {
		pSave->begin_chunk( CHUNK_PARTICLE_DROPSIZE_CONT, PARTICLE_VERSION );
			ui32Error = m_pDropSizeCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pHeightCont ) {
		pSave->begin_chunk( CHUNK_PARTICLE_HEIGHT_CONT, PARTICLE_VERSION );
			ui32Error = m_pHeightCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pLifeTimeCont ) {
		pSave->begin_chunk( CHUNK_PARTICLE_LIFE_CONT, PARTICLE_VERSION );
			ui32Error = m_pLifeTimeCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pSpeedCont ) {
		pSave->begin_chunk( CHUNK_PARTICLE_SPEED_CONT, PARTICLE_VERSION );
			ui32Error = m_pSpeedCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pStartTimeCont ) {
		pSave->begin_chunk( CHUNK_PARTICLE_START_CONT, PARTICLE_VERSION );
			ui32Error = m_pStartTimeCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pTumbleCont ) {
		pSave->begin_chunk( CHUNK_PARTICLE_TUMBLE_CONT, PARTICLE_VERSION );
			ui32Error = m_pTumbleCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pTumbleScaleCont ) {
		pSave->begin_chunk( CHUNK_PARTICLE_TUMBLESCALE_CONT, PARTICLE_VERSION );
			ui32Error = m_pTumbleScaleCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pVariationCont ) {
		pSave->begin_chunk( CHUNK_PARTICLE_VARIATION_CONT, PARTICLE_VERSION );
			ui32Error = m_pVariationCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pVisCont ) {
		pSave->begin_chunk( CHUNK_PARTICLE_VIS_CONT, PARTICLE_VERSION );
			ui32Error = m_pVisCont->save( pSave );
		pSave->end_chunk();
	}

	if( m_pWidthCont ) {
		pSave->begin_chunk( CHUNK_PARTICLE_WIDTH_CONT, PARTICLE_VERSION );
			ui32Error = m_pWidthCont->save( pSave );
		pSave->end_chunk();
	}

	for( uint32 i = 0; i < m_rModifiers.size(); i++ ) {
		pSave->begin_chunk( CHUNK_PARTICLE_MODIFIER, PARTICLE_VERSION );
			sStr = m_rModifiers[i].m_sName;
			if( sStr.size() > 255 )
				sStr.resize( 255 );
			ui32Error = pSave->write_str( sStr.c_str() );
		pSave->end_chunk();
	}

	return ui32Error;
}

uint32
ParticleSystemC::load( LoadC* pLoad )
{
	uint32	ui32Error = IO_OK;
	float32	f32Temp3[3];
	float32	f32Temp4[4];
	uint8	ui8Tmp;
	uint32	ui32Temp;
	uint8	ui8Color[4];
	char	szStr[256];

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

		switch( pLoad->get_chunk_id() ) {

		case CHUNK_PARTICLE_BASE:
			if( pLoad->get_chunk_version() == PARTICLE_VERSION )
				ScenegraphItemI::load( pLoad );

			break;

		case CHUNK_PARTICLE_STATIC:
			if( pLoad->get_chunk_version() == PARTICLE_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];

				ui32Error = pLoad->read( &m_ui32ParticleType, sizeof( m_ui32ParticleType ) );
				ui32Error = pLoad->read( &m_i32StepSize, sizeof( m_i32StepSize ) );
				ui32Error = pLoad->read( &m_i32MaxParticleCount, sizeof( m_i32MaxParticleCount ) );
				ui32Error = pLoad->read( &m_f32DropSize, sizeof( m_f32DropSize ) );
				ui32Error = pLoad->read( &m_f32Speed, sizeof( m_f32Speed ) );
				ui32Error = pLoad->read( &m_i32StartTime, sizeof( m_i32StartTime ) );
				ui32Error = pLoad->read( &m_i32LifeTime, sizeof( m_i32LifeTime ) );
				ui32Error = pLoad->read( &m_f32TumbleScale, sizeof( m_f32TumbleScale ) );
				ui32Error = pLoad->read( &m_f32Tumble, sizeof( m_f32Tumble ) );
				ui32Error = pLoad->read( &m_f32Variation, sizeof( m_f32Variation ) );
				ui32Error = pLoad->read( &m_f32BirthRate, sizeof( m_f32BirthRate ) );
				ui32Error = pLoad->read( &m_f32Width, sizeof( m_f32Width ) );
				ui32Error = pLoad->read( &m_f32Height, sizeof( m_f32Height ) );
				ui32Error = pLoad->read( &ui8Tmp, sizeof( ui8Tmp ) );
				m_bConstantBirth = ui8Tmp == 0 ? false : true;

			}
			break;

		case CHUNK_PARTICLE_MATERIAL:
			if( pLoad->get_chunk_version() == PARTICLE_VERSION ) {
				//color
				ui32Error = pLoad->read( ui8Color, 4 );
				m_rColor.convert_from_uint8( ui8Color[0], ui8Color[1], ui8Color[2], ui8Color[3] );

				// transparency
				ui32Error = pLoad->read( &m_f32Transparency, sizeof( m_f32Transparency ) );
				// transparencytype
				ui32Error = pLoad->read( &m_ui32TransparencyType, sizeof( m_ui32TransparencyType ) );
				// selfillum
				ui32Error = pLoad->read( &m_f32SelfIllum, sizeof( m_f32SelfIllum ) );

				// Tex handle
				ui32Error = pLoad->read( &ui32Temp, sizeof( ui32Temp ) );
//				TRACE( "load tex ID %d\n", ui32Temp );
				if( ui32Temp != 0xffffffff )
					pLoad->add_file_handle_patch( (void**)&m_pTextureHandle, ui32Temp );
			}
			break;

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

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

		case CHUNK_PARTICLE_VIS_CONT:
			if( pLoad->get_chunk_version() == PARTICLE_VERSION ) {
				ContBoolC*	pCont = new ContBoolC;
				ui32Error = pCont->load( pLoad );
				m_pVisCont = pCont;
			}
			break;

		case CHUNK_PARTICLE_DROPSIZE_CONT:
			if( pLoad->get_chunk_version() == PARTICLE_VERSION ) {
				ContFloatC*	pCont = new ContFloatC;
				ui32Error = pCont->load( pLoad );
				m_pDropSizeCont = pCont;
			}
			break;

		case CHUNK_PARTICLE_SPEED_CONT:
			if( pLoad->get_chunk_version() == PARTICLE_VERSION ) {
				ContFloatC*	pCont = new ContFloatC;
				ui32Error = pCont->load( pLoad );
				m_pSpeedCont = pCont;
			}
			break;

		case CHUNK_PARTICLE_START_CONT:
			if( pLoad->get_chunk_version() == PARTICLE_VERSION ) {
				ContFloatC*	pCont = new ContFloatC;
				ui32Error = pCont->load( pLoad );
				m_pStartTimeCont = pCont;
			}
			break;

		case CHUNK_PARTICLE_LIFE_CONT:
			if( pLoad->get_chunk_version() == PARTICLE_VERSION ) {
				ContFloatC*	pCont = new ContFloatC;
				ui32Error = pCont->load( pLoad );
				m_pLifeTimeCont = pCont;
			}
			break;

		case CHUNK_PARTICLE_TUMBLE_CONT:
			if( pLoad->get_chunk_version() == PARTICLE_VERSION ) {
				ContFloatC*	pCont = new ContFloatC;
				ui32Error = pCont->load( pLoad );
				m_pTumbleCont = pCont;
			}
			break;

		case CHUNK_PARTICLE_TUMBLESCALE_CONT:
			if( pLoad->get_chunk_version() == PARTICLE_VERSION ) {
				ContFloatC*	pCont = new ContFloatC;
				ui32Error = pCont->load( pLoad );
				m_pTumbleScaleCont = pCont;
			}
			break;

		case CHUNK_PARTICLE_VARIATION_CONT:
			if( pLoad->get_chunk_version() == PARTICLE_VERSION ) {
				ContFloatC*	pCont = new ContFloatC;
				ui32Error = pCont->load( pLoad );
				m_pVariationCont = pCont;
			}
			break;

		case CHUNK_PARTICLE_BIRTHRATE_CONT:
			if( pLoad->get_chunk_version() == PARTICLE_VERSION ) {
				ContFloatC*	pCont = new ContFloatC;
				ui32Error = pCont->load( pLoad );
				m_pBirthRateCont = pCont;
			}
			break;

		case CHUNK_PARTICLE_WIDTH_CONT:
			if( pLoad->get_chunk_version() == PARTICLE_VERSION ) {
				ContFloatC*	pCont = new ContFloatC;
				ui32Error = pCont->load( pLoad );
				m_pWidthCont = pCont;
			}
			break;

		case CHUNK_PARTICLE_HEIGHT_CONT:
			if( pLoad->get_chunk_version() == PARTICLE_VERSION ) {
				ContFloatC*	pCont = new ContFloatC;
				ui32Error = pCont->load( pLoad );
				m_pHeightCont = pCont;
			}
			break;

		case CHUNK_PARTICLE_MODIFIER:
			if( pLoad->get_chunk_version() == PARTICLE_VERSION ) {
				ui32Error = pLoad->read_str( szStr );
				add_ws_modifier( szStr );
			}
			break;

		default:
			TRACE( "unknown chunk: 0x%x\n", pLoad->get_chunk_id() );
//			assert( 0 );
		}

		pLoad->close_chunk();

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

	return ui32Error;
}
