#pragma once

#include "Mix/Scene/IBillboardParticleSystem.h"
#include "Mix/Scene/IParticleProcessor.h"
#include "Mix/Private/Scene/Common/SimpleParticleGenerator.h"
#include "Mix/Private/Container.h"

namespace Mix{ namespace Scene{ namespace Common{

	class BillboardParticleSystem : public Mix::Scene::IBillboardParticleSystem
	{
	public:
		struct DATA
		{
			Float32 rot;

			Mix::Vector3 pos;

			Float32 life;
			Float32 invLifeMax;

			Mix::Vector3 constLinearVelocity;
			Mix::Vector3 constLinearAcceleration;

			Mix::Vector3 linearAcceleration;
			Mix::Vector3 linearVelocity;
			Float32 linearVelocityDamping;

			Float32 angularAcceleration;
			Float32 angularVelocity;
			Float32 angularVelocityDamping;

			Mix::Vector2 initalSize;
			Mix::Vector2 lastSize;

			Mix::Vector4 initalColor;
			Mix::Vector4 middleColor;
			Mix::Vector4 lastColor;
			Float32 colorCtrlPoints[4];

			Mix::Vector2 tex[2];
		};

	private:
		enum VALUE
		{
			LIST_DEF_SIZE		= 32,
			LIST_RESIZE_STEP	= 16,
		};

		class InternalGenerator : public Mix::Scene::Common::SimpleParticleGenerator
		{
		public:
			static InternalGenerator* CreateInstance( void );

		private:
			Float32 m_MinAngularImpulse;
			Float32 m_MaxAngularImpulse;
			Float32 m_DiffAngularImpulse;

			Float32 m_MinAngularAcc;
			Float32 m_MaxAngularAcc;
			Float32 m_DiffAngularAcc;

			Float32 m_MinAngularVelDamp;
			Float32 m_MaxAngularVelDamp;
			Float32 m_DiffAngularVelDamp;

		private:
			InternalGenerator( void );
			virtual ~InternalGenerator( void );

		public:
			////////////////////////////////////////////////////////////////////////////////////////////////////
			// InternalGenerator
			////////////////////////////////////////////////////////////////////////////////////////////////////

			Float32 GetMinAngularImpulse( void ) const;
			Float32 GetMaxAngularImpulse( void ) const;
			void SetAngularImpulse( Float32 minImpulse, Float32 maxImpulse );

			Float32 GetMinAngularAcceleration( void ) const;
			Float32 GetMaxAngularAcceleration( void ) const;
			void SetAngularAcceleration( Float32 minAcc, Float32 maxAcc );

			Float32 GetMinAngularVelocityDamping( void ) const;
			Float32 GetMaxAngularVelocityDamping( void ) const;
			void SetAngularVelocityDamping( Float32 minDamping, Float32 maxDamping );

		public:
			virtual void Internal_GetData( UInt32 dataSize, void* pData );
		};

		class InternalProcessor : public Mix::Scene::IParticleProcessor
		{
		private:
			Mix::ContainerT<BillboardParticleSystem::DATA, Mix::Memory::SECTION_SCENE> m_DataList;
			Mix::ContainerT<Mix::Scene::IParticleProcessor::FACE, Mix::Memory::SECTION_SCENE> m_FacePool;
			UInt32 m_FaceNum;

		public:
			static InternalProcessor* CreateInstance( const wchar_t* pDebugName );

		private:
			InternalProcessor( const wchar_t* pDebugName );
			virtual ~InternalProcessor( void );

		public:
			virtual UInt32 Internal_GetCount( void ) const;
			virtual void Internal_Clear( void );
			virtual UInt32 Internal_Add( Mix::Scene::IParticleGenerator* pGenerator, UInt32 genCount );
			virtual UInt32 Internal_Add( const Mix::Matrix4x4& worldMat, Mix::Scene::IParticleGenerator* pGenerator, UInt32 genCount );
			virtual Boolean Internal_Update( Float32 dt );
			virtual Boolean Internal_Duplicate( Mix::Scene::IParticleProcessor** ppProcessor, const wchar_t* pDebugName );
			virtual UInt32 Internal_GetFaceNum( void ) const;
			virtual const Mix::Scene::IParticleProcessor::FACE* Internal_GetFaces( void ) const;
#ifdef _DEBUG
			virtual void Debug_Draw( const Mix::Matrix4x4& worldMat, Mix::Graphics::Utility::IPerspectiveRenderer* pPerspectiveRenderer );
#endif //_DEBUG
	};

	public:
		static BillboardParticleSystem* CreateInstance( Boolean fixedY, const wchar_t* pDebugName );

	private:
		IParticleSystem::CONFIG m_Config;
		BillboardParticleSystem::InternalGenerator* m_pGenerator;

		Float32 m_MinEmitInterval;
		Float32 m_MaxEmitInterval;
		Float32 m_DiffEmitInterval;

		UInt32 m_MinEmitCount;
		UInt32 m_MaxEmitCount;
		UInt32 m_DiffEmitCount;

#ifdef _DEBUG
		Mix::String m_DebugName;
#endif //_DEBUG

	private:
		BillboardParticleSystem( Boolean bSimWorldSpace, const wchar_t* pDebugName );
		virtual ~BillboardParticleSystem( void );

	public:
		Boolean Initialize( void );

	public:
		////////////////////////////////////////////////////////////////////////////////////////////////////
		// Mix::Scene::IBillboardParticleSystem
		////////////////////////////////////////////////////////////////////////////////////////////////////

