//--------------------------------------------------------------------------------------
// Constant Buffer Variables
//--------------------------------------------------------------------------------------

Texture2D posTex : register( t0 );
Texture2D normalTex : register( t1 );

Texture2D colorTex : register( t2 );
Texture2D saaoTex : register( t3 );
Texture2D glowTex : register( t4 );

Texture2D randomTex : register( t5 );

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

cbuffer cbChangesOnFrame : register( b0 )
{
	matrix viewM;
    float4 fov;
};

//--------------------------------------------------------------------------------------

static const float aoFactor = 0.5f;
static const int nP = 10;
static const int circleX[nP] = {0,1,2,3,4,5,5,5,4,3};
static const int circleY[nP] = {5,5,5,4,3,0,1,2,3,4};
static const float maxRadius = 5.0f;
static const float radiusFallOff = 60.0f;
static const float comparisonDistance = 5.0f;
static const float2 randomTexDim = float2(2.0f,2.0f);

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

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


//--------------------------------------------------------------------------------------
// Functions
//--------------------------------------------------------------------------------------

float calcAngleWithBresenham(int dx, int dy, int r, float2 dir, float2 tex, float2 texStep, float4 pos, float4 norm) {
	int x, y, t, pdx, pdy, ddx, ddy, es, el, err;
	float4 cPos;
	float ca;
	float angle = 0.0f;
	
	if (dx>dy) {
		pdx=1; pdy=0;
		ddx=1; ddy=1;
		es =dy; el =dx;
	} 
	else {
		pdx=0; pdy=1;
		ddx=1; ddy=1;
		es =dx; el =dy;
	}
 
	x = 0;
	y = 0;
	err = el/2;
	//SetPixel(x,y); //start pixel mustnt be set
 
	for(t=0; t<maxRadius; ++t) {
		if(t<r) { //TODO: radius ja oder nein?
			err -= es; 
			if(err<0) {
			  err += el;
			  x += ddx;
			  y += ddy;
			}
			else {
			  x += pdx;
			  y += pdy;
			}

			cPos = posTex.Sample( samPoint, tex + texStep * dir * int2(x,y) );
			if((pos.w-cPos.w)<comparisonDistance) {
				ca = saturate(dot(norm.xyz, normalize(cPos.xyz - pos.xyz)));
				if(ca>angle)
					angle=ca;
			}
		}
	}
	return angle;
}


//--------------------------------------------------------------------------------------
// 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 = posTex.Sample( samPoint, input.Tex );
	float4 norm = normalTex.Sample( samPoint, input.Tex );
	
	float ao = 0.0f;

	float2 dim = (float2)0;
	posTex.GetDimensions(dim.x,dim.y);
	float2 texStep = float2(1.0f/(dim.x*0.5f),1.0f/(dim.y*0.5f));

	int radius = 0;
	if(pos.w < radiusFallOff) {
		radius = ceil( (1.0f - pos.w / radiusFallOff ) * maxRadius );
	}

	uint4 rnd = randomTex.Sample( samPoint, input.Tex * dim / randomTexDim ) * 255.0f;

	ao += calcAngleWithBresenham(circleX[rnd.x],circleY[rnd.x], radius, float2( 1, 1), input.Tex, texStep, pos, norm);
	ao += calcAngleWithBresenham(circleX[rnd.y],circleY[rnd.y], radius, float2(-1, 1), input.Tex, texStep, pos, norm);
	ao += calcAngleWithBresenham(circleX[rnd.z],circleY[rnd.z], radius, float2( 1,-1), input.Tex, texStep, pos, norm);
	ao += calcAngleWithBresenham(circleX[rnd.w],circleY[rnd.w], radius, float2(-1,-1), input.Tex, texStep, pos, norm);

	ao /= (4 * radius);
	if(radius==0)
		return 1.0f;
	return 1.0f - ao;
}

float4 ApplyPS( PS_INPUT input) : SV_TARGET
{
	float4 color = colorTex.Sample( samPoint, input.Tex );
	float ao = saaoTex.Sample( samLinear, input.Tex );
	float4 glow = glowTex.Sample( samLinear, input.Tex );

	return float4(color.xyz * ao * aoFactor + glow.xyz, 0.0f);
}