//Settings//
#include "/lib/settings.glsl"

//Varyings//
varying vec2 texCoord;

//Fragment Shader///////////////////////////////////////////////////////////////////////////////////
#ifdef FSH

	//Uniforms//
	uniform float viewWidth, viewHeight, aspectRatio;

	uniform ivec2 eyeBrightnessSmooth;

	#ifdef DOF
		uniform float centerDepthSmooth;
	#endif

	#if AA > 1
		uniform int frameCounter;
	#endif

	uniform float frameTimeCounter;
	uniform mat4 gbufferProjection;

	uniform sampler2D colortex0;
	uniform sampler2D depthtex1;

	#ifdef FLOU_EAU
		uniform int isEyeInWater;
	#endif

	#ifdef DISTANCE_BLUR
		uniform sampler2D depthtex0;
		uniform mat4 gbufferProjectionInverse;
	#endif

	#if defined DISTANCE_BLUR || defined RAIN_BLUR_CAM
		uniform float rainStrengthS2;
	#endif

	//Optifine Constants//
	const bool colortex0MipmapEnabled = true;

	//Common Functions//

	mat2 Rotate(float angle)
	{
		return mat2(cos(angle), -sin(angle), sin(angle), cos(angle));
	}

	#ifdef DOF
	float GetCircleStandardDOF(float z, float centerDepthSmooth) {

		#if defined (NETHER) && (defined DOF || defined DISTANCE_BLUR)
			float DOF_STRENGTH = NETHER_DOF_STRENGTH;
		#endif

		#if defined (OVERWORLD) && (defined DOF || defined DISTANCE_BLUR)
			float DOF_STRENGTH = OVERWORLD_DOF_STRENGTH;
		#endif

		#if defined (END) && (defined DOF || defined DISTANCE_BLUR)
			float DOF_STRENGTH = END_DOF_STRENGTH;
		#endif

			float coc = abs(z - centerDepthSmooth) / 0.6;

		return coc / (1.0 / DOF_STRENGTH + coc);
	}

	#endif

	//Includes//
	#include "/lib/util/dofOffsets.glsl"
	#include "/lib/util/dither.glsl"

	//Common Functions//
	float eBS = eyeBrightnessSmooth.y / 240.0;
	float eBS2 = clamp01(pow(eBS, 8.0));

	vec3 ChromaAberration(vec3 color) {
		vec2 offset = texCoord - 0.5;

		offset *= vec2(0.125) * CHROMA_STRENGTH;
		offset *= pow(distance(texCoord.st, vec2(1.0)), 2.5);
			#ifdef CHROMATIC_ABERRATION
				#if defined (OVERWORLD) && defined CHROMATIC_ABERRATION_O
					color.r = texture(colortex0, texCoord.st - offset).r;
					color.g = texture(colortex0, texCoord.st - (offset * 0.5)).g;
					color.b = texture(colortex0, texCoord.st).b;
				#elif defined (NETHER) && defined CHROMATIC_ABERRATION_N
					color.r = texture(colortex0, texCoord.st - offset).r;
					color.g = texture(colortex0, texCoord.st - (offset * 0.5)).g;
					color.b = texture(colortex0, texCoord.st).b;
				#elif defined (END) && defined CHROMATIC_ABERRATION_E
					color.r = texture(colortex0, texCoord.st - offset).r;
					color.g = texture(colortex0, texCoord.st - (offset * 0.5)).g;
					color.b = texture(colortex0, texCoord.st).b;
				#endif

			#endif
	return color;
	}


	vec3 DepthOfField(vec3 color, float z){

			float noise = InterleavedGradientNoise(gl_FragCoord.xy);
				#if AA > 1
				noise = fract(noise + frameTimeCounter * 38.34718);

				mat2 rotation = Rotate(noise * 2.0 * 3.1415);
				#endif

		#if defined (NETHER) && (defined DOF || defined DISTANCE_BLUR)
			float DISTANCE_BLUR_STRENGTH = NETHER_DISTANCE_BLUR_STRENGTH;
			float DOF_STRENGTH = NETHER_DOF_STRENGTH;
		#endif

		#if defined (OVERWORLD) && (defined DOF || defined DISTANCE_BLUR)
			float DISTANCE_BLUR_STRENGTH = OVERWORLD_DISTANCE_BLUR_STRENGTH;
			float DOF_STRENGTH = OVERWORLD_DOF_STRENGTH;
		#endif

		#if defined (END) && (defined DOF || defined DISTANCE_BLUR)
			float DISTANCE_BLUR_STRENGTH = END_DISTANCE_BLUR_STRENGTH;
			float DOF_STRENGTH = END_DOF_STRENGTH;
		#endif

		vec3 dof = vec3(0.0);
		float hand = float(z < 0.56);
		float fovScale = gbufferProjection[1][1] / 1.37;
		float fovScale2 = gbufferProjection[1][1] / 2.74;

		#ifdef DISTANCE_BLUR
			float z0         = texture(depthtex0, texCoord.xy).r;
			vec4  screenPos  = vec4(texCoord.x, texCoord.y, z0, 1.0);
			vec4  viewPos    = gbufferProjectionInverse * (screenPos * 2.0 - 1.0);
			      viewPos   /= viewPos.w;

			float coc1 = max(min(length(viewPos) * 0.001, 0.1) * DISTANCE_BLUR_STRENGTH * (1.0 + (rainStrengthS2 * eBS * RAIN_BLUR_MULT)) / 256 , 0.001);

		#else

			float coc1 = 0;

		#endif

		#ifdef DOF
			float coc2 = GetCircleStandardDOF(z, centerDepthSmooth);

			#ifdef EXTRA_CIRCULAR_DOF
				float coc2dist = max(clamp(abs(1.0 - centerDepthSmooth), 0.0, 0.002) / EXTRA_CIRCULAR_DOF_STRENGTH * DOF_STRENGTH - 0.001, 0.001);
				float coc2center = max(length((texCoord.xy * 2.0 - 1.0) * vec2(aspectRatio, 1.0)), 0.001);
				coc2 = max(coc2,coc2dist*coc2center);
			#endif

		#else

			float coc2 = 0.0;

		#endif

				float coc = max(coc1,coc2);

		#ifdef FLOU_EAU
			if (float(isEyeInWater) > 0.9) coc = max(FACTEUR_FLOU_EAU / 2.0 ,coc);
		#endif

		#ifdef RAIN_BLUR_CAM
			vec2  spos         = texCoord.xy * 2.0 - 1.0;
			float blurVignette = clamp01(pow(length(spos), RAIN_BLUR_CAM_INTENSITY));
			      coc          = max(coc, rainStrengthS2 * eBS2 * blurVignette * 20.0);
		#endif

		coc = coc / sqrt(coc * coc + 0.1);
		float chromaOffset  = CHROMA_STRENGTH / 100.0;
		      chromaOffset *= coc;

		if (coc * 0.5 > 1.0 / max(viewWidth, viewHeight) && hand < 0.5){
			#if BLUR_QUALITY == 1
			int nbOffsets = 60;
			#endif
			#if BLUR_QUALITY == 2
			int nbOffsets = 18;
			#endif

			for(int i = 0; i < nbOffsets; i++) {

				vec2 offset = dofOffsets[i] * coc * 0.0085 * fovScale * vec2(1.0 / aspectRatio, 1.0);

				#if AA > 1
					offset = rotation * offset;
				#endif

				float lod = log2(viewHeight * aspectRatio * coc * 0.75 / 320.0);
				#ifdef CHROMATIC_ABERRATION
					#ifdef OVERWORLD
						#ifdef CHROMATIC_ABERRATION_O
							dof.r += textureLod(colortex0, texCoord + offset + vec2(chromaOffset * fovScale2, 0.0), lod).r;
							dof.g += textureLod(colortex0, texCoord + offset, lod).g;
							dof.b += textureLod(colortex0, texCoord + offset - vec2(chromaOffset * fovScale2, 0.0), lod).b;
						#else
							dof += textureLod(colortex0, texCoord + offset, lod).rgb;
						#endif
					#endif
					#ifdef NETHER
						#ifdef CHROMATIC_ABERRATION_N
							dof.r += textureLod(colortex0, texCoord + offset + vec2(chromaOffset * fovScale2, 0.0), lod).r;
							dof.g += textureLod(colortex0, texCoord + offset, lod).g;
							dof.b += textureLod(colortex0, texCoord + offset - vec2(chromaOffset * fovScale2, 0.0), lod).b;
						#else
							dof += textureLod(colortex0, texCoord + offset, lod).rgb;
						#endif
					#endif
					#ifdef END
						#ifdef CHROMATIC_ABERRATION_E
							dof.r += textureLod(colortex0, texCoord + offset + vec2(chromaOffset * fovScale2, 0.0), lod).r;
							dof.g += textureLod(colortex0, texCoord + offset, lod).g;
							dof.b += textureLod(colortex0, texCoord + offset - vec2(chromaOffset * fovScale2, 0.0), lod).b;
						#else
							dof += textureLod(colortex0, texCoord + offset, lod).rgb;
						#endif
					#endif
				#else
				dof += textureLod(colortex0, texCoord + offset, lod).rgb;
				#endif
			}
			dof /= nbOffsets;
		}
		else dof = color;
		return dof;
	}

	//Includes//
	#ifdef BLACK_OUTLINE
	#include "/lib/outline/outlineOffset.glsl"
	#include "/lib/outline/outlineDepth.glsl"
	#endif


	void main(){
		vec3 color = textureLod(colortex0, texCoord, 0.0).rgb;
		float z = texture(depthtex1, texCoord.st).x;

		#ifdef BLACK_OUTLINE
			DepthOutline(z);
		#endif

		if(z > 0.56){
			#ifdef CHROMATIC_ABERRATION
				color = ChromaAberration(color);
			#endif
		}

		#if (defined DOF || defined DISTANCE_BLUR) || (defined OVERWORLD || defined NETHER || defined END)
			color = DepthOfField(color, z);
		#endif

		/*DRAWBUFFERS:0*/
		gl_FragData[0] = vec4(color, 1.0);
	}

#endif

//Vertex Shader/////////////////////////////////////////////////////////////////////////////////////
#ifdef VSH

	//Program//
	void main(){
		texCoord=gl_MultiTexCoord0.xy;

		gl_Position=ftransform();
	}

#endif