//
// Shadowmapping functions
// (c) 2009-2010 Cruden B.V.
//

#ifdef CSM
#include "../common/constants.cg"

//
// Shadow mapping coord check (if the pixel is in range, its valid for this texture)
//
bool CheckCoords(float2 shadowCoord)
// Returns true if the shadow coordinates are within the projected area
{
  return (shadowCoord.x >= 0.001f &&
          shadowCoord.x <= 0.999f &&
          shadowCoord.y >= 0.001f &&
          shadowCoord.y <= 0.999f);
          // && shadowCoord.z <  1.0f );
}

float ProcessShadowNoBlur(float4 shadowCoord, sampler2D shadowTex, float z, int smSplits, const int index,const float corFactor)
// Get amount of shadow - no blur version
{
  float b;
  
  //if(index==0)return 0.2f; else if(index==1)return 0.9f; else if(index==2)return 0.2; else return 0.9f;    // DEV
  
  // Find coordinates (shadow texture atlas)
  if(smSplits>1)
	{
		shadowCoord.x*=0.5;
		shadowCoord.x+=0.5*(index&1);
	}
  
	if(smSplits>2)
	{
    shadowCoord.y*=0.5;
    shadowCoord.y+=0.5*(index/2);
  }  

#ifdef ATI
  return (tex2D(shadowTex, clamp(shadowCoord.xy + (float2( 0, 0)/1024.0),0.0,1.0)).r+(smCor[index]*corFactor))<z?0.0f:1.0f;;
#else
  // nVidia etc
  return tex2D(shadowTex, float3(shadowCoord.xy,z-smCor[index]*corFactor)).x;
#endif  
}

