#ifndef __LIGHTING__HLSL__
#define __LIGHTING__HLSL__

#include "DiffuseSh.hlsl"

// ----------------------------------------------------------------
float Lambert(float3 vNormal, float3 vToLight, float3 vToEye)
{
    return saturate(dot(vNormal.xyz, vToLight.xyz));
}

// ----------------------------------------------------------------
float HalfLambert(float3 vNormal, float3 vToLight, float3 vToEye)
{
    //return (0.5f + 0.5f * dot(vNormal.xyz, vToLight.xyz));
    return max(dot(vNormal.xyz, vToLight.xyz), 0.0f);
}
// ----------------------------------------------------------------
float OrenNayar(float3 vNormal, float3 vToLight, float3 vToEye, float fRoughness = 0.2f)
{
    float NdotV = dot(vToEye, vNormal);
    float NdotL = dot(vToLight, vNormal);

    float fThetaR = acos(NdotV);
    float fThetaI = acos(NdotL);

    float alpha = max(fThetaR, fThetaI);
    float beta = min(fThetaR, fThetaI);

    float fSigmaSquared = fRoughness*fRoughness;
    float A = 1.0f - 0.5f * fSigmaSquared / (fSigmaSquared + 0.33f);
    float B = 0.45f * fSigmaSquared / (fSigmaSquared + 0.09f);

    float fCosPhiDiff = saturate(dot(normalize(vToEye - vNormal * NdotV), normalize(vToLight - vNormal * NdotV)));

    return NdotL * (A + B * sin(alpha) * tan(beta) * fCosPhiDiff);

}

float Minnaert(float3 vNormal, float3 vToLight, float3 vToEye, int Shininess = 4)
{
    float NdotV = dot(vToEye, vNormal);
    float NdotL = dot(vToLight, vNormal);

    return pow(NdotV*NdotL, Shininess);
}

// ----------------------------------------------------------------
float3 GetDiffuse(float3 vNormal, float3 vLightDir, float3 vLightColor)
{
    return saturate(vLightColor * (dot(vNormal, vLightDir)));
}
// --------------------------------------------------------------
float GetNDFIndex(float2 vTexCoord, uniform sampler2D sNDFSelection)
{
    float fTextureMax = 255.0f;
    float fNDFMax = 31.0f;
    float fNormalizedIndice = tex2D(sNDFSelection,vTexCoord).r;	    // Compris entre 1/fRange et 32/fRange
    //fNormalizedIndice = 4.f/255.f;
    float fIndexInColor = (fNormalizedIndice*fTextureMax)-0.5;		// Compris entre 0 et 31 + 0.5 (pour etre pile sur la tranche)
    fNormalizedIndice = (fIndexInColor)/(fNDFMax+1.0f);				// Compris entre 0 et 1

    return fNormalizedIndice;
}
// ----------------------------------------------------------------
float4 GetNDFComponent(float3 vHalfway, float3 vTangent, float3 vBinormal, float3 vNormal,
                       float2 vTexCoord, uniform sampler3D sNDFSampler, uniform sampler2D sNDFSelection)
{
    float4 NDFColor = 0;
    float2 NDFTexCoord = 0;
    float fNDFIndex = GetNDFIndex(vTexCoord, sNDFSelection);

    float3 vNDFHalfway = vHalfway;

#if NDFMethod == 1
    float3 vHalfwayProjectedOnNormal = dot(vHalfway, vNormal) * vNormal;
    vNDFHalfway = vHalfway - vHalfwayProjectedOnNormal;
#endif

    NDFTexCoord.x = (dot(vNDFHalfway, vTangent)  + 1)/2;
    NDFTexCoord.y = (dot(vNDFHalfway, vBinormal) + 1)/2;

    NDFColor = tex3D(sNDFSampler,float3(NDFTexCoord,fNDFIndex));
//    NDFColor.rgb = float3(NDFTexCoord,fNDFIndex);

    return NDFColor;

}
// ----------------------------------------------------------------
float4 GetSpecularNDF(float3 vHalfway, float3 vNormal, float3 vTangent, float3 vBinormal, 
                      float2 vTexCoord, uniform float3 vLightSpecColor,
                      uniform sampler3D sNDFSampler, uniform sampler2D sNDFSelection)
{
    // On calcule la couleur de NDF
    float4 vNDFColor = GetNDFComponent(vHalfway, vTangent, vBinormal, vNormal, vTexCoord, sNDFSampler, sNDFSelection);
    return float4(vNDFColor.rgb * vLightSpecColor, vNDFColor.a);
}
// ----------------------------------------------------------------
float GetShadow(float4 vCS_Pos, uniform sampler2D DepthMap, uniform float fOpacity = 0.15f)
{
    float fShadow = 1.f;
#if Shadows && !IgnoreShadows
    vCS_Pos.z = clamp(vCS_Pos.z, 0, vCS_Pos.w);
    float4 vDepthMap = tex2Dproj(DepthMap, vCS_Pos);


    #if DepthRead
        fShadow = (vDepthMap.r * vCS_Pos.w >= vCS_Pos.z);
    #else
        fShadow = vDepthMap.r;
    #endif
#endif
    return saturate(fShadow+fOpacity);
}
// ----------------------------------------------------------------
float4 GetTexture(uniform sampler s, float2 vTexCoord, uniform const bool b, uniform float4 vDefaultValue = float4(1,1,1,1))
{
    if (b)
    {
        return tex2D(s, vTexCoord);
    }
    return vDefaultValue;
}
float4x4 LightViewProj 
#if SceneShadowMap
: GLOBAL 
#endif
< bool hidden = true; >;

// ----------------------------------------------------------------
float4 ProjectToLightSpace(float3 vWS_Pos, uniform float fShadowBias = 0.0)
{
	float4 vRet = float4(vWS_Pos,1);
#if Shadows
    vRet = mul(vRet,LightViewProj);

    vRet.xy = float2(.5,-.5)*(vRet.xy) + float2(.5,.5) * vRet.w;

    // On biaise le z: on multiplie par w car on divisera le z par w dans le PS
    vRet.z -= fShadowBias * vRet.w;
#endif
	return vRet;
}

#endif
