//
// Asura_Photon.h
//
// Created by Kevin Lea - 12/06/2001 10:54:05
// 
// Copyright (C) 2001 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __ASURA_PHOTON_H__
#define __ASURA_PHOTON_H__

#include "Core/Asura_Handle.h"
#include "Core/Asura_Maths.h"
#include "Core/Asura_Mempool.h"

#if defined(ASURA_USE_INDEPENDENT_SHADOW_SYSTEM)
#include "Shadows/Asura_Shadow_System.h"
#endif

#if defined (ASURA_USE_NEW_VISIBILITY_SYSTEM )
#include "Visibility/Asura_Visibility_System.h"
#endif

template< class T > class Asura_DynamicBVH;
template<typename T> class Asura_TrackingHandlePool;

class Asura_Photon
{
	/// Kevin Lea - 15/08/2002 16:01:43 - we don't want anyone to new or delete a photon
	/// directly - only Asura_Photon_System can do this
	friend class Asura_Photon_System;
	
	/// Kevin Lea - 25/09/2002 15:25:13 - Asura_Photon's are created/destroyed from a 
	/// memory pool, and so don't want (or need) a constructor or deconstructor

	public:


		inline void SetAsActive(const bool bFlag)
		{
			m_bActive = bFlag;
		}

		/// Kevin Lea - 16/09/2002 17:46:15 - access methods
		
		inline bool IsActive(void) const
		{
			return m_bActive;
		}

		inline void SetPosition(const Asura_Vector_3& xPosition)
		{
			m_xPosition = xPosition;
		}

		inline const Asura_Vector_3& GetPosition(void) const
		{
			return m_xPosition;
		}


		inline void SetColours(const float fRed, const float fGreen, const float fBlue)
		{
			m_fR = fRed;
			m_fG = fGreen;
			m_fB = fBlue;
		}
		inline float GetRedComponent(void) const
		{
			return m_fR;
		}
		inline float GetGreenComponent(void) const
		{
			return m_fG;
		}
		inline float GetBlueComponent(void) const
		{
			return m_fB;
		}


		inline void SetRange(const float fRange)
		{
			m_fRange = fRange;
		}
		inline float GetRange(void) const
		{
			return m_fRange;
		}

		inline void SetFlags(const u_int uFlags)
		{
			m_uFlags = uFlags;
		}
		inline u_int GetFlags(void) const
		{
			return m_uFlags;
		}

		inline bool HasCorona(void) const
		{
			return m_bHasCorona;
		}

		inline void SetCorona(bool bCorona)
		{
			m_bHasCorona = bCorona;
		}

		inline Asura_Handle GetHandle(void) const
		{
			return m_iHandle;
		}

		inline void SetUseBoundingBox(bool bUseBoundingBox)
		{
			m_bUseBoundingBox = bUseBoundingBox;
		}

		inline bool GetUseBoundingBox() const
		{
			return m_bUseBoundingBox;
		}

		inline void SetBoundingBox(const Asura_Bounding_Box& xBox)
		{
			m_xBoundingBox = xBox;
		}

		inline const Asura_Bounding_Box& GetBoundingBox() const
		{
			return m_xBoundingBox;
		}

		inline void SetShadowVolume( bool b )
		{
			m_bIsShadowVolume = b;
		}

		inline bool IsShadowVolume() const
		{
			return m_bIsShadowVolume;
		}

		inline void SetAsStatic( bool b )
		{
			m_bIsStatic = b;
		}

		inline bool IsStatic() const
		{
			return m_bIsStatic;
		}

		inline void SetDontLightEnv(bool b)
		{
			m_bDontLightEnv = b;
		}
	
#if defined(ASURA_USE_INDEPENDENT_SHADOW_SYSTEM)
		const Asura_ShadowParameters& GetShadowParameters(void) const
		{
			return m_xShadowParams;
		}

		void SetShadowParameters(const Asura_ShadowParameters& xParams) 
		{
			m_xShadowParams = xParams;
		}
#endif

#if defined (ASURA_TARGET_SM3)
		void SetFalloffScale(float fScale) {m_fFalloffScale = fScale;}
		void SetFalloffPower(float fPower) {m_fFalloffPower = fPower;}

