//
// Asura_Skybox.h
//
// Created by Kevin Lea - 02/10/2001 10:37:12
// 
// Copyright (C) 2001 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __ASURA_SKYBOX_H__
#define __ASURA_SKYBOX_H__

// Kevin Lea - 02/10/2001 11:18:17 - Rewrite of skybox code to make things a bit nicer. Future changes will be needed
// to cope with things such as clouds customisability, stars, sun/moon etc.

#include "Core/Asura_Maths.h"
#include "Core/Asura_String.h"
#include "Shapes/Asura_Shapes.h"

template <class T> class Asura_Hashed_List;
class Asura_ClientEntity_PhysicalObject;


class Asura_Skybox
{
	public:
		
		// Kevin Lea - 02/10/2001 17:37:33 - basic Init & Shutdown code called by the Asura_SpecialFX_System
		static void Initialise(void);
		static void Shutdown(void);
		static void Reset(void);

		// Kevin Lea - 02/10/2001 10:49:46 - Render methods are platform specific; code exists in Asura_PlatformSkybox.cpp
		// On the assumption that the skybox is fairly static (e.g. shading and orientation are not changing dynamically in-game),
		// I've allowed for 'one-shot' initialise/shutdown methods to cut back on per-frame overheads. However, anything which changes
		// the skybox (e.g. Tools) will cause these initialise routines to be re-called.
		static void Render(void);


		// Kevin Lea - 02/10/2001 11:07:32 - methods to set and get the skybox's settings
		// primarily expected to be used by the Tools and loaders

		// Do not reorder, as textures are saved out in chunks in this order
		// (AW - 24.09.2004) - I have reordered this for chunk version 6 to reduce texture
		// uploads when using the upside-down texture options.
		enum ASURA_SKYBOX_TEXTURES
		{
			ASURA_SKYBOX_DN,
			ASURA_SKYBOX_FR,
			ASURA_SKYBOX_BK,
			ASURA_SKYBOX_LF,
			ASURA_SKYBOX_RT,
			ASURA_SKYBOX_UP,
			ASURA_SKYBOX_FRACTALSKY,
			ASURA_SKYBOX_FRACTALSKY_2,

			ASURA_SKYBOX_NUM_TEXTURES,
		};
	
		static void SetTexture( const Asura_Char* szName, int iFace);
		static void SetOrientationAroundYAxis(Asura_Angle fAngle);
		// Kevin Lea - 02/10/2001 11:09:01 - SetColours: values in range 0.0 to 255.0 inclusive
		static void SetColours(float fRed, float fGreen, float fBlue); 
		static void SetObjectColours(float fRed, float fGreen, float fBlue); 
		static void SetBackTextureIsFrontUpsideDown(bool bBackIsFront);
		static void SetRightTextureIsLeftUpsideDown(bool bRightIsLeft);
		static void SetMirrorBelowHorizon(bool bMirror);

		static void SetDrawClouds(bool bDrawClouds) {s_bDrawClouds = bDrawClouds;}
		// AJG - 13/02/04 - Switch between two cloud textures
		static void SetUseSecondCloudTexture(bool bSecond = true) {s_bUseSecondClouds = bSecond;};
		
        static const Asura_String& GetTexture( int iFace ) 
		{ 
			if (iFace == ASURA_SKYBOX_BK &&
				GetBackTextureIsFrontUpsideDown())
			{
				return s_axTextureName[ASURA_SKYBOX_FR];
			}
			else if (iFace == ASURA_SKYBOX_RT &&
					GetRightTextureIsLeftUpsideDown())
			{
				return s_axTextureName[ASURA_SKYBOX_LF];
			}
			else if (iFace == ASURA_SKYBOX_DN && s_bMirrorBelowHorizon)
			{
				return s_axTextureName[ASURA_SKYBOX_UP];
			}
			return s_axTextureName[iFace]; 
		}
        static void GetOrientationAroundYAxis( Asura_Angle& fAngle ) { fAngle = s_fOrientationAroundYAxis; }
		static void GetColours(float& fRed, float& fGreen, float& fBlue);
		static void GetObjectColours(float& fRed, float& fGreen, float& fBlue);
        static bool GetDrawClouds( void ) { return s_bDrawClouds; }
        static float GetR( void ) { return s_fRed; }
        static float GetG( void ) { return s_fGreen; }
        static float GetB( void ) { return s_fBlue; }
		static bool GetIsUsingSecondClouds() { return s_bUseSecondClouds; };
		static bool GetBackTextureIsFrontUpsideDown() {return s_bBackTextureIsFrontUpsideDown;}
		static bool GetRightTextureIsLeftUpsideDown() {return s_bRightTextureIsLeftUpsideDown;}
		static bool GetMirrorBelowHorizon() { return s_bMirrorBelowHorizon; }

