//////////////////////////////////////////////////////////////////////////////
//
// Asura_PFX_Templates_DataBlock.cpp
//
// Copyright (c) 2005 Rebellion. All rights reserved.
//
// Description:
//
//     functions to fill in / fill out data blocks and templates
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#include "Core/Asura_Hash.h"
#include "SpecialFX/PFX/Asura_PFX_Templates.h"

#include <string.h>	// memcpy

#define DECLARE_GRAPH_PARAMS( graph_name ) \
    u_int m_uNum##graph_name##Samples; \
    float m_f##graph_name##DomainMin; \
    float m_f##graph_name##DomainMax

#define COPY_GRAPH_PARAMS_TO_DATABLOCK( data_hdr_ptr, graph_name ) \
    (data_hdr_ptr)->m_uNum##graph_name##Samples = m_x##graph_name .GetNumDiscreteSamples(); \
    (data_hdr_ptr)->m_f##graph_name##DomainMin  = m_x##graph_name .GetDomainMin(); \
    (data_hdr_ptr)->m_f##graph_name##DomainMax  = m_x##graph_name .GetDomainMax()

#define SET_GRAPH_FROM_DATABLOCK( graph_name, graph_data_ptr, data_hdr_ptr ) \
    (graph_data_ptr) = m_x##graph_name .SetFromDataBlock( (graph_data_ptr), \
                                                          (data_hdr_ptr)->m_uNum##graph_name##Samples, \
                                                          (data_hdr_ptr)->m_f##graph_name##DomainMin, \
                                                          (data_hdr_ptr)->m_f##graph_name##DomainMax )


#ifdef ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK
class Asura_PFX_Source_Template_DataBlockHeader
{

public:

	DECLARE_GRAPH_PARAMS( Speed );
	DECLARE_GRAPH_PARAMS( Angle );
	DECLARE_GRAPH_PARAMS( SpeedSpread );
	DECLARE_GRAPH_PARAMS( AngleSpread );
	DECLARE_GRAPH_PARAMS( ReleaseRate );
	DECLARE_GRAPH_PARAMS( ReleaseRand );
	DECLARE_GRAPH_PARAMS( GravityStrength );
	DECLARE_GRAPH_PARAMS( TurbulenceStrength );

	Asura_Vector_3					m_xGravityVector;
	Asura_Hash_ID					m_uParticleTemplateHash;
	float							m_fDuration;
	float							m_fAirResistance;
	float							m_fParticleBounce;
	float							m_fSourceBounce;
	float							m_fParticleFriction;
	float							m_fTimeScale;
	float							m_fVelocityInheritance;
	float							m_fTurbulenceFrequency;
	float							m_fRangeMin;
	float							m_fRangeMax;
	float							m_fRangeDelayTime;
	u_int							m_uMaxParticles;
	int								m_iSpawnSourceIndex;
	u_int							m_uFlags;

};


class Asura_PFX_Particle_Template_DataBlockHeader
{

public:

	DECLARE_GRAPH_PARAMS( Colour );
	DECLARE_GRAPH_PARAMS( Size );
	DECLARE_GRAPH_PARAMS( Rotation );
	DECLARE_GRAPH_PARAMS( SizeVariation );

	ASURA_TRANSLUCENCY_MODE m_eTranslucency;
	float					m_fMinLifetime;
	float					m_fMaxLifetimeDiff;
	u_int					m_uNumTextureFrames;
	float					m_fBlurLength;
	float					m_fFixedRotation;
	float					m_fLightingScale;
	u_int					m_uNumFixedRotations;
	float					m_fNearFadeDistance;
	float					m_fNearZeroDistance;
	float					m_fFarFadeDistance;
	float					m_fFarZeroDistance;
	float					m_fFPS;
	unsigned int			m_uFlags;

};