		float GetFalloffScale(void) const {return m_fFalloffScale;}
		float GetFalloffPower(void) const {return m_fFalloffPower;}

		bool GetCustomLightVolumeEnable(void) const   {return m_bUsingCustomLightVolume;}
		void SetCustomLightVolumeEnable(bool bEnable) {m_bUsingCustomLightVolume = bEnable;}

		void SetCustomLightVolume(const Asura_Bounding_Box& xBB)   {m_xCustomLightVolume = xBB;}
		void SetCustomLightVolumeOrient(const Asura_Quat& xOrient) {m_xCustomLightVolumeOrient = xOrient;}

		const Asura_Bounding_Box& GetCustomLightVolume(void) const {return m_xCustomLightVolume;}
		const Asura_Quat& GetCustomLightVolumeOrient(void) const   {return m_xCustomLightVolumeOrient;}

		void SetIsDirectional(bool bEnable) {m_bIsDirectional = bEnable;}
		bool GetIsDirectional(void) const   {return m_bIsDirectional;}
		
		const Asura_Vector_3 GetLightDirection(void) const
		{
			Asura_Vector_3 xDir;
			xDir.x = -m_xOrient.e13;
			xDir.y = -m_xOrient.e23;
			xDir.z = -m_xOrient.e33;
			return xDir;
		}

		void SetLightOrientation(const Asura_Matrix_3x3& xOrient) {m_xOrient = xOrient;}
		const Asura_Matrix_3x3& GetLightOrientation(void) const {return m_xOrient;}

		bool  GetDistanceFadeEnable(void) const {return m_bDistanceFadeEnable;}
		float GetDistanceFadeStart(void) const	{return m_fDistanceFadeStart;}
		float GetDistanceFadeDist(void) const	{return m_fDistanceFadeDist;}

		void  SetLimitAnglesEnabled(bool bEnable)  {m_bHasAngularLimits = bEnable;}
		void  SetLimitAngleInner(float fValue)	   {m_fInnerLimitAngle  = fValue;}
		void  SetLimitAngleOuter(float fValue)	   {m_fOuterLimitAngle  = fValue;}

		bool  GetLimitAnglesEnabled(void) const	{return m_bHasAngularLimits;}
		float GetLimitAngleInner(void) const	{return m_fInnerLimitAngle;}
		float GetLimitAngleOuter(void) const	{return m_fOuterLimitAngle;}

		void  SetProjCubeTextureIndex(int iIdx)		{m_iProjCubeTextureIndex = iIdx;}
		int   GetProjCubeTextureIndex(void) const   {return m_iProjCubeTextureIndex;}

		void  SetProjCubeBlurredTextureIndex(int iIdx)		{m_iProjCubeBlurredTextureIndex = iIdx;}
		int   GetProjCubeBlurredTextureIndex(void) const    {return m_iProjCubeBlurredTextureIndex;}

		void  SetProjCubeLerpDist(float fDist)	{m_fProjCubeLerpDist = fDist;}
		float GetProjCubeLerpDist(void)	const	{return m_fProjCubeLerpDist;}

		void  SetDistanceFade(bool bEnable, float fStart = 0.0f, float fDist = 0.0f)
		{
			m_fDistanceFadeStart  = fStart;
			m_fDistanceFadeDist   = fDist;
			m_bDistanceFadeEnable = bEnable;
		}

		void RenderLightVolume(void);

#if defined( ASURA_USE_NEW_VISIBILITY_SYSTEM )
		bool IsVisible( void );
#endif

#endif

	private:
		
		Asura_Vector_3	m_xPosition;

		float			m_fRange;
		//RAR 20/05/03 - Colours are 0 to 1 , with 0.5 being max normal brightness
		float			m_fR;
		float			m_fG;
		float			m_fB;

		Asura_Handle	m_iHandle;

		Asura_Bounding_Box m_xBoundingBox;

		union
		{
			u_int		m_uFlags;