float ProcessShadowBlur(float4 shadowCoord, sampler2D shadowTex, float z, int smSplits, int index,float corFactor)
// Blurring version
{
  //if(index==0)return 0.2f; else if(index==1)return 0.9f; else return 0.0f;    // DEV

  // find coordinates (texture atlas)
  float2 cormin=float2(0.0f,0.0f);
  float2 cormax=float2(1.0f,1.0f);

  if(smSplits>1)
	{
		shadowCoord.x*=1.0-0.5; //0.5f;
		shadowCoord.x+=0.5*(index%2);
	
		cormin.x=(0.5f/1024.0f+(index%2)*0.5f);
		//cormax.x=1.0-b*1.0+b*(0.5f*1023.0f/1024.0f+(index%2)*0.5f);
		cormax.x=0.5f*1023.0f/1024.0f+(index%2)*0.5f;
	}
  
  // Remove 'if' here?
	if(smSplits>2)
	{
    //shadowCoord.y*=(0.5f-10/mapdistance[index]*0.001*filterRadiusUV*1024);
    shadowCoord.y*=0.5f;
    shadowCoord.y+=(index/2)*0.5f;
	
    cormin.y=0.5f/1024.0f+(index/2)*0.5f;
    cormax.y=0.5f*1023.0f/1024.0f+(index/2)*0.5f;
  }  

#ifndef ATI  

  //-----------------------------------------------PCF with edge tap blend
  
  float fShadowTerm = 0.0f; 
  float2 g_vShadowMapSize = float2(shadowMapSize*2,shadowMapSize*2);
  //int iSqrtSamples = max(4-index,2);
  int iSqrtSamples=max(iSqrtSamples-index,2);
  
  int   trsSamples = iSqrtSamples-(iSqrtSamples&1);
  float fRadius = (trsSamples)*0.5;
  float fSample;
  float x,y;
  float3 samplePoint;
  float  dx;

  dx=1.0/shadowMapSize;
  for(y=-fRadius; y<=fRadius; y+=1.0)
  {
    samplePoint=float3(shadowCoord.x-fRadius*dx,shadowCoord.y+y/shadowMapSize,z-(smCor[index]*corFactor));
    for (x = -fRadius; x <= fRadius; x+=1.0)
    {
      // Free PCF filter?
      float fSample = tex2D(shadowTex,samplePoint);
      fShadowTerm += fSample;

      samplePoint.x+=dx;
      
      // Edge tap smoothing
      /*
               // Manual PCF
      float xWeight = 1;
      float yWeight = 1;
			if (x == -fRadius)
                xWeight = 1 - frac(shadowCoord.x * g_vShadowMapSize.x);
            else if (x == fRadius)
                xWeight = frac(shadowCoord.x * g_vShadowMapSize.x);

	     if (y == -fRadius)
                yWeight = 1 - frac(shadowCoord.y * g_vShadowMapSize.y);
            else if (y == fRadius)
                yWeight = frac(shadowCoord.y * g_vShadowMapSize.y);
            fShadowTerm += (fSample * xWeight * yWeight);
			*/
	
      //fShadowTerm+= max(fShadowTerm,fSample);
    }                                            
  }        

  fShadowTerm /= ((trsSamples+1)* (trsSamples+1));
  return fShadowTerm;
  
#else
  
  // ATI
  float fShadowTerm = 0.0f; 
  
  // ATI can't handle these extra 8 lines *sigh*
  //fShadowTerm += (tex2D(shadowTex, clamp(shadowCoord.xy + (float2(-1,-1)/1024.0),0.0,1.0)).r+smCor[index])<z?0.0f:0.4f;
  //fShadowTerm += (tex2D(shadowTex, clamp(shadowCoord.xy + (float2(-1, 0)/1024.0),0.0,1.0)).r+smCor[index])<z?0.0f:0.6f;
  //fShadowTerm += (tex2D(shadowTex, clamp(shadowCoord.xy + (float2(-1, 1)/1024.0),0.0,1.0)).r+smCor[index])<z?0.0f:0.4f;
  //fShadowTerm += (tex2D(shadowTex, clamp(shadowCoord.xy + (float2( 0,-1)/1024.0),0.0,1.0)).r+smCor[index])<z?0.0f:0.4f;
  fShadowTerm += (tex2D(shadowTex, clamp(shadowCoord.xy + (float2( 0, 0)/1024.0),0.0,1.0)).r+smCor[index]*corFactor)<z?0.0f:1.0f;
  //fShadowTerm += (tex2D(shadowTex, clamp(shadowCoord.xy + (float2( 0, 1)/1024.0),0.0,1.0)).r+smCor[index])<z?0.0f:0.4f;
  //fShadowTerm += (tex2D(shadowTex, clamp(shadowCoord.xy + (float2( 1,-1)/1024.0),0.0,1.0)).r+smCor[index])<z?0.0f:0.4f;  
  //fShadowTerm += (tex2D(shadowTex, shadowCoord.xy + (float2( 1, 0)/2048.0)).r+smCor[index])<z?0.0f:0.6f;
  //fShadowTerm += (tex2D(shadowTex, shadowCoord.xy + (float2( 1, 1)/2048.0)).r+smCor[index])<z?0.0f:0.4f;

  return fShadowTerm;
#endif  // ATI

}

bool DoShadowFactorLoop(
  int index,
  float3 Position, 
  float3 normal, 
  sampler2D shadowTex, 
  float4x4 smTexMatArray[SM_MAX_SPLITS],
  float       smSplits,
  float corFactor,
  out float4 shadowCoord,
  out float shadowTerm
)
// If inside the shadow split #'index', calculate amount of shadow
{
  shadowCoord=mul( smTexMatArray[index], float4(Position.xyz,1));
  shadowCoord.xyz /= shadowCoord.w;
  if(CheckCoords(shadowCoord))
  {
#ifdef CSM_BLUR
    // Only blur first split
    if(index>1)
      shadowTerm=ProcessShadowNoBlur(shadowCoord, shadowTex, shadowCoord.z, smSplits, index,corFactor);
    else
      shadowTerm=ProcessShadowBlur(shadowCoord, shadowTex, shadowCoord.z, smSplits, index,corFactor);
#else
    shadowTerm=ProcessShadowNoBlur(shadowCoord, shadowTex, shadowCoord.z, smSplits, index,corFactor);
#endif
    return true;
  }
  return false;
}