        // Return number of specified skybox textures.
        static u_int GetNumSkyboxTextures( void );

		// Merge-with-skybox object handling
#ifdef ASURA_TOOLS
		static void InvalidateSkyboxObjects(){s_bNeedToRegenerateSkyboxObjects = true;}//forces skybox object list to be recalculated for next render
#endif

		//used by the entities to register themselves as skybox objects
		static void AddSkyboxObject(Asura_ClientEntity_PhysicalObject* pxEntity);
		static void RemoveSkyboxObject(Asura_ClientEntity_PhysicalObject* pxEntity);

		struct SkyboxObject
		{
			SkyboxObject() : m_pxShapeInst(NULL) { }
			~SkyboxObject() { delete m_pxShapeInst; }
			Asura_Vector_3 m_xPosition;
			Asura_Matrix_3x3 m_xOrientation;
			int m_iAnimFlags;
			Asura_Hash_ID m_uShapeOrSkinID;
			Asura_Hash_ID m_uAnimID;
			u_int m_uLightSets;

			Asura_Shape_Instance* m_pxShapeInst;

			union
			{
				// NOTE: These bit's are written out, adding any new bits means
				// ~~~~~ that you will need to add those bits to the bitfield
				//       preparation code in "WriteToChunkStream".
				u_int m_uFlags;
				struct
				{
					u_int m_bFullBright:1;
				};
			};

		};