		virtual Float32 GetMinAngularImpulse( void ) const;
		virtual Float32 GetMaxAngularImpulse( void ) const;
		virtual void SetAngularImpulse( Float32 minImpulse, Float32 maxImpulse );

		virtual Float32 GetMinAngularAcceleration( void ) const;
		virtual Float32 GetMaxAngularAcceleration( void ) const;
		virtual void SetAngularAcceleration( Float32 minAcc, Float32 maxAcc );

		////////////////////////////////////////////////////////////////////////////////////////////////////
		// Mix::Scene::ISimpleParticleSystem
		////////////////////////////////////////////////////////////////////////////////////////////////////

		virtual Boolean IsBillboard( void ) const;
		virtual Boolean IsSimulationWorldSpace( void ) const;

		virtual Boolean IsInvisibleSleep( void ) const;
		virtual void SetInvisibleSleep( Boolean state );

		virtual Float32 GetMinFarDist( void ) const;
		virtual Float32 GetMaxFarDist( void ) const;
		virtual void SetFarDist( Float32 minDist, Float32 maxDist );

		virtual Boolean IsFarSkipFrames( void ) const;
		virtual void SetFarSkipFrames( Boolean state );
		virtual UInt32 GetMaxFarSkipFrames( void ) const;
		virtual void SetMaxFarSkipFrames( UInt32 value );

		virtual Float32 GetMinEmitInterval( void ) const;
		virtual Float32 GetMaxEmitInterval( void ) const;
		virtual void SetEmitInterval( Float32 minEi, Float32 maxEI );

		virtual UInt32 GetMinEmitCount( void ) const;
		virtual UInt32 GetMaxEmitCount( void ) const;
		virtual void SetEmitCount( UInt32 minEC, UInt32 maxEC );

		virtual const Mix::Vector3& GetEmitRadius( void ) const;
		virtual void SetEmitRadius( const Mix::Vector3& radius );

		virtual Float32 GetEmitOffset( void ) const;
		virtual void SetEmitOffset( Float32 offset );

		virtual Float32 GetMinLife( void ) const;
		virtual Float32 GetMaxLife( void ) const;
		virtual void SetLife( Float32 minLife, Float32 maxLife );

		virtual Float32 GetMinMass( void ) const;
		virtual Float32 GetMaxMass( void ) const;
		virtual void SetMass( Float32 minMass, Float32 maxMass );

		virtual const Mix::Vector3& GetGravity( void ) const;
		virtual void SetGravity( const Mix::Vector3& gravity );

		virtual const Mix::Vector3& GetConstantLinearVelocity( void ) const;
		virtual void SetConstantLinearVelocity( const Mix::Vector3& vel );

		virtual const Mix::Vector3& GetMinLinearImpulse( void ) const;
		virtual const Mix::Vector3& GetMaxLinearImpulse( void ) const;
		virtual void SetLinearImpulse( const Mix::Vector3& minImpulse, const Mix::Vector3& maxImpulse );

		virtual const Mix::Vector3& GetMinLinearAcceleration( void ) const;
		virtual const Mix::Vector3& GetMaxLinearAcceleration( void ) const;
		virtual void SetLinearAcceleration( const Mix::Vector3& minAcc, const Mix::Vector3& maxAcc );

		virtual Float32 GetMinLinearVelocityDamping( void ) const;
		virtual Float32 GetMaxLinearVelocityDamping( void ) const;
		virtual void SetLinearVelocityDamping( Float32 minDamping, Float32 maxDamping );

		virtual Float32 GetMinAngularVelocityDamping( void ) const;
		virtual Float32 GetMaxAngularVelocityDamping( void ) const;
		virtual void SetAngularVelocityDamping( Float32 minDamping, Float32 maxDamping );

		virtual const Mix::Vector2& GetInitalSize( void ) const;
		virtual const Mix::Vector2& GetLastSize( void ) const;
		virtual Float32 GetMinSizeRand( void ) const;
		virtual Float32 GetMaxSizeRand( void ) const;
		virtual void SetSize( const Mix::Vector2& initalSize, const Mix::Vector2& lastSize, Float32 minSizeScale, Float32 maxSizeScale );

		virtual const Mix::Vector4& GetInitalColor( void ) const;
		virtual const Mix::Vector4& GetMiddleColor( void ) const;
		virtual const Mix::Vector4& GetLastColor( void ) const;
		virtual void SetColor( const Mix::Vector4& initalColor, const Mix::Vector4& middleColor, const Mix::Vector4& lastColor );

		virtual Float32 GetColorControlPoint1( void ) const;
		virtual Float32 GetColorControlPoint2( void ) const;
		virtual void SetColorControlPoints( Float32 p1, Float32 p2 );

		virtual const Mix::Vector2& GetTexTL( void ) const;
		virtual const Mix::Vector2& GetTexBR( void ) const;
		virtual void SetTexCoords( const Mix::Vector2& tl, const Mix::Vector2& br );

		////////////////////////////////////////////////////////////////////////////////////////////////////
		// Mix::Scene::IParticleSystem
		////////////////////////////////////////////////////////////////////////////////////////////////////

		virtual const Mix::Scene::IParticleSystem::CONFIG& GetConfig( void ) const;
		virtual void Internal_GetGenerator( Mix::Scene::IParticleGenerator** ppGenerator );
		virtual Boolean Internal_CreateProcessor( Mix::Scene::IParticleProcessor** ppProcessor );
		virtual Float32 Internal_GetGenerateInterval( void ) const;
		virtual UInt32 Internal_GetGenerateCount( void ) const;

	public:
		static const wchar_t* FAILED_CREATE;
	};

}}}
