#include "SoulParticleSystem.h"
#include <math.h>

#ifdef _DEBUG
	#include "Mix/Graphics/Utility/IPerspectiveRenderer.h"
#endif //_DEBUG

////////////////////////////////////////////////////////////////////////////////////////////////////
// Constant
////////////////////////////////////////////////////////////////////////////////////////////////////

const Mix::Vector3 SoulParticleSystem::GENE_VEC_ONE = Mix::Vector3( 1.0f, 1.0f, 1.0f );

const Float32 SoulParticleSystem::GENE_MIN_LIFE = 0.8f;
const Float32 SoulParticleSystem::GENE_MAX_LIFE = 1.2f;
const Float32 SoulParticleSystem::GENE_DIFF_LIFE = SoulParticleSystem::GENE_MAX_LIFE - SoulParticleSystem::GENE_MIN_LIFE;

const Float32 SoulParticleSystem::GENE_MIN_LOCAL_RANGE = 1.0f;
const Float32 SoulParticleSystem::GENE_MAX_LOCAL_RANGE = 2.0f;
const Float32 SoulParticleSystem::GENE_DIFF_LOCAL_RANGE = SoulParticleSystem::GENE_MAX_LOCAL_RANGE - SoulParticleSystem::GENE_MIN_LOCAL_RANGE;

const Float32 SoulParticleSystem::GENE_MIN_HALF_SIZE = 0.01f;
const Float32 SoulParticleSystem::GENE_MAX_HALF_SIZE = 0.05f;
const Float32 SoulParticleSystem::GENE_DIFF_HALF_SIZE = SoulParticleSystem::GENE_MAX_HALF_SIZE - SoulParticleSystem::GENE_MIN_HALF_SIZE;

const Mix::Vector4 SoulParticleSystem::PROC_BASE_COLOR = Mix::Vector4( 0.0f, 1.0f, 0.0f, 1.0f );

const Float32 SoulParticleSystem::PROC_LAG_INTERVAL = 0.016f * 2.0f;
const Float32 SoulParticleSystem::PROC_LAG_HS_COIFFE = 0.9f;
const Float32 SoulParticleSystem::PROC_LAG_ALPHA_COIFFE = 0.6f;

#ifdef _DEBUG
	const Float32 SoulParticleSystem::DEBUG_TRACK_INTERVAL = 0.15f;
#endif //_DEBUG

////////////////////////////////////////////////////////////////////////////////////////////////////
// Generator
////////////////////////////////////////////////////////////////////////////////////////////////////

SoulParticleSystem::InternalGenerator* SoulParticleSystem::InternalGenerator::CreateInstance( void )
{
	return MIX_NEW_T( SoulParticleSystem::InternalGenerator );
}

SoulParticleSystem::InternalGenerator::InternalGenerator( void )
{
}

SoulParticleSystem::InternalGenerator::~InternalGenerator( void )
{
}

void SoulParticleSystem::InternalGenerator::Internal_GetData( UInt32 dataSize, void* pData )
{
	MIX_ASSERT( sizeof( SoulParticleSystem::DATA ) == dataSize );

	SoulParticleSystem::DATA* pDst = reinterpret_cast<SoulParticleSystem::DATA*>( pData );

	Float32 halfRange;
	Mix::Vector3 dir;
	Mix::Vector3 middlePos;
	Mix::Vector3 lastPos;

	halfRange = ( SoulParticleSystem::GENE_MIN_LOCAL_RANGE + SoulParticleSystem::GENE_DIFF_LOCAL_RANGE * Mix::RandF() ) * 0.5f;

	dir = Mix::Vector3::Rand() * 2.0f - SoulParticleSystem::GENE_VEC_ONE;
	middlePos = dir * halfRange;

	dir = Mix::Vector3::Rand() * 2.0f - SoulParticleSystem::GENE_VEC_ONE;
	lastPos = middlePos + dir * halfRange;

	pDst->life = SoulParticleSystem::GENE_MIN_LIFE + Mix::RandF() * SoulParticleSystem::GENE_DIFF_LIFE;
	pDst->invLifeMax = MIX_FLOAT_RECIPROCAL( pDst->life );
	pDst->ctrlPos[0] = Mix::Vector3::Zero();
	pDst->ctrlPos[1] = Mix::Vector3::Lerp( pDst->ctrlPos[0], middlePos, 0.5f );
	pDst->ctrlPos[2] = Mix::Vector3::Lerp( middlePos, lastPos, 0.5f );
	pDst->ctrlPos[3] = lastPos;
	pDst->halfSize = SoulParticleSystem::GENE_MIN_HALF_SIZE + Mix::RandF() * SoulParticleSystem::GENE_DIFF_HALF_SIZE;
	pDst->lagTime = 0.0f;
	pDst->lagHead = 0;
	pDst->lagTail = 0;
	pDst->lagNum = 0;
}

