//////////////////////////////////////////////////////////////////////////////
//
// Asura_Beam_System.h
//
// Copyright (c) 2004-2005 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// B E G I N   F I L E   W R A P P E R ///////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef ___ASURA_BEAM_SYSTEM_H___
#define ___ASURA_BEAM_SYSTEM_H___ 1

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

// Engine independent.
#include "ChunkSystem/Asura_Chunk_Loaders.h"
#include "Core/Asura_Handle.h"
#include "Core/Asura_Hash.h"
#include "Core/Asura_Maths.h"
#include "Network/Asura_Guid.h"

#ifdef ASURA_BEAMS_ALL_MODES
#include "Rendering/Asura_RenderStates.h"
#endif

// M A C R O S ///////////////////////////////////////////////////////////////

// You can override this limits in your project's / platform's
// Asura_EngineConfiguration.h.
#ifndef ASURA_MAX_NO_OF_BEAMS
    #define ASURA_MAX_NO_OF_BEAMS (64)
#endif

// file name of texture (or tiled-texture file) to use. Define in engine configuration to override this.
#ifndef ASURA_BEAM_SYSTEM_TILED_TEXTURE_FILENAME
    #define ASURA_BEAM_SYSTEM_TILED_TEXTURE_FILENAME "graphics\\beams." ASURA_STANDARD_FILE_EXTENSION
#endif

// F O R W A R D   D E C L A R A T I O N S ///////////////////////////////////

class Asura_Beam_System;
class Asura_Message;
class Asura_RGBA;

// C L A S S E S /////////////////////////////////////////////////////////////

// (MP - 09.08.2005) Ideally we would just use Asura_RGBA here but then VC6
// doesn't regard Asura_Beam_Template as an aggregate type anymore.
class Asura_Beam_Colour
{

public:

    void SetColours( float fR, float fG, float fB, float fA ) 
    {
        m_afChannels[0] = fR;
        m_afChannels[1] = fG;
        m_afChannels[2] = fB;
        m_afChannels[3] = fA;
    }

    float m_afChannels[4]; // R,G,B,A

};

class Asura_Beam_Template
{
public: 
    float             fInitialRadius;   
    float             fEndRadius;
    float             fTailSpeed;
    float             fHeadSpeed;
    float             fDecayRate;
    Asura_Hash_ID     uTileHashID;
    Asura_Handle      iTileHandle;        // Setup after initialisation.
    Asura_Beam_Colour xInitialTailColour;
    Asura_Beam_Colour xInitialHeadColour;
    Asura_Beam_Colour xEndTailColour;
    Asura_Beam_Colour xEndHeadColour;
};

// Note use of tail/head to represent positions with initial/end to represent
// time.
class Asura_Beam
{

public:

	bool IsInvisible() const { return m_bInvisible; }
    void SetInvisible( bool bInvisible ) { m_bInvisible = bInvisible; }
	void SetDontUpdateUntilRenderedOnce( bool bDontUpdateUntilRendered ) { m_bDontUpdateUntilRenderedOnce = bDontUpdateUntilRendered; }
	bool GetDontUpdateUntilRenderedOnce( void ) { return m_bDontUpdateUntilRenderedOnce; }

    void SetPositions( const Asura_Vector_3& xTailPos, const Asura_Vector_3& xHeadPos );
    void SetPositions( const Asura_Vector_3& xTailPos, const Asura_Vector_3& xDir, float fLength );
    void SetLength( float fLength ) { Asura_Assert(fLength >= 0.0f, ""); m_fLength = fLength; }

    void SetTailRadiusScale( float fScale ) { m_fTailRadiusScale = fScale; }
    void SetHeadRadiusScale( float fScale ) { m_fHeadRadiusScale = fScale; }
    void SetSpeedScale( float fScale ) { m_fSpeedScale = fScale; }
    void SetTailAlphaScale( float fScale ) { m_fTailAlphaScale = fScale; }
    void SetHeadAlphaScale( float fScale ) { m_fHeadAlphaScale = fScale; }
    void SetTemplate( const Asura_Beam_Template* pxTemplate ) { m_pxTemplate = pxTemplate; }

