//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================

#include "cbase.h"
#include "shareddefs.h"
#include "materialsystem/imesh.h"
#include "materialsystem/imaterial.h"
#include "view.h"
#include "iviewrender.h"
#include "view_shared.h"
#include "texture_group_names.h"
#include "tier0/icommandline.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

static ConVar mat_slopescaledepthbias_shadowmap( "mat_slopescaledepthbias_shadowmap", "16", FCVAR_CHEAT );
static ConVar mat_depthbias_shadowmap(  "mat_depthbias_shadowmap", "0.0005", FCVAR_CHEAT  );

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class C_EnvProjectedTexture : public C_BaseEntity
{
    DECLARE_CLASS( C_EnvProjectedTexture, C_BaseEntity );
public:
    DECLARE_CLIENTCLASS();

    virtual void OnDataChanged( DataUpdateType_t updateType );
    void    ShutDownLightHandle( void );

    virtual void Simulate();

    void    UpdateLight( bool bForceUpdate );

    C_EnvProjectedTexture();
    ~C_EnvProjectedTexture();

private:

    ClientShadowHandle_t m_LightHandle;

    EHANDLE m_hTargetEntity;

    bool    m_bState;
    float   m_flLightFOV;
    bool    m_bEnableShadows;
    bool    m_bLightOnlyTarget;
    bool    m_bLightWorld;
    bool    m_bCameraSpace;
    Vector  m_LinearFloatLightColor;
    float   m_flAmbient;
    float   m_flNearZ;
    float   m_flFarZ;
    char    m_SpotlightTextureName[ MAX_PATH ];
    int     m_nSpotlightTextureFrame;
    int     m_nShadowQuality;
};

IMPLEMENT_CLIENTCLASS_DT( C_EnvProjectedTexture, DT_EnvProjectedTexture, CEnvProjectedTexture )
    RecvPropEHandle( RECVINFO( m_hTargetEntity )    ),
    RecvPropBool(    RECVINFO( m_bState )           ),
    RecvPropFloat(   RECVINFO( m_flLightFOV )       ),
    RecvPropBool(    RECVINFO( m_bEnableShadows )   ),
    RecvPropBool(    RECVINFO( m_bLightOnlyTarget ) ),
    RecvPropBool(    RECVINFO( m_bLightWorld )      ),
    RecvPropBool(    RECVINFO( m_bCameraSpace )     ),
    RecvPropVector(  RECVINFO( m_LinearFloatLightColor )        ),
    RecvPropFloat(   RECVINFO( m_flAmbient )        ),
    RecvPropString(  RECVINFO( m_SpotlightTextureName ) ),
    RecvPropInt(     RECVINFO( m_nSpotlightTextureFrame ) ),
    RecvPropFloat(   RECVINFO( m_flNearZ )  ),
    RecvPropFloat(   RECVINFO( m_flFarZ )   ),
    RecvPropInt(     RECVINFO( m_nShadowQuality )   ),
END_RECV_TABLE()

C_EnvProjectedTexture::C_EnvProjectedTexture( void )
{
    m_LightHandle = CLIENTSHADOW_INVALID_HANDLE;
}

C_EnvProjectedTexture::~C_EnvProjectedTexture( void )
{
    ShutDownLightHandle();
}

void C_EnvProjectedTexture::ShutDownLightHandle( void )
{
    // Clear out the light
    if( m_LightHandle != CLIENTSHADOW_INVALID_HANDLE )
    {
        g_pClientShadowMgr->DestroyFlashlight( m_LightHandle );
        m_LightHandle = CLIENTSHADOW_INVALID_HANDLE;
    }
}

//-----------------------------------------------------------------------------
// Purpose:
// Input  : updateType -
//-----------------------------------------------------------------------------
void C_EnvProjectedTexture::OnDataChanged( DataUpdateType_t updateType )
{
    UpdateLight( true );
    BaseClass::OnDataChanged( updateType );
}