			struct
			{
				u_int	m_bActive					: 1,
						m_bUseCuboidalRanges		: 1,
						m_bHasCorona				: 1,
						m_bUseBoundingBox			: 1,
						m_bIsShadowVolume			: 1,
						m_bIsStatic					: 1,
						m_bDontLightEnv				: 1,
						m_bUsingCustomLightVolume	: 1,
						m_bIsDirectional			: 1,
						m_bHasAngularLimits			: 1;
			};
		};

#if defined(ASURA_USE_INDEPENDENT_SHADOW_SYSTEM)
		Asura_ShadowParameters m_xShadowParams;
#endif

#if defined (ASURA_TARGET_SM3)
		// DMR 18/04/07
		// Extensions needed for the new dynamic light entity, to support adjustable falloff
		// and custom light volumes
		float			   m_fFalloffScale;
		float			   m_fFalloffPower;
		Asura_Bounding_Box m_xCustomLightVolume;
		Asura_Quat		   m_xCustomLightVolumeOrient;		
		float			   m_fDistanceFadeStart;
		float			   m_fDistanceFadeDist;
		bool			   m_bDistanceFadeEnable;
		float			   m_fInnerLimitAngle;
		float			   m_fOuterLimitAngle;
		int				   m_iProjCubeTextureIndex;
		int				   m_iProjCubeBlurredTextureIndex; 
		float			   m_fProjCubeLerpDist;
		Asura_Matrix_3x3   m_xOrient;

#if defined (ASURA_USE_NEW_VISIBILITY_SYSTEM)
		Asura_Visibility_Query m_xVisibilityQuery;
#else
public:
		int				   m_iLastKnownModule;
#endif

#endif // defined( ASURA_TARGET_SM3 )

};

class Asura_Local_Photon
{
	friend class Asura_Photon_System;

	public:
	
		inline const Asura_Vector_3& GetPosition(void) const
		{
			return Photon->GetPosition();
		}

		// These Asura_Local_Photon colour fns have to be defined later after Asura_Photon_System is declared.
		inline float GetRedComponent(void) const;
		inline float GetGreenComponent(void) const;
		inline float GetBlueComponent(void) const;

		inline float GetRange(void) const
		{
			return Photon->GetRange();
		}


		inline Asura_Handle GetHandle(void) const
		{
			return Photon->GetHandle();
		}

#if defined(ASURA_USE_INDEPENDENT_SHADOW_SYSTEM)
		inline const Asura_ShadowParameters &GetShadowParameters() const
		{
			return Photon->GetShadowParameters();
		}
#endif

		// VGP 15Mar2006 Needed by HighEnd env renderer
		const Asura_Photon* GetPhoton() const
		{
			return Photon;
		}

	private:

		const Asura_Photon*	Photon;
		float			Influence;

		// KJFL - 2004-04-30 - removed this distance term since
		// it wasn't used it anything but Dredd style debris effects
		// float           m_fDistance; // Distance to test centre.
};

class Asura_Photon_Sampling_Result
{
	public:
		float	m_fDiffuseR;
		float	m_fDiffuseG;
		float	m_fDiffuseB;

		float	m_fSpecularR;
		float	m_fSpecularG;
		float	m_fSpecularB;
};


#define ASURA_MAX_NO_OF_LOCAL_PHOTONS 32

typedef Asura_TrackingHandlePool_Iterator<Asura_Photon> Asura_Photon_Iterator;

class Asura_Photon_System
{
	/// Kevin Lea - 26/09/2002 14:39:53 - make friends of Photon chunks and Lighting System;
	/// the later will contain tools-esque code that need not be compiled into non-tools builds
	friend class Asura_Lighting_System;
	friend class Asura_Lighting_Solution;

	public:
		static void Initialise(void);
		static void Reset(void);
		static void Update(void);
		static void Shutdown(void);

		static Asura_Photon*	CreatePhoton(void);
		static void				DestroyPhoton(const Asura_Photon* pxPhoton);

		/// Kevin Lea - 25/09/2002 10:47:01 - when loading entities, photon handles
		/// will have been saved off instead of pointers (which will obviously not 
		/// remain the same). GetPhotonPointer will return the new pointer based on the handle.
		static Asura_Photon*	GetPhotonPointer(Asura_Handle iHandle);
		