    void SetLifeTime( float fLifeTime ) { Asura_Assert(fLifeTime >= 0.0f, ""); m_fLifeTime = fLifeTime; }
    void SetDecayRate( float fDecayRate ) { Asura_Assert(fDecayRate >= 0.0f, ""); m_fDecayRate = fDecayRate; }
    void SetTimesliceOverride( float fOverride ) { Asura_Assert(fOverride >= 0.0f, ""); m_fTimesliceOverride = fOverride; }

#ifdef ASURA_BEAMS_ALL_MODES
    void SetTranslucencyMode( ASURA_TRANSLUCENCY_MODE eMode ) { m_eTranslucencyMode = eMode; }
#endif
    
    void CalcCurColours( Asura_RGBA* pxTail, Asura_RGBA* pxHead ) const;
    void CalcCurRadii( float* pfTail, float* pfHead ) const;
    Asura_Vector_3 CalcCurHeadPos( void ) const;

    const Asura_Vector_3& GetDir( void ) const { return m_xDirection; }
    u_int GetID( void ) const { return m_uID; }
	float GetLifeTime( void ) const { return m_fLifeTime; }
	float GetLength( void ) const { return m_fLength; }
    const Asura_Vector_3& GetTailPos( void ) const { return m_xTailPosition; }
    const Asura_Beam_Template* GetTemplate( void ) const { return m_pxTemplate; }

#ifdef ASURA_BEAMS_ALL_MODES
    ASURA_TRANSLUCENCY_MODE GetTranslucencyMode( void ) const { return m_eTranslucencyMode; }
#endif

#ifdef ASURA_BEAMS_SUPPORT_CAMERA_SELECTION
    void SetOnlyRenderIfCameraIgnoresThisGuid( Asura_Guid uGUID ) { m_uOnlyRenderIfCameraIgnoresThisGuid = uGUID; }
    void SetDontRenderIfCameraIgnoresThisGuid( Asura_Guid uGUID ) { m_uDontRenderIfCameraIgnoresThisGuid = uGUID; }
#endif

private:

    friend class Asura_Beam_System;

    const Asura_Beam_Template* m_pxTemplate;
    u_int m_uID;

    float m_fTailRadiusScale;
    float m_fHeadRadiusScale;
    float m_fSpeedScale;
    float m_fTailAlphaScale;
    float m_fHeadAlphaScale;

    float m_fLifeTime;  // Beam will always start at 1 and disappear after this time reaches zero.
    float m_fDecayRate; // Fraction of time to die
    float m_fTimesliceOverride;//Overide for update time for this beam (for cutscene control). RAR 20/03/03

#ifdef ASURA_BEAMS_ALL_MODES
    ASURA_TRANSLUCENCY_MODE m_eTranslucencyMode;
#endif
    
    // Used to have UVs stored here as well. They are now calculated when
    // required as follows:
    //
    // u0 = pxTileInfo->m_fu1;
    // v0 = pxTileInfo->m_fv1;
    //
    // u1 = pxTileInfo->m_fu1;
    // v1 = 0.5f * (pxTileInfo->m_fv1 + pxTileInfo->m_fv2);
    //
    // u2 = 0.5f * (pxTileInfo->m_fu1 + pxTileInfo->m_fu2);
    // v2 = pxTileInfo->m_fv1;
    //
    // u3 = 0.5f * (pxTileInfo->m_fu1 + pxTileInfo->m_fu2);
    // v3 = pxTileInfo->m_fv2;
    //
    // u4 = pxTileInfo->m_fu2;
    // v4 = 0.5f * (pxTileInfo->m_fv1 + pxTileInfo->m_fv2);
    //
    // u5 = pxTileInfo->m_fu2;
    // v5 = pxTileInfo->m_fv2;

#ifdef ASURA_BEAMS_SUPPORT_CAMERA_SELECTION
    Asura_Guid m_uOnlyRenderIfCameraIgnoresThisGuid;
    Asura_Guid m_uDontRenderIfCameraIgnoresThisGuid;
#endif

