/*//////////////////////////////////////////////////////////////////////////////
//    ##      #####   ######   ######     ##     ####     #######  ##  ##     //
//   ####    ##   ##  # ## #    ##  ##   ####     ##       ##   #  ##  ##     //
//  ##  ##   #          ##      ##  ##  ##  ##    ##       ## #     ####      //
//  ##  ##    #####     ##      #####   ##  ##    ##       ####      ##       //
//  ######        ##    ##      ## ##   ######    ##   #   ## #     ####      //
//  ##  ##   ##   ##    ##      ##  ##  ##  ##    ##  ##   ##   #  ##  ##     //
//  ##  ##    #####    ####    #### ##  ##  ##   #######  #######  ##  ##     //
//----------------------------------------------------------------------------//
//----------------------------------------------------------------------------//
//      #####   ##   ##    ##     #####    #######  ######    #####           //
//     ##   ##  ##   ##   ####     ## ##    ##   #   ##  ##  ##   ##          //
//     #        ##   ##  ##  ##    ##  ##   ## #     ##  ##  #                //
//      #####   #######  ##  ##    ##  ##   ####     #####    #####           //
//          ##  ##   ##  ######    ##  ##   ## #     ## ##        ##          //
//          ##  ##   ##  ######    ##  ##   ## #     ## ##        ##          //
//     ##   ##  ##   ##  ##  ##    ## ##    ##   #   ##  ##  ##   ##          //
//      #####   ##   ##  ##  ##   #####    #######  #### ##   #####           //
/////(BSL Shaders Edit)//////////////////////////////////////By LexBoosT//////*/

//Settings//

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

#include "/settings/globalSettings.glsl"

#include "/settings/color/colorGradingSettings.glsl"