float BlendSplits(float2 shadowCoord,float shadowTerm,float prevShadowTerm)
// Blends 2 shadowterms from 2 different splits
{
  shadowCoord.y=min(1.0f,(0.5f-abs(shadowCoord.y-0.5f))*5.0f); // 0.1f
  shadowCoord.x=min(1.0f,(0.5f-abs(shadowCoord.x-0.5f))*5.0f); // 0.1f
  shadowCoord.x=min(shadowCoord.x,shadowCoord.y);
  //shadowCoord.x = 1.0-max(1.0,length(float2(shadowCoord.x,shadowCoord.y)));

  return lerp(shadowTerm,prevShadowTerm, shadowCoord.x);    
}
void DoShadowFactorBlend(int index,
                        float3 Position, 
                        float3 normal, 
                        sampler2D shadowTex, 
                        float4x4 smTexMatArray[SM_MAX_SPLITS],
                        float     smSplits,
                        float2    prevShadowCoord,
                        float     prevShadowTerm,
                        float 	  corFactor,
                        out float shadowTerm)

{
  float4  shadowCoord;
  float   z;
  
  if(!DoShadowFactorLoop(index,Position,normal,shadowTex,smTexMatArray,smSplits,corFactor,shadowCoord,shadowTerm))
    return;

  // Blend with next split based on position in shadow texture
  shadowCoord.xy=prevShadowCoord.xy;
  shadowTerm=BlendSplits(shadowCoord,shadowTerm,prevShadowTerm);
}

float GetShadowFactorFlexible(float3    Position, 
                      float3    normal, 
                      sampler2D shadowTex, 
                      float4x4  smTexMatArray[SM_MAX_SPLITS],
                      float     smSplits,
                      float3    lightDirection)
// Can handle a user-defined number of splits
{
  float  shadowTerm; //=1.0f;
  float4 shadowCoord=0;
  float  dotAng=dot(normal,lightDirection);
  
  // Check if the sun is behind the pixel (cheap check)
  if(dotAng<-0.1f)
  {
    return 0; //smIntensity;
  }  
  
  // turn into smCor factor - make shadows need less Z correction as they are being lit straight on
  dotAng=1.0f-dotAng;
  
  // Which shadow texture map?
#ifdef ND_DONT_USE
  const int splits=3;
  int i;
  
  for(i=0;i<splits;i++)
  {
    if(DoShadowFactorLoop(i,Position,normal,shadowTex,smTexMatArray,smSplits,dotAng,shadowCoord,shadowTerm))
    {
      if(smSplits<i+2) return shadowTerm;
      DoShadowFactorBlend(i+1,Position,normal,shadowTex,smTexMatArray,smSplits,shadowCoord,shadowTerm,dotAng,shadowTerm);
      return shadowTerm;
    }
  }
  return 1;
#endif
  
  // VERY UGLY
  // ATI does not support FOR loops ...
  // should not be any slower though (it seems faster even, unrolled)
  if(DoShadowFactorLoop(0,Position,normal,shadowTex,smTexMatArray,smSplits,dotAng,shadowCoord,shadowTerm))
  {
    if(smSplits<2) return shadowTerm;
    DoShadowFactorBlend(1,Position,normal,shadowTex,smTexMatArray,smSplits,shadowCoord,shadowTerm,dotAng,shadowTerm);
    return shadowTerm;
  }     

  if(smSplits<2) return 1.0f;
  if(DoShadowFactorLoop(1,Position,normal,shadowTex,smTexMatArray,smSplits,dotAng,shadowCoord,shadowTerm))
  {
    if(smSplits<3) return shadowTerm;
    DoShadowFactorBlend(2,Position,normal,shadowTex,smTexMatArray,smSplits,shadowCoord,shadowTerm,dotAng,shadowTerm);
    return shadowTerm;
  }

  if(smSplits<3) return 1.0f;
  if(DoShadowFactorLoop(2,Position,normal,shadowTex,smTexMatArray,smSplits,dotAng,shadowCoord,shadowTerm))
  {
    if(smSplits<4) return shadowTerm;
    DoShadowFactorBlend(3,Position,normal,shadowTex,smTexMatArray,smSplits,shadowCoord,shadowTerm,dotAng,shadowTerm);
    return shadowTerm;
  }

  if(smSplits<4) return 1.0f;
  if(DoShadowFactorLoop(3,Position,normal,shadowTex,smTexMatArray,smSplits,dotAng,shadowCoord,shadowTerm))
  {
    return shadowTerm;
  }  

  return 1.0f;
}

