/*
BSL Shaders v8 Series by Capt Tatsu
https://bitslablab.com
*/

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

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

//Varyings//
varying vec2 texCoord;

//Uniforms//
uniform sampler2D colortex1;

uniform float viewWidth, viewHeight;
uniform float aspectRatio, frameTimeCounter;

#if defined SKETCH_MODE || defined THERMAL_VISION
uniform sampler2D depthtex0;
#endif

#if defined FILM_GRAIN || defined RAIN_ON_LENS || defined OLD_FILM || defined GLITCH_EFFECT
uniform sampler2D noisetex;
#endif

#ifdef RAIN_ON_LENS
uniform float rainStrength;
#endif


//Optifine Constants//
/*
const int colortex0Format = R11F_G11F_B10F; //main scene
const int colortex1Format = RGBA16; //raw translucent, vl, bloom, final scene
const int colortex2Format = RGBA16; //temporal data
const int colortex3Format = RGB8; //smoothness, sky occlusion, entity mask
const int gaux1Format = R8; //cloud alpha, ao
const int gaux2Format = RGB10_A2; //reflection image
const int gaux3Format = RGBA16; //opaque normals, refraction vector
const int gaux4Format = RGBA16; //fresnel
const int colortex8Format = RGB8; //colored light
const int colortex9Format = RGB16F; //colored light
*/

const bool shadowHardwareFiltering = true;
const float shadowDistanceRenderMul = 1.0;

const int noiseTextureResolution = 512;

const float drynessHalflife = 5.0;
const float wetnessHalflife = 300.0;

//Common Functions//
#ifdef TAA
vec2 sharpenOffsets[4] = vec2[4](
	vec2( 1.0,  0.0),
	vec2( 0.0,  1.0),
	vec2(-1.0,  0.0),
	vec2( 0.0, -1.0)
);

void SharpenFilter(inout vec3 color, vec2 coord) {
	float mult = MC_RENDER_QUALITY * 0.0625;
	vec2 view = 1.0 / vec2(viewWidth, viewHeight);

	vec3 mincolor = color;
	vec3 maxcolor = color;

	color *= mult * 4.0 + 1.0;

	for(int i = 0; i < 4; i++) {
		vec2 offset = sharpenOffsets[i] * view;
		vec3 colorSample = texture2DLod(colortex1, coord + offset, 0).rgb;
		color -= colorSample * mult;

		mincolor = min(mincolor, colorSample);
		maxcolor = max(maxcolor, colorSample);
	}

	color = clamp(color, mincolor, maxcolor);
}
#endif

