#version 400

uniform sampler2D	colorMap;
uniform sampler2D	normalMap;
uniform sampler2D	depthMap;
uniform sampler2DMS normalMapMulti;
uniform sampler2DMS depthMapMulti;

uniform vec2 pixelSize;
uniform vec3 planes;

smooth in vec2 VS_TexCoord;
layout(location = 0) out vec4 colorOut;

const int samplesCount = 4;
const int normalScale = -10;
const float depthScale = -250;

vec3 spheremapDec(vec2 enc)
{
  vec3 ret = vec3(enc * 2.0f, 0.0f) + vec3(-1.0f, -1.0f, 1.0f);
  float l = dot(ret.xy, -ret.xy) + 1.0f;
  ret.z = l;
  ret.xy *= sqrt(l);
  return ret.xyz * 2.0f + vec3(0.0f, 0.0f, -1.0f);
}

float bilateral(vec3 centerN, float centerZ, vec3 nbN, float nbZ)
{
	return exp(min(normalScale * (1.0 - dot(centerN, nbN)), depthScale * abs(centerZ - nbZ)));
}

void main()
{
	//prepare arrays
	float weights[9] = float[9](0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
	float tmpWeights[9];
	vec3  nbNormal[9];
	float nbDepth[9];

	//calculate un-normalized texture coordinates
    //ivec2 coord = ivec2(floor(textureSize(depthMapMulti) * VS_TexCoord));
	ivec2 coord = ivec2(gl_FragCoord.xy);

	//save data from neigbours
	for(int j = 0; j < 3; ++j)
	{
		for(int k = 0; k < 3; ++k)
		{
			vec2 nbCoord = VS_TexCoord + vec2(k-1, j-1) * pixelSize;
					
			int index = k + j * 3;
			nbNormal[index] = spheremapDec(texture(normalMap, nbCoord).xy);
			nbDepth[index] = texture(depthMap, nbCoord).x;
			nbDepth[index] = (planes.y / (planes.x + nbDepth[index])) / planes.z;				
		}
	}

	//iterate over all samples in pixel
	for(int i = 0; i < samplesCount; ++i)
	{
		vec3 sampleNormal = spheremapDec(texelFetch(normalMapMulti, coord, i).xy);
		float sampleDepth = texelFetch(depthMapMulti, coord, i).x;
		sampleDepth = (planes.y / (planes.x + sampleDepth)) / planes.z; //get linear sample depth <0-1>		

		//iterate over the neighboring pixels
		float sum = 0.0;
		float plane = 0.0;
		for(int j = 0; j < 3; ++j)
		{
			for(int k = 0; k < 3; ++k)
			{
				int index = k + j * 3;

				//compute the filter weight
				float w = bilateral(sampleNormal, sampleDepth, nbNormal[index], nbDepth[index]);
				tmpWeights[index] = w;
				sum += w;

				plane += min(w, 0.1);
			}
		}

		//if neighbourhood is same don't blur it
		if(plane > 0.8999 || sum < 0.001)
		{
			colorOut = texture(colorMap, VS_TexCoord);
			return;
		}
			
		for(int j = 0; j < 9; ++j)
			weights[j] += tmpWeights[j] / sum;
	}

	//apply the filter
	colorOut = vec4(0.0);
	for(int j = 0; j < 3; ++j)
	{
		for(int i = 0; i < 3; ++i)
		{
			vec2 nbCoord = VS_TexCoord + vec2(i-1, j-1) * pixelSize;
			colorOut += weights[i + j * 3] * texture(colorMap, nbCoord);
		}
	}

	colorOut = colorOut * 0.25;
	//colorOut = vec4(1,0,0,1); //debug
}
