/*#############################################
#    _   ___ _____ ___    _   _    _____  __  #
#   /_\ / __|_   _| _ \  /_\ | |  | __\ \/ /  #
#  / _ \\__ \ | | |   / / _ \| |__| _| >  <   #
# /_/ \_\___/ |_| |_|_\/_/ \_\____|___/_/\_\  #
#											  #
#############################################*/

//Settings//////////////////Settings///////////////////Settings///////////////////Settings//

#include "/lib/util/functions.glsl"

#include "/settings/globalSettings.glsl"

//Varying////////////////////Varying////////////////////Varying////////////////////Varying//
#ifdef AO
varying vec2 texCoord;
#endif

//Vertex Shader////////////Vertex Shader////////////Vertex Shader////////////Vertex Shader//
#ifdef VERTEX

	//Program//
	void main() {
		#ifdef AO
			texCoord = (gl_TextureMatrix[0] * gl_MultiTexCoord0).xy;

			gl_Position = ftransform();
		#else
			gl_Position = vec4(0.0);
		#endif
	}

#endif

//Fragment Shader//////////Fragment Shader/////////Fragment Shader/////////Fragment Shader//
#ifdef FRAGMENT

	//Uniforms//
	#ifdef AO
		uniform float far, near;

		uniform float viewWidth, viewHeight, aspectRatio;

		uniform mat4 gbufferProjection;

		uniform sampler2D depthtex0;

	#endif

	uniform int frameCounter;

	//Includes//
	#ifdef AO
		#include "/lib/util/dither.glsl"
	#endif

	//Common Functions//
	#ifdef AO

		float GetLinearDepth(float depth) {
			return (2.0 * near) / (far + near - depth * (far - near));
		}

		vec2 OffsetDist(float x) {
			float n = fract(x * 8.0) * PI;
			return vec2(cos(n), sin(n)) * x;
		}

		float AmbientOcclusion(float dither) {
			float ao = 0.0;
			float fovScaleAO = 0.0;

			#if AO_FOVSCALE ==1
				fovScaleAO = 3.425;
			#elif AO_FOVSCALE ==2
				fovScaleAO = 2.74;
			#elif AO_FOVSCALE ==3
				fovScaleAO = 2.055;
			#elif AO_FOVSCALE ==4
				fovScaleAO = 1.37;
			#elif AO_FOVSCALE ==5
				fovScaleAO = 0.685;
			#endif

			float depth = texture2D(depthtex0, texCoord).r;
			if (depth >= 1.0)
				return 1.0;

			float hand = float(depth < 0.56);
			depth = GetLinearDepth(depth);

			#if AA > 1
				dither = (dither + frameCounter * 0.618) - floor(dither + frameCounter * 0.618);
			#endif

			float currentStep = 0.2 * dither + 0.2;

			const float radius = 0.5;
			float fovScale = gbufferProjection[1][1] / fovScaleAO;
			float distScale = max((far - near) * depth + near, 7.0);
			vec2 scale = radius * vec2(1.0 / aspectRatio, 1.0) * fovScale / distScale;
			float mult = (0.7 / radius) * (far - near) * (hand > 0.5 ? 1024.0 : 1.0);

			#if AO_QUALITY == 1
				const int samples = 4;
			#elif AO_QUALITY == 2
				const samples = 5;
			#elif AO_QUALITY == 3
				const samples = 6;
			#endif

			for (int i = 0; i < samples; i++) {
				vec2 offset = OffsetDist(currentStep) * scale;
				vec2 angleDistSum = vec2(0.0);

				for (int j = 0; j < samples * 0.5; j++) {
					float sampleDepth = GetLinearDepth(texture(depthtex0, texCoord + offset).r);
					float sample = (depth - sampleDepth) * mult / currentStep;
					angleDistSum.x += clamp01(0.5 - sample);
            		angleDistSum.y += clamp01(0.25 * sample - 1.0);
					offset = -offset;
				}

				ao += clamp01(angleDistSum.x + angleDistSum.y);
        		currentStep += 0.2;
			}

			ao *= 0.25;

			return ao;
		}
	#endif

	//Program//
	void main() {

		float ao = 1.0;

		#ifdef AO
			ao = AmbientOcclusion(Bayer8(gl_FragCoord.xy));
		#endif

		/* RENDERTARGETS: 4 */
		gl_FragData[0] = vec4(ao, 0.0, 0.0, 0.0);
	}

#endif