float GetShadowFactor(float3    Position, 
                      float3    normal, 
                      sampler2D shadowTex, 
                      float4x4  smTexMatArray[SM_MAX_SPLITS],
                      float     smSplits,
                      float3    lightDirection)
// Hardcoded for 4 splits
// Actually the function name is a bit confusing; the return value is 0 for full shadow, 1 for no shadow.
{
  float  shadowTerm; //=1.0f;
  float4 shadowCoord=0;
  float  dotAng=dot(normal,lightDirection);

  // Check if the sun is behind the pixel (cheap check)
  //if(dotAng<0)
  if(dotAng<-0.072f)
  {
    return 0; //smIntensity;
  }  

  //Position+=-lightDirection*0.01;
  
  // turn into smCor factor - make shadows need less Z correction as they are being lit straight on
  //const float pi=3.14159265;
  //dotAng=0.5*pi-acos(dotAng);
  //dotAng*=dotAng; dotAng=1.0f-dotAng;
   
  dotAng=1.0f-dotAng;
  dotAng=max(0.05,pow(dotAng,5));
  
  //dotAng=1.0f-dotAng;
  //dotAng=0;
  //dotAng=1.0f;
  // Light direction dependency
  //dotAng*=4.0*(1.0-lightDirection.y);
  //dotAng*=4.0*(1.0-lightDirection.y);
  //dotAng=0.0f;

  // Which shadow texture map?
  // ATI does not support FOR loops ... should not be any slower though (it seems faster even, unrolled)
  if(DoShadowFactorLoop(0,Position,normal,shadowTex,smTexMatArray,smSplits,dotAng,shadowCoord,shadowTerm))
  {
    //if(smSplits<2) return shadowTerm;
    DoShadowFactorBlend(1,Position,normal,shadowTex,smTexMatArray,smSplits,shadowCoord,shadowTerm,dotAng,shadowTerm);
    return shadowTerm;
  }     

  //if(smSplits<2) return 1.0f;
  if(DoShadowFactorLoop(1,Position,normal,shadowTex,smTexMatArray,smSplits,dotAng,shadowCoord,shadowTerm))
  {
    //if(smSplits<3) return shadowTerm;
    DoShadowFactorBlend(2,Position,normal,shadowTex,smTexMatArray,smSplits,shadowCoord,shadowTerm,dotAng,shadowTerm);
    return shadowTerm;
  }

  //if(smSplits<3) return 1.0f;
  if(DoShadowFactorLoop(2,Position,normal,shadowTex,smTexMatArray,smSplits,dotAng,shadowCoord,shadowTerm))
  {
    //if(smSplits<4) return shadowTerm;
    DoShadowFactorBlend(3,Position,normal,shadowTex,smTexMatArray,smSplits,shadowCoord,shadowTerm,dotAng,shadowTerm);
    return shadowTerm;
  }

  // 4th and last split
  if(DoShadowFactorLoop(3,Position,normal,shadowTex,smTexMatArray,smSplits,dotAng,shadowCoord,shadowTerm))
  {
    // Fade last split
    shadowTerm=BlendSplits(shadowCoord,1,shadowTerm);
//shadowTerm=0;
    return shadowTerm;
  }

  // No shadow
  return 1.0f;
}

#endif // CSM