		static void SetSkyboxVisible( bool bVisible ) { s_bSkyboxVisible = bVisible; }
		static bool IsSkyboxVisible( void ) { return s_bSkyboxVisible; }

#ifdef ASURA_TOOLS
		static void SetSkyboxName(const char* szName) {s_xSkyboxName = szName;}
		static const char* GetSkyboxName() {return s_xSkyboxName.GetString();}
#endif
		static void RequestRenderSetup();

#if defined( ASURA_TARGET_SM3)
		static void SetSkyboxColour( float* pfColour )
		{
			for( int i = 0; i < 3; ++i )
			{
				s_afSkyboxColour[ i ] = pfColour[ i ];
			}
		}
		static void GetSkyboxColour( float* pfSkyboxColour
#ifdef ASURA_TOOLS
			, bool bNormalize = false
#endif
			)
		{
			for( int i = 0; i < 3; ++i )
			{
				pfSkyboxColour[ i ] = s_afSkyboxColour[ i ];
#ifdef ASURA_TOOLS
				if( bNormalize )
				{
					pfSkyboxColour[ i ] /= 255.0f;
				}
#endif
			}
		}
		static void SetHaze( float* pfColour1, float* pfColour2, bool bDisableHaze, bool bDisable2Haze, bool b1MirrorBelowHorizon, bool b2MirrorBelowHorizon )
		{
			for( int i = 0; i < 3; ++i )
			{
				s_afHazeColour1[ i ] = pfColour1[ i ];
				s_afHazeColour2[ i ] = pfColour2[ i ];
			}
			s_bDisableHaze			= bDisableHaze;
			s_bDisable2Haze			= bDisable2Haze;
			s_b1MirrorBelowHorizon	= b1MirrorBelowHorizon;
			s_b2MirrorBelowHorizon	= b2MirrorBelowHorizon;
		}
		static void GetHaze( float* pfColour1, float* pfColour2, bool& bDisableHaze, bool& bDisable2Haze, bool& b1MirrorBelowHorizon, bool& b2MirrorBelowHorizon
#ifdef ASURA_TOOLS
			, bool bNormalize = false
#endif
			)
		{
			for( int i = 0; i < 3; ++i )
			{
				pfColour1[ i ] = s_afHazeColour1[ i ];
				pfColour2[ i ] = s_afHazeColour2[ i ];
#ifdef ASURA_TOOLS
				if( bNormalize )
				{
					pfColour1[ i ] /= 255.0f;
					pfColour2[ i ] /= 255.0f;
				}
#endif
			}
			bDisableHaze			= s_bDisableHaze;
			bDisable2Haze			= s_bDisable2Haze;
			b1MirrorBelowHorizon	= s_b1MirrorBelowHorizon;
			b2MirrorBelowHorizon	= s_b2MirrorBelowHorizon;
		}
		static void SetSun( float* pfColour1, float* pfColour2, bool bDisableSun, bool bDisable2Sun )
		{
			for( int i = 0; i < 3; ++i )
			{
				s_afSunColour1[ i ] = pfColour1[ i ];
				s_afSunColour2[ i ] = pfColour2[ i ];
			}
			s_bDisableSun			= bDisableSun;
			s_bDisable2Sun			= bDisable2Sun;
		}
		static void GetSun( float* pfColour1, float* pfColour2, bool& bDisableSun, bool& bDisable2Sun
#ifdef ASURA_TOOLS
			, bool bNormalize = false
#endif
			)
		{
			for( int i = 0; i < 3; ++i )
			{
				pfColour1[ i ] = s_afSunColour1[ i ];
				pfColour2[ i ] = s_afSunColour2[ i ];
#ifdef ASURA_TOOLS
				if( bNormalize )
				{
					pfColour1[ i ] /= 255.0f;
					pfColour2[ i ] /= 255.0f;
				}
#endif
			}
			bDisableSun			= s_bDisableSun;
			bDisable2Sun		= s_bDisable2Sun;
		}
		static void SetHazeStrength( float fStrength1, float fStrength2 )
		{
			s_fHazeStrength1 = fStrength1;
			s_fHazeStrength2 = fStrength2;
		}
		static void GetHazeStrength( float* fStrength1, float* fStrength2
#ifdef ASURA_TOOLS
			, bool bNormalize = false
#endif
			)
		{
#ifdef ASURA_TOOLS
			if( bNormalize )
			{
				*fStrength1 = Asura_Maths::Power(2, ( 7.0f - s_fHazeStrength1 ) );//( 100.01f - s_fHazeStrength1 );
				*fStrength2 = Asura_Maths::Power(2, ( 7.0f - s_fHazeStrength2 ) );//( 100.01f - s_fHazeStrength2 );
			}
			else
#endif
			{
				*fStrength1 = s_fHazeStrength1;
				*fStrength2 = s_fHazeStrength2;
			}
		}
		static void SetSunSize( float fSize1, float fSize2 )
		{
			s_fSunStrength1 = fSize1;
			s_fSunStrength2 = fSize2;
		}
		static void GetSunSize( float* fSize1, float* fSize2
#ifdef ASURA_TOOLS
			, bool bNormalize = false
#endif
			)
		{
#ifdef ASURA_TOOLS
			if( bNormalize )
			{
				*fSize1 = Asura_Maths::Power(5, ( 7.0f - s_fSunStrength1 ) );
				*fSize2 = Asura_Maths::Power(5, ( 7.0f - s_fSunStrength2 ) );
			}
			else
#endif
			{
				*fSize1 = s_fSunStrength1;
				*fSize2 = s_fSunStrength2;
			}
		}
		static void SetSunDir( float* pfDirection1, float* pfDirection2 )
		{
#ifdef ASURA_TOOLS
			for( int i = 0; i < 2; ++i )
			{
				s_afSunDir1[ i ] = pfDirection1[ i ];
				s_afSunDir2[ i ] = pfDirection2[ i ];
			}
#else
			for( int i = 0; i < 3; ++i )
			{
				s_afSunDir1[ i ] = pfDirection1[ i ];
				s_afSunDir2[ i ] = pfDirection2[ i ];
			}
#endif
		}
		static void GetSunDir( float* pfDirection1, float* pfDirection2
#ifdef ASURA_TOOLS
			, bool bNormalize = false
#endif
			)
		{
#ifdef ASURA_TOOLS
			if( bNormalize )
			{
				float fSinPitch, fSinYaw;
				float fCosPitch, fCosYaw;
				Asura_Vector_3 xDir1, xDir2;
				Asura_Maths::SinCos( Asura_Maths::DegToRad( s_afSunDir1[ 0 ] ), fSinPitch, fCosPitch );
				Asura_Maths::SinCos( Asura_Maths::DegToRad( s_afSunDir1[ 1 ] ), fSinYaw, fCosYaw );
				xDir1.x = fCosYaw * fCosPitch;
				xDir1.y = fSinPitch;
				xDir1.z = fSinYaw * fCosPitch;

				Asura_Maths::SinCos( Asura_Maths::DegToRad( s_afSunDir2[ 0 ] ), fSinPitch, fCosPitch );
				Asura_Maths::SinCos( Asura_Maths::DegToRad( s_afSunDir2[ 1 ] ), fSinYaw, fCosYaw );
				xDir2.x = fCosYaw * fCosPitch;
				xDir2.y = fSinPitch;
				xDir2.z = fSinYaw * fCosPitch;

				xDir1.Normalise(); xDir2.Normalise();
				pfDirection1[ 0 ] = xDir1.x; pfDirection1[ 1 ] = xDir1.y; pfDirection1[ 2 ] = xDir1.z;
				pfDirection2[ 0 ] = xDir2.x; pfDirection2[ 1 ] = xDir2.y; pfDirection2[ 2 ] = xDir2.z;
			}
			else
			{
				for( int i = 0; i < 2; ++i )
				{
					pfDirection1[ i ] = s_afSunDir1[ i ];
					pfDirection2[ i ] = s_afSunDir2[ i ];
				}
			}
#else
			for( int i = 0; i < 3; ++i )
			{
				pfDirection1[ i ] = s_afSunDir1[ i ];
				pfDirection2[ i ] = s_afSunDir2[ i ];
			}
#endif
		}
		static bool GetUseNewSkyboxShader( void )
		{
			return s_bUseNewSkyboxShader;
		}
		static void SetUseNewSkyboxShader( bool bUseNewSkyboxShader )
		{
			s_bUseNewSkyboxShader = bUseNewSkyboxShader;
		}
#endif
	private:
#if defined( ASURA_TARGET_SM3)
		static float s_afSkyboxColour[ 3 ];
		static float s_afHazeColour1[ 3 ];
		static float s_fHazeStrength1;
		static float s_afHazeColour2[ 3 ];
		static float s_fHazeStrength2;
		static float s_afSunColour1[ 3 ];
		static float s_fSunStrength1;
		static float s_afSunColour2[ 3 ];
		static float s_fSunStrength2;
#ifdef ASURA_TOOLS
		static float s_afSunDir1[ 2 ];
		static float s_afSunDir2[ 2 ];
#else
		static float s_afSunDir1[ 3 ];
		static float s_afSunDir2[ 3 ];
#endif
#endif

