#version 330

in vec2 UV;

layout(location = 0) out vec4 color;
layout(location = 1) out vec4 albedo;
layout(location = 2) out vec4 ND;
layout(location = 3) out vec4 var;
layout(location = 4) out vec4 pos3D;
//layout(location = 5) out vec4 colorM;
//layout(location = 6) out vec4 varM;
//layout(location = 5) out vec4 colorView;
//layout(location = 6) out vec4 varView;

uniform sampler2D texDiff;
uniform sampler2D texAlbedo;
uniform sampler2D texNormalDepth;

uniform sampler2D texDiffPrev;
uniform sampler2D texDiffFilteredPrev;
uniform sampler2D texAlbedoPrev;
uniform sampler2D texNDPrev;
uniform sampler2D texVarPrev;
uniform sampler2D texUV;
uniform sampler2D prev3D;

uniform sampler2D texReprojMPrev;
uniform sampler2D texVarMPrev;

uniform usampler2D texDepth;
uniform usampler2D texDepthFar;

uniform vec2 resolution;
uniform uint frameId;
uniform int rejectNow;
uniform int fullNew;
uniform float reprojSp;
uniform float debug;
uniform int useReproject;
uniform int camMoved;
uniform int accum;

uniform mat4 MVP;
uniform mat4 MVPprev;
uniform vec3 origin;
uniform vec3 originPrev;

vec2 offset[] = vec2[](vec2(-1,-1), vec2(0,-1), vec2(1,-1),
vec2(-1,0), vec2(1,0),
vec2(-1,1), vec2(0,1), vec2(1,1));

vec2 offset2[] = vec2[](vec2(-1,-1), vec2(0,-1), vec2(1,-1),
vec2(-1,0), vec2(0,0), vec2(1,0),
vec2(-1,1), vec2(0,1), vec2(1,1));

float kernel[] =
float[](6.250000e-02, 9.375000e-02, 6.250000e-02,
9.375000e-02, 1.406250e-01, 9.375000e-02,
6.250000e-02, 9.375000e-02, 6.250000e-02);

vec3 grayRgb = vec3(0.2989, 0.5870, 0.1140);

vec2 signNotZero(vec2 v) {
	return vec2((v.x >= 0.0) ? +1.0 : -1.0, (v.y >= 0.0) ? +1.0 : -1.0);
}

vec4 encodeN(vec3 n)
{
    float p = sqrt(n.z*8+8);
    return vec4(n.xy/p + 0.5,0,0);
}
vec3 decodeN(vec2 enc)
{
    vec2 fenc = enc*4-2;
    float f = dot(fenc,fenc);
    float g = sqrt(1-f/4);
    vec3 n;
    n.xy = fenc*g;
    n.z = 1-f/2;
    return n;
}

vec2 float32x3_to_oct(in vec3 v) {
	vec2 p = v.xy * (1.0 / (abs(v.x) + abs(v.y) + abs(v.z)));
	return (v.z <= 0.0) ? ((1.0 - abs(p.yx)) * signNotZero(p)) : p;
}
vec3 oct_to_float32x3(vec2 e) {
	vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
	if (v.z < 0)
		v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy);
	return normalize(v);
}

