#version 330 core

uniform sampler2D depth_buffer;
uniform sampler2D normal_depth;
uniform sampler2D color_buffer;
uniform sampler2D rotate;
uniform mat4 ProjectionMatrixInverse;

uniform vec4 CameraRange; // near, far, (far - near), far*near
uniform vec2 ScreenSize;
uniform float radius;
uniform float attBias;
//uniform float attScale;
uniform float attPower;
uniform float depthScale;
uniform vec3 gi_samples[16];
uniform vec2 TexelSize;

// units in space the AO effect extends to (this gets divided by the camera far range)
#define aorange depthScale

#define depthTolerance attBias

in vec2 uv;

layout (location = 0) out vec4 frag_color;

vec3 readNormal(in vec2 coord)
{
	return texture(normal_depth, coord).xyz;
}

vec3 calcPosition(in vec2 coord)
{
	float d = texture(depth_buffer, coord).x;
	vec4 wpos = vec4((coord.x - 0.5)*2.0, (coord.y - 0.5)*2.0, d, 1.0);
	wpos = ProjectionMatrixInverse * wpos;
	return wpos.xyz / wpos.w;
}

float aoFactor(in vec3 ddiff, in vec3 cnorm, in float c1, in float c2)
{
	vec3 vv = normalize(ddiff);
	float rd = length(ddiff);
	return (1.0 - clamp(dot( readNormal(uv + vec2(c1,c2)), -vv),0.0,1.0)) *
	clamp(dot( cnorm, vv ), 0.0, 1.0) * (1.0 - 1.0/sqrt(1.0/(rd*rd) + 1.0));
}

float giFactor(in vec3 ddiff, in vec3 cnorm, in float c1, in float c2)
{
	vec3 vv = normalize(ddiff);
	float rd = length(ddiff);
	return clamp(dot(readNormal(uv + vec2(c1,c2)), -vv), 0.0, 1.0) *
		clamp(dot(cnorm, vv), 0.0, 1.0) / (rd*rd + 1.0);  
}

void main()
{
	//read current normal,position and color.
	vec3 n = readNormal(uv);
	vec3 p = calcPosition(uv);
	vec3 col = texture(color_buffer, uv).rgb;

	// randomization texture
	vec2 fres = vec2(ScreenSize.x/16.0, ScreenSize.y/16.0);
	vec3 random = texture(rotate, uv*fres.xy).xyz*2.0 - vec3(1.0);

	//initialize variables:
	float ao = 0.0;
	vec3  gi = vec3(0.0);
	float incx = TexelSize.x*radius;
	float incy = TexelSize.y*radius;
	float pw = incx;
	float ph = incy;
	float cdepth = texture(depth_buffer, uv).r;

	for (float i=0.0; i < 3.0; i+=1.0)
	{
		float npw = (pw + 0.0007*random.x) / cdepth;
		float nph = (ph + 0.0007*random.y) / cdepth;

		vec3 ddiff  = calcPosition(uv + vec2(npw,nph)) - p;
		vec3 ddiff2 = calcPosition(uv + vec2(npw,-nph)) - p;
		vec3 ddiff3 = calcPosition(uv + vec2(-npw,nph)) - p;
		vec3 ddiff4 = calcPosition(uv + vec2(-npw,-nph)) - p;
		vec3 ddiff5 = calcPosition(uv + vec2(0.0,nph)) - p;
		vec3 ddiff6 = calcPosition(uv + vec2(0.0,-nph)) - p;
		vec3 ddiff7 = calcPosition(uv + vec2(npw,0.0)) - p;
		vec3 ddiff8 = calcPosition(uv + vec2(-npw,0.0)) - p;

		ao +=  aoFactor( ddiff, n, npw,nph);
		ao +=  aoFactor(ddiff2, n, npw,-nph);
		ao +=  aoFactor(ddiff3, n, -npw,nph);
		ao +=  aoFactor(ddiff4, n, -npw,-nph);
		ao +=  aoFactor(ddiff5, n, 0.0, nph);
		ao +=  aoFactor(ddiff6, n, 0.0, -nph);
		ao +=  aoFactor(ddiff7, n, npw, 0.0);
		ao +=  aoFactor(ddiff8, n, -npw, 0.0);

		gi += giFactor( ddiff, n, npw, nph) * texture(color_buffer, uv+vec2(npw,nph)).rgb;
		gi += giFactor(ddiff2, n, npw,-nph) * texture(color_buffer, uv+vec2(npw,-nph)).rgb;
		gi += giFactor(ddiff3, n,-npw, nph) * texture(color_buffer, uv+vec2(-npw,nph)).rgb;
		gi += giFactor(ddiff4, n,-npw,-nph) * texture(color_buffer, uv+vec2(-npw,-nph)).rgb;
		gi += giFactor(ddiff5, n, 0.0, nph) * texture(color_buffer, uv+vec2(0.0,nph)).rgb;
		gi += giFactor(ddiff6, n, 0.0,-nph) * texture(color_buffer, uv+vec2(0.0,-nph)).rgb;
		gi += giFactor(ddiff7, n, npw, 0.0) * texture(color_buffer, uv+vec2(npw,0.0)).rgb;
		gi += giFactor(ddiff8, n,-npw, 0.0) * texture(color_buffer, uv+vec2(-npw,0.0)).rgb;

		pw += incx;
		ph += incy;
	}

	ao /= 24.0;
	gi /= 24.0;

	frag_color.rgb = gi*5.0;
	frag_color.a = pow(1.0 - ao, attPower);
	//frag_color = vec4(col-vec3(ao)+gi*5.0,1.0);
}