		static void MakePhotonListForBoundingBox( const Asura_Bounding_Box& xBoundingBox,
												  const Asura_Vector_3& xBoundingBoxCentre,
                                                  const bool bIncludeStaticPhotons,
                                                  const bool bIncludeDynamicPhotons,
                                                  u_int uMaxNumberOfPhotons = ASURA_MAX_NO_OF_LOCAL_PHOTONS,
												  bool bResetLocalPhotons = true
												 ) ASURA_CODE_SECTION("photon_list");
		static void CalculateAverageLocalLightPos(Asura_Vector_3& xPos);

		static const Asura_Photon_Sampling_Result* SamplePhotonList(const Asura_Vector_3& xPosition);
	
		static u_int NumberOfLocalPhotons;
		static Asura_Local_Photon LocalPhotons[ASURA_MAX_NO_OF_LOCAL_PHOTONS];

        // (MH - 05.09.2002) want to average out the last n photons for renderers which
        // can't support max local photons. can't create a temporary since they are private.
        static Asura_Photon* s_pxTemporaryAveragePhoton;
	
		// Loop over all photons calling the callback function.
		static void TraverseAll(void(*pfnCallback)(Asura_Photon* pxPhoton),
								const bool bIncludeStaticPhotons,
								const bool bIncludeDynamicPhotons);

		static void TraverseAll(void(*pfnCallback)(Asura_Photon* pxPhoton, void* pData),
								void* pData,
								const bool bIncludeStaticPhotons,
								const bool bIncludeDynamicPhotons);

		static float GetShadowVolumeScale()
		{
			return s_fShadowVolumeScale;
		}

		static float GetEntityAmbientRed();
		static float GetEntityAmbientGreen();
		static float GetEntityAmbientBlue();

        static const Asura_DynamicBVH<Asura_Photon>& GetDynamicPhotonBVH( void );
		static inline Asura_TrackingHandlePool<Asura_Photon>* GetDynamicPhotonList( void ) { return s_pxDynamicPhotonList; }

		static Asura_Photon_Iterator GetDynamicIterator(void);

	private:

        static bool AABBTreeCreateCallback( u_int uNumStaticPhotons, Asura_Bounding_Box* pxBBs, Asura_Vector_3* pxCentres );
		
		static bool StaticPhotonCallback(unsigned short index) ASURA_CODE_SECTION("photon_list");
		static void DynamicPhotonCallback( const Asura_Photon& xPhoton ) ASURA_CODE_SECTION("photon_list");

		static int PhotonSortSub(const void *cmp1, const void *cmp2);

		static Asura_TrackingHandlePool<Asura_Photon>*		s_pxDynamicPhotonList;
		
		static class Asura_AABB_Tree*						s_pxStaticPhotonTree;
		static Asura_Photon*								s_pxStaticPhotonStorage;
		static u_int										s_uNumberOfStaticPhotons;

		static const Asura_Vector_3*						s_pxProximityTestCentre;
		static const Asura_Bounding_Box*					s_pxProximityTestBoundingBox;
		static float										s_fProximityTestMaxInfluence;

		static Asura_Photon_Sampling_Result					s_xPhotonResult;

		static float										s_fShadowVolumeScale;

		static bool											s_bGettingPhotonsForEnv;
		
};


// These Asura_Local_Photon colour fns have to be defined here after Asura_Photon_System is declared.

inline float Asura_Local_Photon::GetRedComponent(void) const
{
	if (Photon->IsStatic())
	{
		return Photon->GetRedComponent() * Asura_Photon_System::GetShadowVolumeScale();
	}
	else
	{
		return Photon->GetRedComponent();
	}
}

inline float Asura_Local_Photon::GetGreenComponent(void) const
{
	if (Photon->IsStatic())
	{
		return Photon->GetGreenComponent() * Asura_Photon_System::GetShadowVolumeScale();
	}
	else
	{
		return Photon->GetGreenComponent();
	}
}

inline float Asura_Local_Photon::GetBlueComponent(void) const
{
	if (Photon->IsStatic())
	{
		return Photon->GetBlueComponent() * Asura_Photon_System::GetShadowVolumeScale();
	}
	else
	{
		return Photon->GetBlueComponent();
	}
}

#endif//__ASURA_PHOTON_H__