#ifdef _DEBUG

void SoulParticleSystem::InternalGenerator::Debug_Draw( const Mix::Matrix4x4& worldMat, Mix::Graphics::Utility::IPerspectiveRenderer* pPersRenderer )
{
}

#endif //_DEBUG

////////////////////////////////////////////////////////////////////////////////////////////////////
// Processor
////////////////////////////////////////////////////////////////////////////////////////////////////

const wchar_t* SoulParticleSystem::InternalProcessor::MSG_NOT_IMPL = L"実装されていません";

SoulParticleSystem::InternalProcessor* SoulParticleSystem::InternalProcessor::CreateInstance( void )
{
	return MIX_NEW_T( SoulParticleSystem::InternalProcessor );
}

SoulParticleSystem::InternalProcessor::InternalProcessor( void )
{
}

SoulParticleSystem::InternalProcessor::~InternalProcessor( void )
{
}

UInt32 SoulParticleSystem::InternalProcessor::Internal_GetCount( void ) const
{
	return MIX_UIT_TO_UI32( m_Dats.size() );
}

void SoulParticleSystem::InternalProcessor::Internal_Clear( void )
{
	m_Dats.clear();
	m_Faces.clear();
}

UInt32 SoulParticleSystem::InternalProcessor::Internal_Add( Mix::Scene::IParticleGenerator* pGenerator, UInt32 genCount )
{
	MIX_ERROR_W( InternalProcessor::MSG_NOT_IMPL );
	return 0;
}

UInt32 SoulParticleSystem::InternalProcessor::Internal_Add( const Mix::Matrix4x4& worldMat, Mix::Scene::IParticleGenerator* pGenerator, UInt32 genCount )
{
	SoulParticleSystem::DATA data;

	for( UInt32 i = 0; i < genCount; i++ )
	{
		pGenerator->Internal_GetData( sizeof( SoulParticleSystem::DATA ), &data );

		worldMat.Transform( data.ctrlPos, SoulParticleSystem::MAX_CTRL_POS );

		m_Dats.push_back( data );
	}

	return genCount;
}