//Varyings//
varying vec2 texCoord;
varying vec3 sunVec, upVec;

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

	//Uniforms//
	uniform sampler2D colortex0;
	uniform sampler2D colortex1;
	uniform sampler2D colortex2;
	uniform sampler2D noisetex;
	uniform sampler2D depthtex0;

	uniform int isEyeInWater;

	uniform int moonPhase;
	#define UNIFORM_MOONPHASE

	uniform float frameTimeCounter;
	uniform int frameCounter;
	uniform float rainStrengthS;
	uniform float screenBrightness;
	uniform float viewWidth, viewHeight, aspectRatio;
	uniform float far, near;

	uniform ivec2 eyeBrightnessSmooth;

	#ifdef DIRTY_LENS
		uniform sampler2D depthtex2;
	#endif

	#if (defined COLOR_START || defined BLOOM_START)
		uniform float starter;
	#endif

	#if (defined ENABLE_DARKNESS_EFFECT && MC_VERSION >= 11900)
		uniform float darknessFactor;
	#endif

	#if (defined OVERWORLD && defined LENS_FLARE)
		uniform float blindFactor;
		uniform vec3 sunPosition;
		uniform mat4 gbufferProjection;
	#endif

	#ifdef UNDERGROUND_SKY
	uniform float isEyeInCave;
	#endif

	#ifdef AUTO_EXPOSURE
		const bool colortex0MipmapEnabled = true;
	#endif

	uniform mat4 gbufferModelView;

	//Common Variables//
	float eBS              =eyeBrightnessSmooth.y / 240.0;
	float sunVisibility    =clamp00125(dot( sunVec,upVec) + 0.0625) * 8.0;
	float moonVisibility   =clamp00125(dot( -sunVec,upVec) + 0.0625) * 8.0;
	float screenBrightness2=clamp01(screenBrightness);
	float pw               =1.0 / viewWidth;
	float ph               =1.0 / viewHeight;

	//Common Functions//

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

	float GetLuminance(vec3 color){
		return dot(color,vec3(0.299, 0.587, 0.114));
	}

	#ifdef FILM_GRAINS
		float hash13(vec3 p3){
			p3  = fract(p3 * 443.8975);
			p3 += dot(p3, p3.yzx + 19.19);
			return fract((p3.x + p3.y) * p3.z);
		}

		vec3 applyGrain(vec2 vUV, vec3 col, float amount){
			float h = hash13(vec3(vUV, frameTimeCounter));

			col *= (h * 2.0 - 1.0) * amount + (1.0 -amount);

			return col;
		}
	#endif

	vec3 GetBloomTile(float lod, vec2 coord, vec2 offset) {
		float scale = exp2(lod);
		float resScale = 1.25 * min(360.0, viewHeight) / viewHeight;
		vec2 centerOffset = vec2(0.125 * pw, 0.25 * ph);
		vec3 bloom = texture2D(colortex1, (coord / scale + offset) * resScale + centerOffset).rgb;
			 bloom *= bloom; bloom *= bloom * 32.0;
		return bloom;
	}

	void Bloom(inout vec3 color, vec2 coord){

				vec3 blur = vec3(0.0);

				float bloomStrength = 0.0;
				const float bloomTileSize = BLOOM_TILE_SIZE;

				vec3 blur1=GetBloomTile(1.0 + bloomTileSize, coord, vec2(0.0      , 0.0   ));
				vec3 blur2=GetBloomTile(2.0 + bloomTileSize, coord, vec2(0.51     , 0.0   ));
				vec3 blur3=GetBloomTile(3.0 + bloomTileSize, coord, vec2(0.51     , 0.26  ));
				vec3 blur4=GetBloomTile(4.0 + bloomTileSize, coord, vec2(0.645    , 0.26  ));
				vec3 blur5=GetBloomTile(5.0 + bloomTileSize, coord, vec2(0.7175   , 0.26  ));
				vec3 blur6=GetBloomTile(6.0 + bloomTileSize, coord, vec2(0.645    , 0.3325));
				vec3 blur7=GetBloomTile(7.0 + bloomTileSize, coord, vec2(0.670625 , 0.3325));

			#ifdef DIRTY_LENS
				float newAspectRatio =1.777777777777778 / aspectRatio;
				vec2  scale          =vec2(max(newAspectRatio, 1.0), max(1.0 / newAspectRatio, 1.0));
				float dirt           =texture2D(depthtex2, (coord - 0.5) / scale + 0.5).r;
				      dirt          *=length(blur6 / (1.0 + blur6)) * DIRTY_LENS_STRENGTH;
				      blur3 *= dirt *  2.0 + 1.0;
					  blur4 *= dirt *  4.0 + 1.0;
					  blur5 *= dirt *  8.0 + 1.0;
					  blur6 *= dirt * 16.0 + 1.0;
					  blur7 *= dirt * 32.0 + 1.0;
			#endif

			#if BLOOM_RADIUS == 1
				blur = blur1 * 0.667;
			#elif BLOOM_RADIUS == 2
				blur = (blur1 + blur2) * 0.37;
			#elif BLOOM_RADIUS == 3
				 blur = (blur1 + blur2 + blur3) * 0.27;
			#elif BLOOM_RADIUS == 4
				blur = (blur1 + blur2 + blur3 + blur4) * 0.212;
			#elif BLOOM_RADIUS == 5
				blur = (blur1 + blur2 + blur3 + blur4 + blur5) * 0.175;
			#elif BLOOM_RADIUS == 6
				blur = (blur1 + blur2 + blur3 + blur4 + blur5 + blur6) * 0.151;
			#elif BLOOM_RADIUS == 7
				blur = (blur1 + blur2 + blur3 + blur4 + blur5 + blur6 + blur7) * 0.137;
			#endif

			vec3 dirtblur = (blur1 + blur2 + blur3 + blur4 + blur5 + blur6 + blur7) * 0.137;

		#ifndef DIRTY_LENS
			blur = mix(blur, dirtblur, 0.0);
		#else
			blur = mix(blur, dirtblur, dirt);
		#endif

		#ifdef BLOOM
				bloomStrength = BLOOM_MULTIPLIER;

			#if defined (OVERWORLD)
				bloomStrength *= OVERWORLD_BLOOM_STRENGTH;
			#elif defined (NETHER)
				bloomStrength *= NETHER_BLOOM_STRENGTH;
			#elif defined (END)
				bloomStrength *= END_BLOOM_STRENGTH;
			#endif

		#endif

		#if (defined BLOCK_BLOCK_LIGHT_JITTER && defined BLOOM_JITTER && defined BLOOM)

			const float speed = JITTER_SPEED;
				  float t = frameTimeCounter * 1.4 * speed;
				  float jitter1 = 1.0 - sin(t + cos(t * 2.0) - sin(t * 3.0)) * JITTER_STRENGTH1;
				  float jitter2 = 1.0 - sin(t + cos(t * 0.5) - sin(t * 2.25)) * JITTER_STRENGTH2;
				  float jitter3 = 1.0 - sin(t + cos(t * 0.25) - sin(t * 1.25)) * JITTER_STRENGTH3;
			bloomStrength *= jitter1 * jitter2 * jitter3;
		#endif

		#if (defined ENABLE_DARKNESS_EFFECT && MC_VERSION >= 11900)
			bloomStrength = mix(bloomStrength, WARDEN_BLOOM_MULTIPLIER, darknessFactor);
		#endif

		#if (defined BLOOM_START && defined BLOOM)
			float animate = 0.0;

			#ifdef ANIM_MOVE
				animate = min(starter, 0.1) * 10.0;
			#endif

			bloomStrength *= BLOOM_S_STRENGTH;
			bloomStrength = mix(min(bloomStrength, 5.0), 1.0, animate);

		#endif

		#if (defined UNDERWATER_BLUR_BASED_BLOOM && defined BLOOM)
			float underwaterBloomStrength = UNDERWATER_BLUR_BASED_B_BLOOM_STRENGTH;
			float underwaterBlurStrength  = UNDERWATER_BLUR_BASED_B_BLUR_STRENGTH;

		if (isEyeInWater == 1) 	color = mix(color, blur, underwaterBlurStrength) * underwaterBloomStrength;
		#endif

		if (isEyeInWater == 3) 	color = mix(color, blur, 0.5 * bloomStrength);

		color = mix(color, blur, 0.2 * bloomStrength);
	}

	void AutoExposure(inout vec3 color, inout float exposure, float tempExposure) {
	float exposureLod = log2(viewWidth * 0.4);

	exposure = length(textureLod(colortex0, vec2(0.5), exposureLod).rgb);
	exposure = clamp(exposure, 0.0001, 10.0);

	color /= 2.2 * clamp(tempExposure, 0.2, 10.0) + 0.125;
	}

	void ColorGrading(inout vec3 color) {
		vec3 powColor = pow(color, vec3(2.2));
		vec3 cgColor = vec3(powColor.r, powColor.g, powColor.b);
		vec3 cgMin = vec3(pow(vec3(CG_RM, CG_GM, CG_BM) / 255.0, vec3(2.2)));
		vec3 cgTint = vec3(pow(vec3(CG_TR, CG_TG, CG_TB) / 255.0, vec3(2.2))) * GetLuminance(color) * CG_TI;

		cgColor = vec3(pow(cgColor.r, CG_RC) * vec3(CG_RR, CG_RG, CG_RB) / 65025.0 +
			pow(cgColor.g, CG_GC) * vec3(CG_GR, CG_GG, CG_GB) / 65025.0 +
			pow(cgColor.b, CG_BC) * vec3(CG_BR, CG_BG, CG_BB) / 65025.0);

		color = mix(color * (1.0 - cgMin) + cgMin, vec3(CG_RI, CG_GI, CG_BI) * cgColor, CG_TM);
	}

	void BSLTonemap(inout vec3 color) {
		color = color * exp2(TONEMAP_EXPOSURE + 2.0);
		vec3 numerator = pow(color, vec3(TONEMAP_WHITE_CURVE)) + 1.0;
		color /= pow(numerator, vec3(1.0 / TONEMAP_WHITE_CURVE));
		vec3 mixFactors = mix(vec3(TONEMAP_LOWER_CURVE), vec3(TONEMAP_UPPER_CURVE), sqrt(color));
		color = pow(color, mixFactors);
	}

	vec3 Aces_Approx(vec3 v) {
	v = max(v, 0.0);
	v *= 0.6f;
	const float a = 2.51;
	const float b = 0.03;
	const float c = 2.43;
	const float d = 0.59;
	const float e = 0.14;
	return clamp((v * (a * v + b)) / (v * (c * v + d) + e), 0.0, 1.0);
	}

	void ColorSaturation(inout vec3 color) {
		float grayVibrance = dot(vec3(0.333), color);
		float graySaturation = grayVibrance;

		if (SATURATION < 1.00) {
			graySaturation = GetLuminance(color);
		}

		float mn = min(min(color.r, color.g), color.b);
		float mx = max(max(color.r, color.g), color.b);
		float d = mx - mn;
		float sat = d / mx * (1.0 - mx) * grayVibrance * 5.0;
		vec3 lightness = vec3((mn + mx) * 0.5);

		color = mix(color, mix(color, lightness, 1.0 - VIBRANCE), sat);
		float mixVal = (1.0 - lightness.r) * (2.0 - VIBRANCE) * 0.5 * abs(VIBRANCE - 1.0);
		color = mix(color, lightness, mixVal);
		color = color * SATURATION - graySaturation * (SATURATION - 1.0);
	}


	/*////////////////////////////////////////////////////////////////////////
	//////////////////////////////BOTW_MAPPING///////////////////////////////
	//////////////////////////////////////////////////////////////////////*/

	#if (defined BOTW && defined OVERWORLD)
	vec3 BOTWTonemap(vec3 color){
		color=pow(color, vec3(1.0 * 0.833));

		float mixrgb=(color.r + color.g + color.b) * 0.285714;
		float weight=BOTWSAT + (1.0 - pow(1.0 - 1.0 * mixrgb, 2.0)) * 0.08;

		return mix(vec3(mixrgb), color, weight);
	}
	#endif

	/*//////////////////////////////////////////////////////////////////////////
	/////////////////////////////////HDR///////////////////////////////////////
	////////////////////////////////////////////////////////////////////////*/

	#ifdef HDR
	void LexHDR(inout vec3 color){

		vec3 ExpositionGlobal=color * GLOBAL_EXPOSITION;

		vec3 SousExposition=color / SOUS_EXPOSITION;

		color=mix(SousExposition, ExpositionGlobal, color);
	}
	#endif

	/*//////////////////////////////////////////////////////////////////////////
	/////////////////////////////////LENS_FLARE////////////////////////////////
	////////////////////////////////////////////////////////////////////////*/

	#if (defined LENS_FLARE && defined OVERWORLD)
		vec2 GetLightPos(){
			vec4 tpos     =gbufferProjection * vec4(sunPosition, 1.0);
				 tpos.xyz/=tpos.w;
			return tpos.xy / tpos.z * 0.5;
		}
	#endif

	//Includes//
	#include "/lib/color/lightColor.glsl"

	#if (defined LENS_FLARE && defined OVERWORLD)
		#include "/lib/post/lensFlare.glsl"
	#endif

	//Program//
	void main(){

		vec2 filmGrainCoord = texCoord * vec2(viewWidth, viewHeight) * 0.001953125;
		vec3 filmGrain = texture2D(noisetex, filmGrainCoord).rgb;

		#ifdef FILM_GRAINS
			vec2 uv = vec2(viewWidth, viewHeight);
			vec2 vUV = gl_FragCoord.xy / uv;
		#endif

		vec4 color = texelFetch(colortex0, texelCoord, 0).rgba;

		#ifdef AUTO_EXPOSURE
			float tempExposure = texture2D(colortex2, vec2(pw, ph)).r;
		#endif

		#if (defined LENS_FLARE && defined OVERWORLD)
			float tempVisibleSun = texture2D(colortex2, vec2(3.0 * pw, ph)).r;
		#endif

		vec3 temporalColor = vec3(0.0);

		#if AA > 1
			temporalColor = texelFetch(colortex2, texelCoord, 0).gba;
		#endif

		vec2 newTexCoord = texCoord;

		#ifdef BLOOM
			Bloom(color.rgb, newTexCoord);
		#endif

		#ifdef AUTO_EXPOSURE
			float exposure = 1.0;
			AutoExposure(color.rgb, exposure, tempExposure);
		#endif

		#ifdef COLOR_GRADING
			ColorGrading(color.rgb);
		#endif

		BSLTonemap(color.rgb);

		#if defined (OVERWORLD)
			#if ACES_APPROX_OVERWORLD == 1
				color.rgb = Aces_Approx(color.rgb);
			#elif ACES_APPROX_OVERWORLD == 2
				color.rgb = mix(color.rgb, Aces_Approx(color.rgb), moonVisibility);
			#elif ACES_APPROX_OVERWORLD == 3
				color.rgb = mix(color.rgb, Aces_Approx(color.rgb), sunVisibility);
			#endif
		#elif defined (NETHER)
			#if ACES_APPROX_NETHER == 1
				color.rgb = Aces_Approx(color.rgb);
			#endif
		#elif defined (END)
			#if ACES_APPROX_ENDER == 1
				color.rgb = Aces_Approx(color.rgb);
			#endif
		#endif

		#if (defined BOTW && defined OVERWORLD)
			color.rgb = clamp01(BOTWTonemap(color.rgb));
		#endif

		#ifdef HDR
			LexHDR(color.rgb);
		#endif

		#if (defined LENS_FLARE && defined OVERWORLD)
			vec2 lightPos = GetLightPos();
			float truePos = sign(sunVec.z);
			float moonPhaseOffsetLensFlare = 1.0;

			#ifdef NEWMOON_DISABLER_STUFF
				moonPhaseOffsetLensFlare = 1.0 - (float((moonPhase == 4)) * (1.0 - sunVisibility));
			#endif

			float visibleSun = float(texture2D(depthtex0, lightPos + 0.5).r >= 1.0);
				  visibleSun *= max(1.0 - isEyeInWater, eBS) * (1.0 - blindFactor) * (1.0 - rainStrengthS);

			#ifdef UNDERGROUND_SKY
				visibleSun *= 1.0 - isEyeInCave;
			#endif

			#if (defined ENABLE_DARKNESS_EFFECT && MC_VERSION >= 11900)
					visibleSun *= 1.0 - darknessFactor;
			#endif

			float multiplier=0.0;

			multiplier = mix(mix(multiplier, tempVisibleSun * LENS_FLARE_STRENGTH_NIGHT * 0.6, 1.0) * moonPhaseOffsetLensFlare,
							 mix(multiplier, tempVisibleSun * LENS_FLARE_STRENGTH_DAY * 0.5, 1.0),
							 sunVisibility);

			if (multiplier > 0.001) LensFlare(color.rgb, lightPos, truePos, multiplier);
		#endif

		float temporalData=0.0;

		#ifdef AUTO_EXPOSURE
			if (texCoord.x < 2.0 * pw && texCoord.y < 2.0 * ph)
				temporalData=mix(tempExposure, sqrt(exposure), 0.016);
		#endif

		#if (defined LENS_FLARE && defined OVERWORLD)
			if (texCoord.x > 2.0 * pw && texCoord.x < 4.0 * pw && texCoord.y < 2.0 * ph)
				temporalData=mix(tempVisibleSun, visibleSun, 0.1);
		#endif

		#ifdef BSL_VIGNETTE
			color.rgb*=1.0 - length(texCoord.xy - 0.5) * FORCE_BSL_VIGNETTE * (1.0 - GetLuminance(color.rgb));
		#endif

		color.rgb = pow(color.rgb, vec3(1.0 / 2.2));

		ColorSaturation(color.rgb);

		#ifdef FILM_GRAINS
			color.rgb = applyGrain(vUV, color.rgb, FORCE_DU_GRAIN);
		#endif

		color.rgb += (filmGrain - 0.25) * 0.0078125;

		/*DRAWBUFFERS:12*/
		gl_FragData[0] = clamp01(color);
		gl_FragData[1] = vec4(temporalData, temporalColor);
	}

#endif

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

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

		gl_Position=ftransform();

		upVec=normalize(gbufferModelView[1].xyz);
		vec2 sunRotationData = getSunRotationData();
		vec3 usunvec = getUsunvec(sunRotationData);
		sunVec = getSunVec(usunvec, sunRotationData);
	}

#endif