		// Kevin Lea - 02/10/2001 17:26:46 - internal data
		static Asura_String s_axTextureName[ASURA_SKYBOX_NUM_TEXTURES];
		static Asura_Angle s_fOrientationAroundYAxis;
		static float s_fRed;
		static float s_fGreen;
		static float s_fBlue;
		
		//ambient colours for skybox objects
		static float s_fObjectRed;
		static float s_fObjectGreen;
		static float s_fObjectBlue;


		static bool s_bDrawClouds;
		static bool s_bUseSecondClouds;
		static bool s_bBackTextureIsFrontUpsideDown;
		static bool s_bRightTextureIsLeftUpsideDown;
		static bool s_bMirrorBelowHorizon;
		static bool s_bSkyboxVisible;

#if defined( ASURA_TARGET_SM3)
		static bool s_bUseNewSkyboxShader;

		static bool s_bDisableHaze;
		static bool s_bDisable2Haze;
		static bool s_bDisableSun;
		static bool s_bDisable2Sun;
		static bool s_b1MirrorBelowHorizon;
		static bool s_b2MirrorBelowHorizon;
#endif

		#ifdef ASURA_TOOLS
		//Skybox used when doing separate skybox exports. RAR 10/03/2006
		static Asura_String s_xSkyboxName;
		#endif

        static void ResetInternal( bool bCallPlatformReset );

        static void Platform_Initialise( void );
        static void Platform_Render( void );
        static void Platform_Reset( void );
        static void Platform_Shutdown( void );

        // Used to notify platform-specific code that some settings have
        // changed, eg. a skybox texture has changed. In tools, this may
        // happen at any time. Otherwise it should just happen after a level
        // has been loaded.
        static void Platform_DoRenderSetup( void );
        
        static bool Platform_ShouldModifyFogForSkyboxObjects( void );

		// Merge-with-skybox objects. These get rendered with camera-relative positions, making them behave like the rest of the skybox
		
		static Asura_Hashed_List<Asura_ClientEntity_PhysicalObject> s_xSkyboxObjects;

		#ifdef ASURA_TOOLS
		static bool s_bNeedToRegenerateSkyboxObjects;
		#endif
};


#endif//__ASURA_SKYBOX_H__