    Asura_Vector_3 m_xTailPosition;
    Asura_Vector_3 m_xDirection;
    float          m_fLength;

	union
	{
		struct 
		{
			u_int m_bInvisible : 1,
				  m_bDontUpdateUntilRenderedOnce : 1,
				  _BEAM_FLAGS_UNUSED : 30;
		};

		u_int m_uBeamFlags;
	};  
};

class Asura_Msg_Create_Beam // ASURA_MSG_CREATE_BEAM
{

public:

    Asura_Vector_3      m_xInitialTailPos;
    Asura_Vector_3      m_xInitialHeadPos;
    Asura_Beam_Template m_xTemplate;
    float               m_fDecayRate;
    
};

class Asura_Beam_System
{

public:

    enum { uINVALID_BEAM_ID = 0 };

    static void Initialise();
    static void Shutdown();
    static void Reset();
    static void SetupTemplates( Asura_Beam_Template* pxTemplates, u_int uCount );
	static void ResetTemplates( Asura_Beam_Template* pxTemplates, u_int uCount );

    static void Render();
    static void Update();

    // the difference between the camera LOS and the beam direction
    // at angles less than this the special end effect rendering stuff will be used
    static void SetEndEffectAngleTolerance( const float fAngleTolerance ) { s_fEndEffectAngleTolerance = fAngleTolerance; }

    static void HandleMessage(const Asura_Message* pxMessage);
    static Asura_Beam* CreateBeam( const Asura_Vector_3& xInitialTailPos,
                                   const Asura_Vector_3& xInitialHeadPos,
                                   const Asura_Beam_Template& xTemplate );
    
    static void GlobalCreate( const Asura_Vector_3& xInitialTailPos,
                              const Asura_Vector_3& xInitialHeadPos,
                              const Asura_Beam_Template& xTemplate,
                              Asura_Guid uExceptForPlayerGuid,
                              float fDecayRate = 0.0f );

    //Deallocate using an ID. This deallocate can be used when it is unknown whether the
    //beam still exists. RAR 20/03/03
    static void DeallocateBeam(u_int uBeamID);
    static Asura_Beam* AllocateBeam();

    //Overides the timeslice for a beam for a frame (for cutscene control) RAR 20/03/03
    static void SetTimesliceForBeam(u_int uBeamID,float fTimeSlice);
    
    static u_int GetNumActiveBeams( void ) { return s_uNumActiveBeams; }
    static Asura_Beam* GetBeamByID( u_int uID );

private:

    static void Project_Initialise( void );
	static void Project_Shutdown( void );
	static void Project_Reset( void );

    static void DeallocateBeamByPtrArrayIdx( u_int uPtrArrayIdx );
    static int  GetBeamArrayPtrIdxByID( u_int uID );

    static void Platform_Initialise();
	static void Platform_Shutdown();

	static void Platform_RenderStart(void);
    static void Platform_RenderEnd(void);
    static void Platform_Render(void);

    static Asura_Beam  s_axBeams[ASURA_MAX_NO_OF_BEAMS];
    static Asura_Beam* s_apxBeamPtrs[ASURA_MAX_NO_OF_BEAMS];
    static u_int       s_uNumActiveBeams;
    static u_int       s_uNextBeamID;
    static float       s_fEndEffectAngleTolerance;
    static int         s_iTiledTextureTextureID;

};

// E N D   F I L E   W R A P P E R ///////////////////////////////////////////

#endif // ___ASURA_BEAM_SYSTEM_H___