Boolean SoulParticleSystem::InternalProcessor::Internal_Update( Float32 dt )
{
	if( m_Dats.size() == 0 )
	{
		return False;
	}

	std::vector<SoulParticleSystem::DATA>::iterator it_dat = m_Dats.begin();

	SoulParticleSystem::DATA* pData;
	const Mix::Vector3* ctrlPos;
	Mix::Vector3 pos;
	Float32 ratio;
	Float32 alpha;
	Float32 hs;
	UInt32 lagNum;
	UInt32 lagIndex;
	UInt32 i;
	Float32 a;
	Float32 b;
	Float32 a2;
	Float32 b2;

	Mix::Scene::IParticleProcessor::FACE face;

	//共通の設定
	face.texCoords[0].Set( 0.0f, 0.0f );
	face.texCoords[1].Set( 1.0f, 0.0f );
	face.texCoords[2].Set( 1.0f, 1.0f );
	face.texCoords[3].Set( 0.0f, 1.0f );

	//使用していません
//	face.normal = Mix::Vector3::Zero();
//	face.tangent = Mix::Vector3::Zero();
//	face.binormal = Mix::Vector3::Zero();

	m_Faces.clear();

	while( it_dat != m_Dats.end() )
	{
		pData = &( *it_dat );

		if( pData->life > 0.0f )
		{
			ratio = 1.0f - ( pData->life * pData->invLifeMax );

			a = 1.0f - ratio;
			b = ratio;
			a2 = a * a;
			b2 = b * b;

			ctrlPos = pData->ctrlPos;
			pos = ( a2 * a * ctrlPos[0] ) + ( 3.0f * a2 * b * ctrlPos[1] ) + ( 3.0f * a * b2 * ctrlPos[2] ) + ( b2 * b * ctrlPos[3] );

			////////////////////////////////////////////////////////////////////////////////////////////////////

			alpha = 1.0f - ::fabs( ratio - 0.5f ) * 2.0f;
			hs = pData->halfSize;

			face.pos = pos;

			face.color = SoulParticleSystem::PROC_BASE_COLOR;
			face.color.a = alpha;

			face.localPoints[0].Set( -hs, -hs, 0.0f );
			face.localPoints[1].Set( +hs, -hs, 0.0f );
			face.localPoints[2].Set( +hs, +hs, 0.0f );
			face.localPoints[3].Set( -hs, +hs, 0.0f );

			m_Faces.push_back( face );

			////////////////////////////////////////////////////////////////////////////////////////////////////

			lagNum = pData->lagNum;

			if( lagNum > 0 )
			{
				lagIndex = ( pData->lagHead > 0 )? ( pData->lagHead - 1 ) : ( SoulParticleSystem::MAX_LAG - 1 );

				for( i = 0; i < lagNum; i++ )
				{
					hs *= SoulParticleSystem::PROC_LAG_HS_COIFFE;
					alpha *= SoulParticleSystem::PROC_LAG_ALPHA_COIFFE;

					face.pos = pData->lags[lagIndex];

//					face.color = SoulParticleSystem::PROC_BASE_COLOR;
					face.color.a = alpha;

					face.localPoints[0].Set( -hs, -hs, 0.0f );
					face.localPoints[1].Set( +hs, -hs, 0.0f );
					face.localPoints[2].Set( +hs, +hs, 0.0f );
					face.localPoints[3].Set( -hs, +hs, 0.0f );

					m_Faces.push_back( face );

					lagIndex = ( lagIndex > 0 )? ( lagIndex - 1 ) : ( SoulParticleSystem::MAX_LAG - 1 );
				}
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( MIX_FLOAT_IS_ZERO( pData->lagTime ) == True )
			{
				pData->lags[pData->lagHead] = pos;
				pData->lagHead = ( pData->lagHead + 1 ) % SoulParticleSystem::MAX_LAG;

				if( pData->lagNum < SoulParticleSystem::MAX_LAG )
				{
					pData->lagNum++;
				}
				else
				{
					pData->lagTail = ( pData->lagTail + 1 ) % SoulParticleSystem::MAX_LAG;
				}
			}

			pData->lagTime += dt;
			if( pData->lagTime > SoulParticleSystem::PROC_LAG_INTERVAL )
			{
				pData->lagTime = 0.0f;
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////

			it_dat++;
		}
		else
		{
			it_dat = m_Dats.erase( it_dat );
		}

		pData->life -= dt;
	}

	return True;
}

Boolean SoulParticleSystem::InternalProcessor::Internal_Duplicate( Mix::Scene::IParticleProcessor** ppProcessor, const wchar_t* pDebugName )
{
	MIX_ERROR_W( InternalProcessor::MSG_NOT_IMPL );
	return False;
}

UInt32 SoulParticleSystem::InternalProcessor::Internal_GetFaceNum( void ) const
{
	return MIX_UIT_TO_UI32( m_Faces.size() );
}

const Mix::Scene::IParticleProcessor::FACE* SoulParticleSystem::InternalProcessor::Internal_GetFaces( void ) const
{
	MIX_ASSERT( m_Faces.size() > 0 );

	return &( m_Faces[0] );
}

#ifdef _DEBUG

void SoulParticleSystem::InternalProcessor::Debug_Draw( const Mix::Matrix4x4& worldMat, Mix::Graphics::Utility::IPerspectiveRenderer* pPersRenderer )
{
	MIX_ASSERT( SoulParticleSystem::DEBUG_TRACK_INTERVAL > MIX_FLOAT_EPSILON );

	std::vector<SoulParticleSystem::DATA>::const_iterator it_dat_begin = m_Dats.begin();
	std::vector<SoulParticleSystem::DATA>::const_iterator it_dat_end = m_Dats.end();
	std::vector<SoulParticleSystem::DATA>::const_iterator it_dat;

	const SoulParticleSystem::DATA* pData;
	const Mix::Vector3* ctrlPos;
	Float32 ratio;
	Float32 a;
	Float32 b;
	Float32 a2;
	Float32 b2;
	Mix::Vector3 prePos;
	Mix::Vector3 curPos;

	pPersRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 0.2f ) );

	for( it_dat = it_dat_begin; it_dat != it_dat_end; ++it_dat )
	{
		pData = &( *it_dat );
		ratio = SoulParticleSystem::DEBUG_TRACK_INTERVAL;

		prePos = pData->ctrlPos[0];

		while( ratio <= 1.0f )
		{
			a = 1.0f - ratio;
			b = ratio;
			a2 = a * a;
			b2 = b * b;

			ctrlPos = pData->ctrlPos;
			curPos = ( a2 * a * ctrlPos[0] ) + ( 3.0f * a2 * b * ctrlPos[1] ) + ( 3.0f * a * b2 * ctrlPos[2] ) + ( b2 * b * ctrlPos[3] );

			pPersRenderer->AddLine( prePos, curPos );

			prePos = curPos;
			ratio += SoulParticleSystem::DEBUG_TRACK_INTERVAL;
		}
	}
}