#ifdef PSYCHEDELIC
vec3 rgb2hsv(vec3 c) {
	vec4 K = vec4(0.0, -1.0/3.0, 2.0/3.0, -1.0);
	vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
	vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
	float d = q.x - min(q.w, q.y);
	float e = 1.0e-10;
	return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

vec3 hsv2rgb(vec3 c) {
	vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0);
	vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
	return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
#endif

#ifdef SHADOW
#endif

//Program//
void main() {
    vec2 newTexCoord = texCoord;

	#ifdef RETRO_FILTER
    vec2 view = vec2(viewWidth, viewHeight) / float(RETRO_FILTER_SIZE);
	float offset = (ceil(RETRO_FILTER_SIZE * 0.5) - 0.5) / float(RETRO_FILTER_SIZE);
	newTexCoord = (floor(newTexCoord * view) + offset) / view;
	#endif

	#ifdef CRT_MONITOR
	// Barrel distortion
	vec2 crtCoord = newTexCoord - 0.5;
	float crtDist = dot(crtCoord, crtCoord);
	crtCoord *= 1.0 + crtDist * 0.3 + crtDist * crtDist * 0.15;
	newTexCoord = crtCoord + 0.5;
	// Black out-of-bounds areas
	vec2 crtClamp = step(vec2(0.0), newTexCoord) * step(newTexCoord, vec2(1.0));
	float crtMask = crtClamp.x * crtClamp.y;
	#endif

	vec3 color = texture2DLod(colortex1, newTexCoord, 0).rgb;

	#ifdef CRT_MONITOR
	color *= crtMask;
	// Scanlines
	float scanline = 0.75 + 0.25 * sin(gl_FragCoord.y * 3.14159);
	color *= scanline;
	// RGB phosphor subpixels
	float pixel = mod(gl_FragCoord.x, 3.0);
	vec3 phosphor = vec3(
		step(pixel, 1.0),
		step(1.0, pixel) * step(pixel, 2.0),
		step(2.0, pixel)
	);
	color *= mix(vec3(1.0), phosphor * 1.5 + 0.5, 0.3);
	// Edge vignette
	float crtVig = 1.0 - crtDist * 2.0;
	color *= clamp(crtVig, 0.0, 1.0);
	#endif

	#ifdef FILM_GRAIN
	vec2 grainCoord = gl_FragCoord.xy / 512.0 + vec2(frameTimeCounter * 0.7, frameTimeCounter * 0.3);
	float grain = texture2D(noisetex, grainCoord).r;
	color += (grain - 0.5) * FILM_GRAIN_STRENGTH;
	#endif

	#if CHROMATIC_ABERRATION > 0
	float caStrength = 0.004 * CHROMATIC_ABERRATION;
	vec2 caScale = vec2(1.0 / aspectRatio, 1.0);
	color *= vec3(0.0,1.0,0.0);
	color += texture2DLod(colortex1, mix(newTexCoord, vec2(0.5), caScale * -caStrength), 0).rgb * vec3(1.0,0.0,0.0);
	color += texture2DLod(colortex1, mix(newTexCoord, vec2(0.5), caScale * -caStrength * 0.5), 0).rgb * vec3(0.5,0.5,0.0);
	color += texture2DLod(colortex1, mix(newTexCoord, vec2(0.5), caScale * caStrength * 0.5), 0).rgb * vec3(0.0,0.5,0.5);
	color += texture2DLod(colortex1, mix(newTexCoord, vec2(0.5), caScale* caStrength), 0).rgb * vec3(0.0,0.0,1.0);

	color /= vec3(1.5,2.0,1.5);
	#endif

	#if defined FXAA && defined TAA
	SharpenFilter(color, newTexCoord);
	#endif

	#ifdef PSYCHEDELIC
	vec3 hsv = rgb2hsv(color);
	hsv.x = fract(hsv.x + frameTimeCounter * PSYCHEDELIC_SPEED);
	color = hsv2rgb(hsv);
	#endif

	#ifdef THERMAL_VISION
	float heat = dot(color, vec3(0.2126, 0.7152, 0.0722));
	heat = clamp(heat, 0.0, 1.0);
	vec3 thermal;
	if (heat < 0.25) {
		thermal = mix(vec3(0.0), vec3(0.0, 0.0, 1.0), heat * 4.0);
	} else if (heat < 0.5) {
		thermal = mix(vec3(0.0, 0.0, 1.0), vec3(1.0, 0.0, 0.0), (heat - 0.25) * 4.0);
	} else if (heat < 0.75) {
		thermal = mix(vec3(1.0, 0.0, 0.0), vec3(1.0, 1.0, 0.0), (heat - 0.5) * 4.0);
	} else {
		thermal = mix(vec3(1.0, 1.0, 0.0), vec3(1.0), (heat - 0.75) * 4.0);
	}
	color = thermal;
	#endif

	#ifdef SKETCH_MODE
	// Desaturate to grayscale
	float lum = dot(color, vec3(0.2126, 0.7152, 0.0722));

	// Edge detection from depth buffer
	float px = 1.0 / viewWidth;
	float py = 1.0 / viewHeight;
	float depthC = texture2D(depthtex0, newTexCoord).r;
	float depthL = texture2D(depthtex0, newTexCoord + vec2(-px, 0.0)).r;
	float depthR = texture2D(depthtex0, newTexCoord + vec2( px, 0.0)).r;
	float depthU = texture2D(depthtex0, newTexCoord + vec2(0.0, -py)).r;
	float depthD = texture2D(depthtex0, newTexCoord + vec2(0.0,  py)).r;
	float edge = abs(depthL - depthR) + abs(depthU - depthD);
	edge = smoothstep(0.0, 0.002, edge);

	// Cross-hatching based on darkness
	float hatch1 = step(0.5, fract(gl_FragCoord.x * 0.5 + gl_FragCoord.y * 0.5));
	float hatch2 = step(0.5, fract(gl_FragCoord.x * 0.5 - gl_FragCoord.y * 0.5));
	float hatching = 1.0;
	if (lum < 0.3) hatching *= mix(0.4, 1.0, hatch1 * hatch2);
	if (lum < 0.5) hatching *= mix(0.6, 1.0, hatch1);
	if (lum < 0.7) hatching *= mix(0.8, 1.0, hatch2);

	// Paper-white background with ink
	vec3 paper = vec3(0.95, 0.92, 0.88);
	vec3 ink = paper * lum * hatching;
	ink = mix(ink, vec3(0.05), edge);
	color = ink;
	#endif

	#ifdef NEGATIVE_WORLD
	color = vec3(1.0) - color;
	#endif

	#ifdef RAIN_ON_LENS
	if (rainStrength > 0.01) {
		// Large slow drops
		vec2 dropUV1 = texCoord * 4.0;
		dropUV1.y -= frameTimeCounter * 0.12;
		float drop1 = texture2D(noisetex, dropUV1).r;
		drop1 = pow(smoothstep(0.55, 0.7, drop1), 2.0);

		// Small fast drops
		vec2 dropUV2 = texCoord * 9.0 + 0.3;
		dropUV2.y -= frameTimeCounter * 0.35;
		float drop2 = texture2D(noisetex, dropUV2).g;
		drop2 = pow(smoothstep(0.58, 0.72, drop2), 2.0);

		// Streaks sliding down
		vec2 streakUV = vec2(texCoord.x * 15.0, texCoord.y * 2.0);
		streakUV.y -= frameTimeCounter * 0.7;
		float streak = texture2D(noisetex, streakUV).b;
		streak = smoothstep(0.82, 0.92, streak);

		float drops = (drop1 + drop2 * 0.7 + streak * 0.5) * rainStrength;

		// Refract image through droplets
		vec2 dropOffset = vec2(drop1 * 0.02 - drop2 * 0.015, drop1 * 0.015) * rainStrength;
		vec3 refracted = texture2DLod(colortex1, newTexCoord + dropOffset, 0).rgb;
		color = mix(color, refracted, clamp(drops * 0.7, 0.0, 1.0));
		// Specular highlight on drops
		color += drops * 0.2;
	}
	#endif

	#ifdef OLD_FILM
	{
		float luma = dot(color, vec3(0.299, 0.587, 0.114));
		vec3 sepia = vec3(luma * 1.2, luma * 1.0, luma * 0.7);
		color = mix(color, sepia, 0.85);
		float scratch = texture2D(noisetex, vec2(texCoord.x * 200.0 + floor(frameTimeCounter * 12.0) * 0.37, 0.5) / 512.0).r;
		scratch = smoothstep(0.985, 1.0, scratch);
		color += scratch * 0.4;
		float flicker = 1.0 + 0.06 * sin(frameTimeCounter * 17.3) * sin(frameTimeCounter * 31.7);
		color *= flicker;
		vec2 vigUV = texCoord * 2.0 - 1.0;
		float vig = 1.0 - dot(vigUV * 0.7, vigUV * 0.7);
		color *= clamp(vig, 0.0, 1.0);
		float grain = texture2D(noisetex, gl_FragCoord.xy / 512.0 + fract(frameTimeCounter * 3.7)).r;
		color += (grain - 0.5) * 0.08;
	}
	#endif

	#ifdef GLITCH_EFFECT
	{
		float time = frameTimeCounter;
		// Intermittent bursts using time-varying noise
		float burstNoise = sin(floor(time * 3.0) * 437.585453) * 0.5 + 0.5;
		float glitchActive = step(0.6, burstNoise);
		float intensity = GLITCH_INTENSITY * glitchActive;

		// RGB channel split
		float splitAmount = intensity * 0.025;

		// Scanline displacement - horizontal bands that shift
		float bandNoise = sin(floor(texCoord.y * 40.0 + time * 8.0) * 173.7) * 0.5 + 0.5;
		float displaceAmount = step(0.85, bandNoise) * intensity * 0.06;

		vec3 glitchColor;
		glitchColor.r = texture2DLod(colortex1, newTexCoord + vec2(displaceAmount + splitAmount, 0.0), 0).r;
		glitchColor.g = texture2DLod(colortex1, newTexCoord + vec2(displaceAmount, 0.0), 0).g;
		glitchColor.b = texture2DLod(colortex1, newTexCoord + vec2(displaceAmount - splitAmount, 0.0), 0).b;

		// Block artifacts
		vec2 blockCoord = floor(texCoord * vec2(16.0, 12.0));
		float blockNoise = sin(dot(blockCoord, vec2(12.9898, 78.233)) + floor(time * 6.0)) * 0.5 + 0.5;
		float blockGlitch = step(0.92, blockNoise) * glitchActive;
		vec2 blockOffset = vec2(blockGlitch * 0.1, 0.0);
		vec3 blockColor = texture2DLod(colortex1, newTexCoord + blockOffset, 0).rgb;

		color = mix(color, glitchColor, clamp(intensity + 0.3 * glitchActive, 0.0, 1.0));
		color = mix(color, blockColor, blockGlitch * 0.8);
	}
	#endif

	gl_FragColor = vec4(color, 1.0);
}

#endif

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

//Varyings//
varying vec2 texCoord;

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

	gl_Position = ftransform();
}

#endif