void C_EnvProjectedTexture::UpdateLight( bool bForceUpdate )
{
    if ( m_bState == false )
    {
        if ( m_LightHandle != CLIENTSHADOW_INVALID_HANDLE )
        {
            ShutDownLightHandle();
        }

        return;
    }

    Vector vForward, vRight, vUp, vPos = GetAbsOrigin();
    FlashlightState_t state;

    if ( m_hTargetEntity != NULL )
    {
        if ( m_bCameraSpace )
        {
            const QAngle &angles = GetLocalAngles();

            C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
            if( pPlayer )
            {
                const QAngle playerAngles = pPlayer->GetAbsAngles();

                Vector vPlayerForward, vPlayerRight, vPlayerUp;
                AngleVectors( playerAngles, &vPlayerForward, &vPlayerRight, &vPlayerUp );

                matrix3x4_t mRotMatrix;
                AngleMatrix( angles, mRotMatrix );

                VectorITransform( vPlayerForward, mRotMatrix, vForward );
                VectorITransform( vPlayerRight, mRotMatrix, vRight );
                VectorITransform( vPlayerUp, mRotMatrix, vUp );

                float dist = (m_hTargetEntity->GetAbsOrigin() - GetAbsOrigin()).Length();
                vPos = m_hTargetEntity->GetAbsOrigin() - vForward*dist;

                VectorNormalize( vForward );
                VectorNormalize( vRight );
                VectorNormalize( vUp );
            }
        }
        else
        {
            // VXP: Fixing targeting
            Vector vecToTarget;
            QAngle vecAngles;
            if( m_hTargetEntity == NULL ) {
                vecAngles = GetAbsAngles();
            }
            else {
                vecToTarget = m_hTargetEntity->GetAbsOrigin() - GetAbsOrigin();
                VectorAngles( vecToTarget, vecAngles );
            }
            AngleVectors( vecAngles, &vForward, &vRight, &vUp );
        }
    }
    else
    {
        AngleVectors( GetAbsAngles(), &vForward, &vRight, &vUp );
    }

    state.m_fHorizontalFOVDegrees = m_flLightFOV;
    state.m_fVerticalFOVDegrees = m_flLightFOV;

    state.m_vecLightOrigin = vPos;
    BasisToQuaternion( vForward, vRight, vUp, state.m_quatOrientation );

    state.m_fQuadraticAtten = 0.0;
    state.m_fLinearAtten = 100;
    state.m_fConstantAtten = 0.0f;
    state.m_Color[0] = m_LinearFloatLightColor.x;
    state.m_Color[1] = m_LinearFloatLightColor.y;
    state.m_Color[2] = m_LinearFloatLightColor.z;
    state.m_Color[3] = 0.0f; // fixme: need to make ambient work m_flAmbient;
    state.m_NearZ = m_flNearZ;
    state.m_FarZ = m_flFarZ;
    state.m_flShadowSlopeScaleDepthBias = mat_slopescaledepthbias_shadowmap.GetFloat();
    state.m_flShadowDepthBias = mat_depthbias_shadowmap.GetFloat();
    state.m_bEnableShadows = m_bEnableShadows;
    state.m_pSpotlightTexture = materials->FindTexture( m_SpotlightTextureName, TEXTURE_GROUP_OTHER, false );
    state.m_nSpotlightTextureFrame = m_nSpotlightTextureFrame;

    state.m_nShadowQuality = m_nShadowQuality; // Allow entity to affect shadow quality

    if( m_LightHandle == CLIENTSHADOW_INVALID_HANDLE )
    {
        m_LightHandle = g_pClientShadowMgr->CreateFlashlight( state );
    }
    else
    {
        if ( m_hTargetEntity != NULL || bForceUpdate == true )
        {
            g_pClientShadowMgr->UpdateFlashlightState( m_LightHandle, state );
        }
    }

    if( m_bLightOnlyTarget )
    {
        g_pClientShadowMgr->SetFlashlightTarget( m_LightHandle, m_hTargetEntity );
    }
    else
    {
        g_pClientShadowMgr->SetFlashlightTarget( m_LightHandle, NULL );
    }

    g_pClientShadowMgr->SetFlashlightLightWorld( m_LightHandle, m_bLightWorld );
    g_pClientShadowMgr->UpdateProjectedTexture( m_LightHandle, true );
}

void C_EnvProjectedTexture::Simulate( void )
{
    UpdateLight( !!GetMoveParent() );

    BaseClass::Simulate();
}

