//--------------------------------------------------------------------------------------
// Constant Buffer Variables
//--------------------------------------------------------------------------------------
Texture2D lightColorTex :	register( t0 );
Texture2D lightNormal :		register( t1 );
Texture2D lightPos :		register( t2 );
Texture2D lightMaterial :	register( t3 );
Texture2D lightDepth :		register( t4 );
Texture2D lightVSM :		register( t5 );
Texture2D eyeColor :		register( t6 );
Texture2D eyeNormal :		register( t7 );
Texture2D eyePos :			register( t8 );
Texture2D eyeMaterial :		register( t9 );
Texture2D eyeDepth :		register( t10 );

SamplerState samLinear :register( s0 );
SamplerState samPoint :	register( s1 );
SamplerState samVSM :	register( s2 );

cbuffer cbChangeOnRenderTarget: register( b0 )
{
    matrix lightView;
	matrix lightProj;
	float4 lightWorldPos;
	float4 lightColor;
	float4 eyeWorldPos;
};

//--------------------------------------------------------------------------------------
struct VS_INPUT
{
    float2 Pos : POSITION;
    float2 Tex : TEXCOORD0;
};

struct PS_INPUT
{
    float4 Pos : SV_POSITION;
    float2 Tex : TEXCOORD0;
};

bool isInShadowMap(float4 ndc) {
	if(( ndc.x-ndc.w)>0) return false; //  1  0  0  -1
	if((-ndc.x-ndc.w)>0) return false; // -1  0  0  -1
	if(( ndc.y-ndc.w)>0) return false; //  0  1  0  -1
	if((-ndc.y-ndc.w)>0) return false; //  0 -1  0  -1
	if(( ndc.z-ndc.w)>0) return false; //  0  0  1  -1
	if((-ndc.z-ndc.w)>0) return false; //  0  0 -1  -1
	return true;
}

float calcVSMIntensity(float2 moments, float depth) {  
    float E_x2 = moments.y;
    float Ex_2 = moments.x * moments.x;
    float variance = E_x2 - Ex_2;    
    float mD = (moments.x - depth );
    float mD_2 = mD * mD;
    float p = variance / (variance + mD_2 );
    return max( p, depth <= moments.x );
}

static const float fallOffStart = 0.75f;

//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
PS_INPUT VS( VS_INPUT input )
{
    PS_INPUT output = (PS_INPUT)0;
    output.Pos.xy = input.Pos;
	output.Pos.zw = float2(0.0f,1.0f);
    output.Tex = input.Tex;
    
    return output;
}


//--------------------------------------------------------------------------------------
// Pixel Shader
//--------------------------------------------------------------------------------------
float4 PS( PS_INPUT input) : SV_TARGET
{
	float4 pos = eyePos.Sample( samPoint, input.Tex ); pos.w=1.0f;
	float4 norm = eyeNormal.Sample( samPoint, input.Tex );
	float4 color = eyeColor.Sample( samPoint, input.Tex );
	float4 material = eyeMaterial.Sample( samPoint, input.Tex );

	float4 posInSm = mul( lightView, pos );
    posInSm = mul( lightProj, posInSm );

	if(isInShadowMap(posInSm)) {
		
		posInSm.xy /= posInSm.w;
		float2 smtex;

		float intensity = 1.0f;
		float radius = saturate(length(posInSm.xy));
		if(radius > fallOffStart) {
			intensity = 1.0f - (radius - fallOffStart)/(1.0f - fallOffStart);
		}

		smtex.x = (posInSm.x+1.0f)/2.0f;
		smtex.y = (posInSm.y-1.0f)/-2.0f;
		float2 d = lightVSM.Sample( samVSM, smtex ) * intensity;

		intensity = calcVSMIntensity(d,posInSm.z);
		if(intensity>0.0f) { //TODO calc intensity
			
			float4 lightvec = lightWorldPos - pos;
			lightvec = normalize(lightvec);
			//lightvec.x = -lightvec.x; //?
			float4 viewvec = eyeWorldPos - pos;
			viewvec = normalize(viewvec);

			float intensityDiffuse = saturate(dot(lightvec,norm)) * intensity;
			float4 h = viewvec + (lightvec-viewvec)*0.5f;
			h = normalize(h);
			//        reflected color  *   ( Diffuse                         +        Specular        )     
			float4 r = lightColor * color * (intensityDiffuse * material.y + intensityDiffuse * material.z * pow(saturate(dot(norm,h)),material.w) ) ;
			r.w = 0.0f;
			return r;
		}
	}
	return float4(0.0f,0.0f,0.0f,1.0f);
}