#ifndef ASURA_GRAPH_LEAN_AND_MEAN
void Asura_PFX_Source_Template::CreateDataBlock()
{
	Asura_Assert(!m_pDataBlock, "data block already allocated");
	if (m_pDataBlock)
	{
		return;
	}

	u_int uDataBlockSize = sizeof(Asura_PFX_Source_Template_DataBlockHeader);
	uDataBlockSize += m_xSpeed.GetDataBlockSize();
	uDataBlockSize += m_xAngle.GetDataBlockSize();
	uDataBlockSize += m_xSpeedSpread.GetDataBlockSize();
	uDataBlockSize += m_xAngleSpread.GetDataBlockSize();
	uDataBlockSize += m_xReleaseRate.GetDataBlockSize();
	uDataBlockSize += m_xReleaseRand.GetDataBlockSize();
	uDataBlockSize += m_xGravityStrength.GetDataBlockSize();
	uDataBlockSize += m_xTurbulenceStrength.GetDataBlockSize();

	m_pDataBlock = Asura_Allocate(uDataBlockSize);
	m_uDataBlockSize = uDataBlockSize;

	// fill out the data header
	Asura_PFX_Source_Template_DataBlockHeader* pxDataHeader = static_cast<Asura_PFX_Source_Template_DataBlockHeader*>(m_pDataBlock);

    COPY_GRAPH_PARAMS_TO_DATABLOCK( pxDataHeader, Speed );
    COPY_GRAPH_PARAMS_TO_DATABLOCK( pxDataHeader, Angle );
    COPY_GRAPH_PARAMS_TO_DATABLOCK( pxDataHeader, SpeedSpread );
    COPY_GRAPH_PARAMS_TO_DATABLOCK( pxDataHeader, AngleSpread );
    COPY_GRAPH_PARAMS_TO_DATABLOCK( pxDataHeader, ReleaseRate );
    COPY_GRAPH_PARAMS_TO_DATABLOCK( pxDataHeader, ReleaseRand );
    COPY_GRAPH_PARAMS_TO_DATABLOCK( pxDataHeader, GravityStrength );
    COPY_GRAPH_PARAMS_TO_DATABLOCK( pxDataHeader, TurbulenceStrength );

	pxDataHeader->m_xGravityVector = m_xGravityVector;
	pxDataHeader->m_uParticleTemplateHash = m_uParticleTemplateHash;
	pxDataHeader->m_fDuration = m_fDuration;
	pxDataHeader->m_fAirResistance = m_fAirResistance;
	pxDataHeader->m_fParticleBounce = m_fParticleBounce;
	pxDataHeader->m_fSourceBounce = m_fSourceBounce;
	pxDataHeader->m_fParticleFriction = m_fParticleFriction;
	pxDataHeader->m_fTimeScale = m_fTimeScale;
	pxDataHeader->m_fVelocityInheritance = m_fVelocityInheritance;
	pxDataHeader->m_fTurbulenceFrequency = m_fTurbulenceFrequency;
	pxDataHeader->m_fRangeMin = m_fRangeMin;
	pxDataHeader->m_fRangeMax = m_fRangeMax;
	pxDataHeader->m_fRangeDelayTime = m_fRangeDelayTime;
	pxDataHeader->m_uMaxParticles = m_uMaxParticles;
	pxDataHeader->m_iSpawnSourceIndex = m_iSpawnSourceIndex;
	pxDataHeader->m_uFlags = m_uFlags;

	// copy each block of graph data to after the header
	void* pvData = pxDataHeader + 1;
	pvData = m_xSpeed.WriteDataBlock( pvData );
	pvData = m_xAngle.WriteDataBlock( pvData );
	pvData = m_xSpeedSpread.WriteDataBlock( pvData );
	pvData = m_xAngleSpread.WriteDataBlock( pvData );
	pvData = m_xReleaseRate.WriteDataBlock( pvData );
	pvData = m_xReleaseRand.WriteDataBlock( pvData );
	pvData = m_xGravityStrength.WriteDataBlock( pvData );
	pvData = m_xTurbulenceStrength.WriteDataBlock( pvData );

	Asura_Assert( static_cast<char*>(m_pDataBlock) + uDataBlockSize == pvData, "wrong amount of data" );
}
#endif // #ifndef ASURA_GRAPH_LEAN_AND_MEAN

