#ifndef __SHADOW__HLSL__
#define __SHADOW__HLSL__

#include "Uniforms.hlsl"

#ifndef ENABLE_SHADOW 
#define ENABLE_SHADOW 0
#endif

#ifndef ENABLE_HARDWARE_PCF 
#define ENABLE_HARDWARE_PCF 0
#endif

#ifndef SHADOWPASS 
#define SHADOWPASS 0
#endif

#if ENABLE_SHADOW==0
#undef SHADOWPASS
#undef ENABLE_HARDWARE_PCF
#define SHADOWPASS 0
#define ENABLE_HARDWARE_PCF 0
#endif

const float4x4      mLightMatrix[3]             : GLOBAL;
const float4        SplitDepthShadowIntensity   : GLOBAL;
const float4        CloudsParams                : GLOBAL; //Direction multiply by speed , scale, clouds intensity
const float2        ShadowFade                  : GLOBAL; //Start fade, inverse length fade
const float         LandscapeWidth              : GLOBAL;

/*******************************************************************************************************
*
*******************************************************************************************************/
texture tShadowMap
<
    string NTM = "Shader";
    int NTMIndex = 12;
>;
texture tCloudsMap1
<
    string NTM = "Shader";
    int NTMIndex = 13;
>;
texture tCloudsMap2
<
    string NTM = "Shader";
    int NTMIndex = 14;
>;
texture tStaticShadowMap
<
    string NTM = "Shader";
    int NTMIndex = 15;
>;

/*******************************************************************************************************
*
*******************************************************************************************************/
sampler sShadowMapSampler = sampler_state
{
    Texture = (tShadowMap);
    MAGFILTER = LINEAR;
    MINFILTER = LINEAR;
    MIPFILTER = LINEAR;
    ADDRESSU = WRAP;
    ADDRESSV = WRAP;
};

sampler sCloudsMapSampler1 = sampler_state
{
    Texture = (tCloudsMap1);
    MAGFILTER = LINEAR;
    MINFILTER = LINEAR;
    MIPFILTER = LINEAR;
    ADDRESSU = WRAP;
    ADDRESSV = WRAP;
};

sampler sCloudsMapSampler2 = sampler_state
{
    Texture = (tCloudsMap2);
    MAGFILTER = LINEAR;
    MINFILTER = LINEAR;
    MIPFILTER = LINEAR;
    ADDRESSU = WRAP;
    ADDRESSV = WRAP;
};

sampler sStaticShadowMapSampler = sampler_state
{
    Texture = (tStaticShadowMap);
    MAGFILTER = LINEAR;
    MINFILTER = LINEAR;
    MIPFILTER = LINEAR;
    ADDRESSU = CLAMP;
    ADDRESSV = CLAMP;
};

/*******************************************************************************************************
*
*******************************************************************************************************/
float ComputeClouds(float3 vWSVertex)
{        
    float2 vTextureCoords = vWSVertex.xy * CloudsParams.zz - CloudsParams.xy;
    float fClouds = lerp(
        tex2D(sCloudsMapSampler1, vTextureCoords).r,
        tex2D(sCloudsMapSampler2, vTextureCoords).r,
        BlendWeatherValue);
    
    return fClouds;
}

/*******************************************************************************************************
*
*******************************************************************************************************/
float ComputeDynamicShadow(float3 vWSVertex, float fWSDistance)
{	
#if ENABLE_SHADOW    
    float3  vGreater    = (SplitDepthShadowIntensity.xyz < fWSDistance);
    int     iMapIndex   = dot(vGreater.xyz, float3(1.0f, 1.0f, 1.0f));
    float4  vShadowTex  = float4(0.0f,0.0f,0.0f,1.0f);
    if(iMapIndex<3) vShadowTex = mul(float4(vWSVertex,1.0f), mLightMatrix[iMapIndex]);

    float   fShadowDepth    = tex2Dproj(sShadowMapSampler, vShadowTex).r;
    float   fFrameDepth     = saturate(vShadowTex.z / vShadowTex.w);

    float fOverDarkeningFactor = 8192.0f;
	float fShadow = saturate( exp( fOverDarkeningFactor * ( fShadowDepth - fFrameDepth ) ) ); 

    float fIntensity = SplitDepthShadowIntensity.w;
    fShadow = lerp(fShadow,1.0f,clamp((fWSDistance-ShadowFade.x)*ShadowFade.y,0.0f,1.0f));
    
    /*if(iMapIndex<1) return 0.0f;
    else if(iMapIndex<2) return 1.0f;
    else return 1.0f;*/
    /*if(iMapIndex<1) return vShadowTex.y<0.0f ? 1.0 : 0.5;
    else return 0.0f;*/

    return  fShadow*fIntensity+(1.0f-fIntensity);
#else
    return  1.0f;
#endif
}