#endif //_DEBUG

////////////////////////////////////////////////////////////////////////////////////////////////////
// System
////////////////////////////////////////////////////////////////////////////////////////////////////

SoulParticleSystem* SoulParticleSystem::CreateInstance( void )
{
	return MIX_NEW_T( SoulParticleSystem );
}

SoulParticleSystem::SoulParticleSystem( void ) :
m_pGenerator( NULL )
{
	m_Config.baseFlags = Mix::Scene::IParticleSystem::BILLBOARD;
	m_Config.behaviorFlags = 0;
	m_Config.farMinDist = 0.0f;
	m_Config.farMaxDist = 0.0f;
	m_Config.maxSkipFrames = 0;

	m_pGenerator = SoulParticleSystem::InternalGenerator::CreateInstance();
	MIX_ASSERT( m_pGenerator != NULL );
}

SoulParticleSystem::~SoulParticleSystem( void )
{
	MIX_RELEASE( m_pGenerator );
}

const Mix::Scene::IParticleSystem::CONFIG& SoulParticleSystem::GetConfig( void ) const
{
	return m_Config;
}

void SoulParticleSystem::Internal_GetGenerator( Mix::Scene::IParticleGenerator** ppGenerator )
{
	MIX_ADD_REF( m_pGenerator );
	( *ppGenerator ) = m_pGenerator;
}

Boolean SoulParticleSystem::Internal_CreateProcessor( Mix::Scene::IParticleProcessor** ppProcessor )
{
	SoulParticleSystem::InternalProcessor* pProcessor = SoulParticleSystem::InternalProcessor::CreateInstance();
	if( pProcessor == NULL )
	{
		return False;
	}

	( *ppProcessor ) = pProcessor;

	return True;
}

Float32 SoulParticleSystem::Internal_GetGenerateInterval( void ) const
{
	return 0.0f;
}

UInt32 SoulParticleSystem::Internal_GetGenerateCount( void ) const
{
	return 1;
}