#ifdef ASURA_GRAPH_LEAN_AND_MEAN
void Asura_PFX_Source_Template::FillInFromDataBlock(const void* const pInputData, u_int uSize)
{
	// fill from the data header
	const Asura_PFX_Source_Template_DataBlockHeader* pxDataHeader = static_cast
		<const Asura_PFX_Source_Template_DataBlockHeader*>(pInputData);

	m_xGravityVector		= pxDataHeader->m_xGravityVector;
	m_uParticleTemplateHash	= pxDataHeader->m_uParticleTemplateHash;
	m_fDuration				= pxDataHeader->m_fDuration;
	m_fAirResistance		= pxDataHeader->m_fAirResistance;
	m_fParticleBounce		= pxDataHeader->m_fParticleBounce;
	m_fSourceBounce			= pxDataHeader->m_fSourceBounce;
	m_fParticleFriction		= pxDataHeader->m_fParticleFriction;
	m_fTimeScale			= pxDataHeader->m_fTimeScale;
	m_fVelocityInheritance	= pxDataHeader->m_fVelocityInheritance;
	m_fTurbulenceFrequency	= pxDataHeader->m_fTurbulenceFrequency;
	m_fRangeMin				= pxDataHeader->m_fRangeMin;
	m_fRangeMax				= pxDataHeader->m_fRangeMax;
	m_fRangeDelayTime		= pxDataHeader->m_fRangeDelayTime;
	m_uMaxParticles			= pxDataHeader->m_uMaxParticles;
	m_iSpawnSourceIndex		= pxDataHeader->m_iSpawnSourceIndex;
	m_uFlags				= pxDataHeader->m_uFlags;

	// copy each block of graph data from after the header
	const void* pvData = pxDataHeader + 1;
    SET_GRAPH_FROM_DATABLOCK( Speed,              pvData, pxDataHeader );
    SET_GRAPH_FROM_DATABLOCK( Angle,              pvData, pxDataHeader );
    SET_GRAPH_FROM_DATABLOCK( SpeedSpread,        pvData, pxDataHeader );
    SET_GRAPH_FROM_DATABLOCK( AngleSpread,        pvData, pxDataHeader );
    SET_GRAPH_FROM_DATABLOCK( ReleaseRate,        pvData, pxDataHeader );
    SET_GRAPH_FROM_DATABLOCK( ReleaseRand,        pvData, pxDataHeader );
    SET_GRAPH_FROM_DATABLOCK( GravityStrength,    pvData, pxDataHeader );
    SET_GRAPH_FROM_DATABLOCK( TurbulenceStrength, pvData, pxDataHeader );

	Asura_Assertf( static_cast<const char*>(pInputData) + uSize == pvData, 
		("wrong amount of data 0x%x != 0x%x", static_cast<const char*>(pInputData) + uSize, pvData ));
}
#endif // #ifdef ASURA_GRAPH_LEAN_AND_MEAN

#ifndef ASURA_GRAPH_LEAN_AND_MEAN
void Asura_PFX_Particle_Template::CreateDataBlock()
{
	Asura_Assert(!m_pDataBlock, "data block already allocated");
	if (m_pDataBlock)
	{
		return;
	}

	// bytes required = header size + graph data + texture frames
	u_int uDataBlockSize = sizeof(Asura_PFX_Particle_Template_DataBlockHeader);
	uDataBlockSize += m_xColour.GetDataBlockSize();
	uDataBlockSize += m_xSize.GetDataBlockSize();
	uDataBlockSize += m_xRotation.GetDataBlockSize();
	uDataBlockSize += m_xSizeVariation.GetDataBlockSize();
	u_int uTextureFramesDataSize = sizeof(Asura_PFX_TextureFrame) * m_uNumTextureFrames;
	uDataBlockSize += uTextureFramesDataSize;

	m_pDataBlock = Asura_Allocate(uDataBlockSize);
	m_uDataBlockSize = uDataBlockSize;

	// fill out the data header
	Asura_PFX_Particle_Template_DataBlockHeader* pxDataHeader = static_cast<Asura_PFX_Particle_Template_DataBlockHeader*>(m_pDataBlock);

	COPY_GRAPH_PARAMS_TO_DATABLOCK( pxDataHeader, Colour );
	COPY_GRAPH_PARAMS_TO_DATABLOCK( pxDataHeader, Size );
	COPY_GRAPH_PARAMS_TO_DATABLOCK( pxDataHeader, Rotation );
	COPY_GRAPH_PARAMS_TO_DATABLOCK( pxDataHeader, SizeVariation );

	pxDataHeader->m_eTranslucency		= m_eTranslucency;
	pxDataHeader->m_fMinLifetime		= m_fMinLifetime;
	pxDataHeader->m_fMaxLifetimeDiff	= m_fMaxLifetimeDiff;
	pxDataHeader->m_uNumTextureFrames	= m_uNumTextureFrames;
	pxDataHeader->m_fBlurLength			= m_fBlurLength;
	pxDataHeader->m_fFixedRotation 		= m_fFixedRotation;
	pxDataHeader->m_fLightingScale 		= m_fLightingScale;
	pxDataHeader->m_uNumFixedRotations	= m_uNumFixedRotations;
	pxDataHeader->m_fNearFadeDistance	= m_fNearFadeDistance;
	pxDataHeader->m_fNearZeroDistance	= m_fNearZeroDistance;
	pxDataHeader->m_fFarFadeDistance	= m_fFarFadeDistance;
	pxDataHeader->m_fFarZeroDistance	= m_fFarZeroDistance;
	pxDataHeader->m_fFPS				= m_fFPS;
	pxDataHeader->m_uFlags				= m_uFlags;

	// copy each block of graph data to after the header
	void* pvData = pxDataHeader + 1;
	pvData = m_xColour.WriteDataBlock( pvData );
	pvData = m_xSize.WriteDataBlock( pvData );
	pvData = m_xRotation.WriteDataBlock( pvData );
	pvData = m_xSizeVariation.WriteDataBlock( pvData );

	// copy the texture frame data to after the graph data
	memcpy( pvData, m_pxTextureFrames, uTextureFramesDataSize );
	pvData = static_cast<char*>(pvData) + uTextureFramesDataSize;

	Asura_Assert( static_cast<char*>(m_pDataBlock) + uDataBlockSize == pvData, "wrong amount of data" );
}
#endif // #ifndef ASURA_GRAPH_LEAN_AND_MEAN

