#define DIRECTIONAL_SHADOWMAPS_SIZE 16
Texture2D shaderTextures[3];

SamplerState SampleType;
SamplerState ShadowmapSampler;

Texture2DArray shadowmapTextures;
TextureCubeArray pointShadowmapTextures;

struct SPointLight
{
	float4 ambientColor;
	float4 diffuseColor;
	float4 specularColor;
	float3 position;
	float  intensityDecrease;
};

struct SDirectionalLight
{
	float4 ambientColor;
	float4 diffuseColor;
	float4 specularColor;
	float3 direction;
	float  padding;
};

struct SMaterialInfo
{
    float4 ambientColor;
    float4 diffuseColor;
    float4 specularColor;
    float specularExponent;
	uint flags;
	float2 padding;
};

cbuffer LightBuffer : register(ps, cb0)
{
	SDirectionalLight directionalLights[100];
	SPointLight pointLights[100];
	uint directionalLightsCount;
	uint pointLightsCount;
	float2 lightbuffer_padding;
};

cbuffer MaterialBuffer : register(ps, cb1)
{
	SMaterialInfo material;
};

cbuffer Shadowmap
{
	matrix shadowmapView[DIRECTIONAL_SHADOWMAPS_SIZE];
	matrix shadowmapProjection[DIRECTIONAL_SHADOWMAPS_SIZE];
};

cbuffer PointShadowmap
{
	matrix pointShadowmapProjection;
};

struct PixelInputType
{
    float4 position : SV_POSITION;
	float4 worldPosition : POSITION1;
    float2 tex : TEXCOORD0;
    float3 normal : NORMAL;
	float3 tangent : TANGENT;
    float3 binormal : BINORMAL;
	float3 viewDirection : TEXCOORD1;
	int materialId : MATERIALID;
};

float3 offset_lookup(float2 loc, float2 offset, int index)
{
	float2 texmapscale = float2(1.0f/1600.0f, 1.0f/1200.0f);
	return shadowmapTextures.Sample(SampleType, float3(loc + offset * texmapscale, index));
}

float3 point_offset_lookup(float3 loc, float3 offset, int index)
{
	//float3 texmapscale = float2(1.0f/256.0f, 1.0f/256.0f);
	return pointShadowmapTextures.Sample(SampleType, float4(loc /*+ offset * texmapscale*/, index));
}