void main(){
	//varM.a = -1;
	vec4 NDCol = texture(texNormalDepth, UV).rgba;

	float depth = NDCol.a;
	vec2 UV2 = UV;
	vec2 UVview = UV;
	float bad = 0;
	if (useReproject == 1) {
		vec2 off = texture(texUV, UV).rg;

		if (off.x < -200) { //missing UV, TODO: better approx
			bad = 1;
			UV2 = vec2(0);
			int cnt = 0;
			float cntF = 0;
			float minDepth = 1e+17;
			float maxDepth = 1e-17;
			for(int i = 0; i<8; i++) { //average values around
				vec2 UV3 = texture(texUV, UV + offset[i]/resolution).rg;
				if (UV3.x > -200) {
					UV2 += UV3;//*kernel[i];
					cnt++;
					//cntF += kernel[i];

					float depthNow = texture(texNormalDepth, UV + UV3*resolution).a;
					minDepth = min(depthNow,minDepth);
					maxDepth = max(depthNow,maxDepth);
				}
			}
			if (cnt < 3)
				UV2 = vec2(-1);
			else {
				UV2 /= cnt;
				UV2 = UV2*resolution + UV;
				float myDepth = texture(texNormalDepth, UV2).a;
				//if (abs(maxDepth - myDepth) > 100*debug || abs(minDepth - myDepth) > 100*debug)
				//	UV2 = vec2(-1);
			}
			UVview = UV2;
		}
		else //off correct
			UV2 = off*resolution + UV;

		vec2 p = UV*2 - 1;
		vec4 tmpDir = MVP * vec4(p,1,1);
		vec3 dir = normalize(tmpDir.xyz/tmpDir.w - origin);
		vec3 pos = origin + dir*depth; //pos in world space

		vec4 pos4 = MVP * vec4(pos, 1);
		pos4 /= pos4.w;
		UV2 = (pos4.xy + 1) / 2;

		float myDepth = texture(texNormalDepth, UV2).a;
		//if (abs(myDepth - depth) > 350)
		//	UV2 = vec2(-1);
	}

	vec2 velocity = UV2;//(UV2 - UV)/resolution;

	vec4 prevND;
	vec4 prevVarS;

	vec4 allCol = texture(texDiff, UV).rgba;
	vec4 prevCol;
	vec4 prevFilteredCol;
	vec4 prevAlbedo;

	prevND = texture(texNDPrev, UV2).rgba;

	vec2 grad = vec2(dFdx(depth),dFdy(depth))*2;
	float gradMag = dot(grad,grad);

	//read and compare frame number
	
	uint tmpNum = floatBitsToUint(allCol.a);
	uint countNum = tmpNum & uint(0xFFFF);
	uint frameIdNum = (tmpNum >> uint(16)) & uint(0x3FFF);
	if (!((countNum > uint(0)) && (frameIdNum==frameId))) {

		prevND = texture(texNDPrev, UV2).rgba;
		prevVarS = texture(texVarPrev, UV2).rgba;

		prevCol = texture(texDiffPrev, UV2).rgba;
		prevFilteredCol = texture(texDiffFilteredPrev, UV2).rgba;
		prevAlbedo = texture(texAlbedoPrev, UV2).rgba;

		//missing new color, none in old
		if (rejectNow == 1 || UV2.x < 0 /*|| debug == 0 && debug!=3*/) {
			if (UV2.x < 0)
				color = vec4(0,1,0,-1);
			else
				color = vec4(1,0,0,-1);
			color = vec4(1,0,0,-1);
			albedo = vec4(0);
			ND = vec4(0);
			var = vec4(0,0,0,-1);
			pos3D = vec4(0/0);
		}
		//missing new color, copying old
		else {
			/*if (accum == 1) {
				colorM = prevFilteredCol; colorM.a = velocity.x;
				varM = prevVarS;
			}*/
			color = prevFilteredCol; color.a = velocity.x;
			var = prevVarS;
			albedo = prevAlbedo;
			ND = prevND;
			pos3D = texture(prev3D, UV2).rgba; pos3D.a = velocity.y;
		}
	}
	else {

		vec2 cumUV = UV2;
		vec3 posPrev;
		vec3 cumLumSample;

		prevVarS = texture(texVarPrev, UV2).rgba;

		prevCol = texture(texDiffPrev, UV2).rgba;
		prevFilteredCol = texture(texDiffFilteredPrev, UV2).rgba;
		prevAlbedo = texture(texAlbedoPrev, UV2).rgba;
		posPrev = texture(prev3D, UV2).rgb;

		cumLumSample = texture(texVarPrev, cumUV).rga;


		vec4 albedoCol = texture(texAlbedo, UV).rgba;
		vec3 deModulCol = allCol.rgb;//albedoCol.rgb;

		//NDCol.xyz = oct_to_float32x3(NDCol.xy); //unpack normal
		//NDCol.xyz = decodeN(NDCol.xy); //unpack normal

		vec2 p = UV*2 - 1;
		vec4 tmpDir = MVP * vec4(p,1,1);
		vec3 dir = normalize(tmpDir.xyz/tmpDir.w - origin);
		vec3 pos = origin + dir*depth; //pos in world space

		color = vec4(deModulCol/countNum,velocity.x);
		albedo = albedoCol;
		ND = NDCol;
		var = prevVarS;
		pos3D = vec4(pos,velocity.y);

		vec3 nnn = decodeN(NDCol.xy);
		if (isnan(nnn.x + nnn.y + nnn.z))
			ND = vec4(0,0,0,-1);

		float singleLum = dot(color.rgb, grayRgb);
		vec2 lum = vec2(singleLum,0);
		lum.y = lum.x*lum.x;

		//spatial variance
		ND.z = lum.x;
		albedo.w = lum.y;

		vec3 distD = pos - posPrev;
		float dist = sqrt(dot(distD,distD));

		float rejectPrev = prevVarS.a;
		float reject = 1.0f / ((1.0f / rejectPrev) + 1.0f); //next in sequence 1, 1/2, 1/3, 1/4...

		if (accum == 0)
		if (/*debug!=3 &&*/ rejectNow == 1 || prevVarS.a < 0 || (useReproject == 1 && UV2.x < 0)) {
			var.a = 1;
			return; //don't blend if rejected
		}

		if (camMoved == 1)
			reject = max(reject,0.1f); //ratio max 0.1 while moving
		else
			reject = max(reject,0.0f); //no ratio bounding while steady
		if (bad > 0)
			reject = max(reject,0.5f); //a lot of ratio ´bounding when badly reprojected

		float rejectOrig = reject;
		reject = reject*reprojSp/100;

		vec2 cumLum = cumLumSample.rg;
		float prevVar = cumLum.y - cumLum.x*cumLum.x;

		vec2 lumSum = lum*reject + cumLum*(1 - reject);
		if (reject > 0.99)
			lumSum = lum;
		float varVal = lumSum.y - lumSum.x*lumSum.x;

		if (debug > 0)
			color = vec4(reject*color.rgb + (1-reject)*prevFilteredCol.rgb,velocity.x);
		//albedo = reject*albedo + (1-reject)*albedo;
		var = vec4(lumSum,varVal,rejectOrig);

		//spatial variance
		ND.z = lum.x;
		albedo.w = lum.y;

		if (accum == 1) {
			color = vec4(deModulCol/countNum,velocity.x);
			lumSum.x = NDCol.z/countNum;
			lumSum.y = albedoCol.w/countNum;
			varVal = lumSum.y - lumSum.x*lumSum.x;
			var = vec4(lumSum,varVal,1.0f/(countNum));
		}
	}
}