/*******************************************************************************************************
*
*******************************************************************************************************/
float ComputeShadow(float3 vWSVertex, float fWSDistance)
{	
#if ENABLE_SHADOW    
    float fDynamicShadow = ComputeDynamicShadow(vWSVertex,fWSDistance);
    float fClouds = ComputeClouds(vWSVertex);

    float fStaticShadow = tex2D(sStaticShadowMapSampler,vWSVertex.xy/LandscapeWidth).r;
    float fIntensity = SplitDepthShadowIntensity.w;
    
    float fStaticShadowFade = lerp(fStaticShadow,1.0f,saturate((ShadowFade.x-fWSDistance)*ShadowFade.y));
    fStaticShadowFade = lerp(fStaticShadow,fStaticShadowFade,fIntensity);
    return  min(min(fClouds,fDynamicShadow),fStaticShadowFade);
#else
    return  1.0f;
#endif
}

/*******************************************************************************************************
*
*******************************************************************************************************/
float ComputeShadowWithOcclusion(float3 vWSVertex, float fWSDistance, float fOcclusion )
{	
#if ENABLE_SHADOW    
    float fDynamicShadow = ComputeDynamicShadow(vWSVertex,fWSDistance);
    float fClouds = ComputeClouds(vWSVertex);

    float2 fStaticShadowAndOcclusion = tex2D(sStaticShadowMapSampler,vWSVertex.xy/LandscapeWidth).rg;
    float fMinStaticShadowAndOcclusion = min(fStaticShadowAndOcclusion.r, lerp(1.0, fStaticShadowAndOcclusion.g, fOcclusion));
    float fIntensity = SplitDepthShadowIntensity.w;
    //fMinStaticShadowAndOcclusion = fMinStaticShadowAndOcclusion*fIntensity+(1.0f-fIntensity);
    
    float fMinStaticShadowAndOcclusionFade = lerp(fMinStaticShadowAndOcclusion,1.0f,saturate((ShadowFade.x-fWSDistance)*ShadowFade.y));
    fMinStaticShadowAndOcclusionFade = lerp(fMinStaticShadowAndOcclusion,fMinStaticShadowAndOcclusionFade,fIntensity);
    return  min(min(fClouds,fDynamicShadow),fMinStaticShadowAndOcclusionFade);
#else
    return  1.0f;
#endif
}

/*******************************************************************************************************
*
*******************************************************************************************************/
float ComputeStandardLightingWithOcclusion(float fNDotL, float3 vWSVertex, float fWSDistance, float fOcclusion)
{
#if ENABLE_SHADOW
    float fShadow = ComputeShadowWithOcclusion(vWSVertex, fWSDistance,fOcclusion);
    fNDotL = max(fNDotL, 0.0f);
    return min(fNDotL, fNDotL*fShadow);
#else
    return max(fNDotL, 0.0f);
#endif
}
float ComputeStandardLighting(float fNDotL, float3 vWSVertex, float fWSDistance)
{
#if ENABLE_SHADOW
    float fShadow = ComputeShadow(vWSVertex, fWSDistance);
    fNDotL = max(fNDotL, 0.0f);
    return min(fNDotL, fNDotL*fShadow);
#else
    return max(fNDotL, 0.0f);
#endif
}
float ComputeStandardLightingWithoutShadow(float fNDotL, float3 vWSVertex, float fWSDistance)
{
    return max(fNDotL, 0.0f);
}

/*******************************************************************************************************
*
*******************************************************************************************************/
float ComputeNoHalfLambertLightingWithOcclusion(float fNDotL, float3 vWSVertex, float fWSDistance, float fOcclusion)
{
#if ENABLE_SHADOW
    return min(fNDotL, fNDotL*ComputeShadowWithOcclusion(vWSVertex,fWSDistance,fOcclusion));
#else
    return fNDotL;
#endif
}
float ComputeNoHalfLambertLighting(float fNDotL, float3 vWSVertex, float fWSDistance)
{
#if ENABLE_SHADOW
    return min(fNDotL, fNDotL*ComputeShadow(vWSVertex,fWSDistance));
#else
    return fNDotL;
#endif
}
float ComputeNoHalfLambertLightingWithoutShadow(float fNDotL, float3 vWSVertex, float fWSDistance)
{
    return fNDotL;
}

#endif