/*
float samplePCF(float3 tc)
{
  const float4 shadowMapSize = float4(800.0, 1.0 / 800.0, 0.0, 1.0 / 600.0);
  tc.xy -= shadowMapSize.ww;
  float4 shd;
  shd.x = shadowmapTexture.Sample(ShadowmapSampler, tc.xy).x;
  shd.y = shadowmapTexture.Sample(ShadowmapSampler, tc.xy + shadowMapSize.yz).x;
  shd.z = shadowmapTexture.Sample(ShadowmapSampler, tc.xy + shadowMapSize.zy).x;
  shd.w = shadowmapTexture.Sample(ShadowmapSampler, tc.xy + shadowMapSize.yy).x;
  shd = step(tc.z, shd);
  float2 lerpAmount = frac(tc.xy * shadowMapSize.xx);
  float2 lr = lerp(shd.xz, shd.yw, lerpAmount.x);
  return lerp(lr.x, lr.y, lerpAmount.y);
}*/
////////////////////////////////////////////////////////////////////////////////
// Pixel Shader
////////////////////////////////////////////////////////////////////////////////
float4 LightFragmentShader(PixelInputType input) : SV_TARGET
{
	float4 textureColor;
    float3 lightDir;
    float lightIntensity;
    float4 ambientColorComponent = float4(.0f,.0f,.0f,1.0f);
	float4 diffuseColorComponent = float4(.0f,.0f,.0f,1.0f);
	float4 specularColorComponent = float4(.0f,.0f,.0f,1.0f);
	float3 reflection;
    float4 specular;
	float4 bumpFragment;
    float3 resultNormal;

    // Sample the pixel color from the texture using the sampler at this texture coordinate location.
    textureColor = shaderTextures[0].Sample(SampleType, input.tex);
	clip(shaderTextures[2].Sample(SampleType, input.tex).r-0.75f);
	
	resultNormal = input.normal;
	
	if (material.flags & 1)
	{
		// Sample the pixel in the bump map.
		bumpFragment = shaderTextures[1].Sample(SampleType, input.tex);

		// Expand the range of the normal value from (0, +1) to (-1, +1).
		bumpFragment = (bumpFragment * 2.0f) - 1.0f;

		// Calculate the normal from the data in the bump map.
		resultNormal = (bumpFragment.x * input.tangent) + (bumpFragment.y * input.binormal) + (bumpFragment.z * input.normal);
		//resultNormal = (resultNormal + bumpFragment)/2;
	}

	// Normalize the resulting bump normal.
	resultNormal = normalize(resultNormal);

	 // Calculate the amount of light on this pixel for each DIRECTIONAL LIGHT
	for (uint i = 0; i < directionalLightsCount; ++i)
	{
		ambientColorComponent += clamp(directionalLights[i].ambientColor,0.0f,100.0f);
		
		/* shadow map */
		float4 shadowmapCoord;
		shadowmapCoord = input.worldPosition;
		shadowmapCoord = mul(shadowmapCoord, shadowmapView[i]);
		shadowmapCoord = mul(shadowmapCoord, shadowmapProjection[i]);
		shadowmapCoord = shadowmapCoord / shadowmapCoord.w;
		float2 tex = shadowmapCoord.xy;
		tex.x = (shadowmapCoord.x+1.0f)/2.0f;
		tex.y = (1.0f-shadowmapCoord.y)/2.0f;
		float f=1000.0;
		float n = 0.1;
		float z = shadowmapCoord.z;
		/* pcf algorithm for soft shadow */
		float sum = 0;
		float x, y;
		[unroll]
		for (y = -1.5; y <= 1.5; y += 1.0)
			for (x = -1.5; x <= 1.5; x += 1.0)
			{
				sum += saturate((offset_lookup(tex.xy, float2(x, y), i).r - shadowmapCoord.z)*10000000);
			}
		float shadowCoeff = sum / 16.0;
		
		// Invert the light direction for calculations.
		lightDir = -directionalLights[i].direction;
		if ( shadowmapTextures.Sample(ShadowmapSampler, float3(tex,i)).r > shadowmapCoord.z)  
		{
			//if((saturate(tex.x) == tex.x) && (saturate(tex.y) == tex.y))
			{
				shadowCoeff = 1.0f;
			}
		}
				lightIntensity = saturate(dot(resultNormal, lightDir)*shadowCoeff);
				if(lightIntensity > 0.0f)
				{
					// Determine the final diffuse color based on the diffuse color and the amount of light intensity.
					diffuseColorComponent += directionalLights[i].diffuseColor * lightIntensity;
				
					// Calculate the reflection vector based on the light intensity, normal vector, and light direction.
					reflection = normalize(2 * lightIntensity * resultNormal - lightDir); 

					// Determine the amount of specular light based on the reflection vector, viewing direction, and specular power.
					specularColorComponent += directionalLights[i].specularColor*pow(saturate(dot(reflection, input.viewDirection)), material.specularExponent);
				}
	}
    // Calculate the amount of light on this pixel for POINT LIGHT
	for (int i = 0; i < pointLightsCount; ++i)
	{
		lightDir = pointLights[i].position - input.worldPosition;
		float deltaIntensity = pointLights[i].intensityDecrease*length(lightDir);
		ambientColorComponent += clamp(pointLights[i].ambientColor - deltaIntensity,0.0f,100.0f);
	
		/* shadow map texCUBE*/
		float4 shadowmapCoord;
		float distance = length(lightDir);

		
		shadowmapCoord = mul(-lightDir, pointShadowmapProjection);
		//distance = length(shadowmapCoord);

		float shadowCoeff = 0.0f;
		
		float depth = point_offset_lookup(shadowmapCoord.xyz, float3(0, 0, 0), i).r;
		float f=10000.0;
		float n = 0.1;
		depth =(2 * n) / (f + n - depth * (f - n));
		distance = (distance-n)/(f-n);
		//distance = ( f + n - 2*n /distance) / (f-n);
		shadowCoeff = saturate((depth - distance)*1000000.0f);
		
		lightDir = normalize(lightDir);
		lightIntensity = saturate(dot(resultNormal, lightDir)*shadowCoeff);
		if(lightIntensity > 0.0f)
		{
			// Determine the final diffuse color based on the diffuse color and the amount of light intensity.
			diffuseColorComponent += clamp(pointLights[i].diffuseColor - deltaIntensity,0.0f,100.0f)* lightIntensity;
				
			// Calculate the reflection vector based on the light intensity, normal vector, and light direction.
			reflection = normalize(2 * lightIntensity * resultNormal - lightDir); 

			// Determine the amount of specular light based on the reflection vector, viewing direction, and specular power.
			specularColorComponent += clamp(pointLights[i].specularColor - deltaIntensity,0.0f,100.0f)*pow(saturate(dot(reflection, input.viewDirection)), material.specularExponent);
		}
	}
	
    // Multiply the texture pixel and the final diffuse color to get the final pixel color result.
    diffuseColorComponent = (ambientColorComponent*material.ambientColor + diffuseColorComponent*material.diffuseColor) * textureColor;

	// Add the specular component last to the output color.
	specularColorComponent = saturate(diffuseColorComponent + specularColorComponent * material.specularColor);
	
    return specularColorComponent;
}