#ifdef ASURA_GRAPH_LEAN_AND_MEAN
void Asura_PFX_Particle_Template::FillInFromDataBlock(const void* const pInputData, u_int uSize)
{
	// fill from the data header
	const Asura_PFX_Particle_Template_DataBlockHeader* pxDataHeader = static_cast
		<const Asura_PFX_Particle_Template_DataBlockHeader*>(pInputData);

	m_eTranslucency			= pxDataHeader->m_eTranslucency;
	m_fMinLifetime			= pxDataHeader->m_fMinLifetime;
	m_fMaxLifetimeDiff		= pxDataHeader->m_fMaxLifetimeDiff;
	m_uNumTextureFrames		= pxDataHeader->m_uNumTextureFrames;
	m_fBlurLength			= pxDataHeader->m_fBlurLength;
	m_fFixedRotation		= pxDataHeader->m_fFixedRotation;
	m_fLightingScale		= pxDataHeader->m_fLightingScale;
	m_uNumFixedRotations	= pxDataHeader->m_uNumFixedRotations;
	m_fNearFadeDistance		= pxDataHeader->m_fNearFadeDistance;
	m_fNearZeroDistance		= pxDataHeader->m_fNearZeroDistance;
	m_fFarFadeDistance		= pxDataHeader->m_fFarFadeDistance;
	m_fFarZeroDistance		= pxDataHeader->m_fFarZeroDistance;
	m_fFPS					= pxDataHeader->m_fFPS;
	m_uFlags				= pxDataHeader->m_uFlags;

	// copy each block of graph data from after the header
	const void* pvData = pxDataHeader + 1;
    SET_GRAPH_FROM_DATABLOCK( Colour,        pvData, pxDataHeader );
    SET_GRAPH_FROM_DATABLOCK( Size,          pvData, pxDataHeader );
    SET_GRAPH_FROM_DATABLOCK( Rotation,      pvData, pxDataHeader );
    SET_GRAPH_FROM_DATABLOCK( SizeVariation, pvData, pxDataHeader );

	// copy the texture frame data from after the graph data
	Asura_ResourceAllocateArray( m_pxTextureFrames, m_uNumTextureFrames );
	u_int uTextureFramesDataSize = sizeof(Asura_PFX_TextureFrame) * m_uNumTextureFrames;
	memcpy( m_pxTextureFrames, pvData, uTextureFramesDataSize );
	pvData = static_cast<const char*>(pvData) + uTextureFramesDataSize;

	Asura_Assertf( static_cast<const char*>(pInputData) + uSize == pvData, 
		("wrong amount of data 0x%x != 0x%x", static_cast<const char*>(pInputData) + uSize, pvData ));
}

#endif // #ifdef ASURA_GRAPH_LEAN_AND_MEAN

#endif // #ifdef ASURA_PFX_TEMPLATES_SUPPORT_DATABLOCK
