/*
Complementary Shaders by EminGT, based on BSL Shaders by Capt Tatsu
*/

//Common//
#include "/lib/common.glsl"

//Varyings//
varying float vanillaStars;

varying vec3 upVec, sunVec;

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

//Uniforms//
uniform int isEyeInWater;
uniform int worldDay;
uniform int moonPhase;
#define UNIFORM_moonPhase

uniform float isEyeInCave;
uniform float blindFactor;
uniform float frameCounter;
uniform float frameTimeCounter;
uniform float nightVision;
uniform float rainStrength;
uniform float rainStrengthS;
uniform float screenBrightness; 
uniform float viewWidth, viewHeight;
uniform float eyeAltitude;

uniform ivec2 eyeBrightnessSmooth;

uniform vec3 cameraPosition;
uniform vec3 skyColor;
uniform vec3 fogColor;
uniform vec3 moonPosition;

uniform mat4 gbufferModelView;
uniform mat4 gbufferModelViewInverse;
uniform mat4 gbufferProjectionInverse;

uniform sampler2D noisetex;

#ifdef GALAXIES
	uniform sampler2D gaux4;
#endif

#ifdef AURORA
	uniform float isDry, isRainy, isSnowy;
#endif

#if MC_VERSION >= 11900
	uniform float darknessFactor;
#endif

//Common Variables//
#if WORLD_TIME_ANIMATION >= 1
	int modifiedWorldDay = int(mod(worldDay, 100.0) + 5.0);
	float frametime = (worldTime + modifiedWorldDay * 24000) * 0.05 * ANIMATION_SPEED;
#else
	float frametime = frameTimeCounter * ANIMATION_SPEED;
#endif

float eBS = eyeBrightnessSmooth.y / 240.0;
float sunVisibility = clamp(dot( sunVec,upVec) + 0.0625, 0.0, 0.125) * 8.0;
float vsBrightness = clamp(screenBrightness, 0.0, 1.0);

vec3 lightVec = sunVec * (1.0 - 2.0 * float(timeAngle > 0.5325 && timeAngle < 0.9675));

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

float MoonSoftSpot(vec2 uv, vec2 center, float radius) {
	float distanceToSpot = length(uv - center);
	return 1.0 - smoothstep(radius * 0.28, radius, distanceToSpot);
}

float MoonSoftRim(vec2 uv, vec2 center, float radius) {
	float distanceToRim = length(uv - center);
	return smoothstep(radius * 0.58, radius * 0.82, distanceToRim)
			 * (1.0 - smoothstep(radius * 0.82, radius * 1.08, distanceToRim));
}

float MoonSurfaceDetail(vec2 uv) {
	float maria = 0.52 * MoonSoftSpot(uv, vec2(-0.24,  0.14), 0.31)
				+ 0.32 * MoonSoftSpot(uv, vec2( 0.28, -0.18), 0.22)
				+ 0.20 * MoonSoftSpot(uv, vec2( 0.08,  0.42), 0.15)
				+ 0.15 * MoonSoftSpot(uv, vec2(-0.48, -0.30), 0.13);
	float craterRims = MoonSoftRim(uv, vec2( 0.34,  0.25), 0.105)
					 + MoonSoftRim(uv, vec2(-0.10, -0.36), 0.080)
					 + MoonSoftRim(uv, vec2(-0.48,  0.39), 0.060);
	return clamp(1.0 - 0.12 * maria + 0.025 * craterRims, 0.84, 1.03);
}

vec3 RoundSunMoon(vec3 nViewPos, vec3 sunColor, vec3 moonColor, float NdotU, float cosS) {
	float isMoon = float(cosS < 0.0);
	float sun = pow(abs(cosS), 2600.0 * isMoon + 1800.0 * (1 - isMoon));

	if (isMoon > 0.0) {
		vec3 moonDirection = normalize(-sunVec);
		// Blend smoothly toward the transformed world-X axis near the zenith.
		// It is orthogonal to world-up, so the tangent cannot collapse or jump.
		vec3 moonWorldRight = normalize(gbufferModelView[0].xyz);
		float zenithBlend = smoothstep(0.94, 0.995,
								  abs(dot(upVec, moonDirection)));
		vec3 moonReference = normalize(mix(upVec, moonWorldRight, zenithBlend));
		vec3 moonRight = normalize(cross(moonReference, moonDirection));
		vec3 moonUp = normalize(cross(moonDirection, moonRight));
		vec2 moonUv = vec2(dot(nViewPos, moonRight), dot(nViewPos, moonUp)) * 28.5;
		float moonRadius = length(moonUv);
		float limbAA = max(fwidth(moonRadius) * 1.25, 0.006);
		float moonLimb = 1.0 - smoothstep(1.0 - limbAA,
										 1.0 + limbAA, moonRadius);
		float limbLighting = sqrt(clamp(1.0 - moonRadius * moonRadius,
										 0.0, 1.0));
		float naturalMoonProfile = moonLimb * mix(0.78, 1.0, limbLighting);
		// Keep most of the familiar round moon, but add a soft limb, broad maria
		// and a few understated crater rims. Everything is analytic and smooth.
		sun = mix(sun, naturalMoonProfile, 0.32);
		sun *= MoonSurfaceDetail(moonUv);
		sun += pow(abs(cosS), 420.0) * 0.018;

		if (moonPhase >= 1) {
			float moonPhaseOffset = float(!(moonPhase == 4));
			if (moonPhase > 4) moonPhaseOffset *= -1.0;

			float ang = fract(timeAngle - (0.25 + 0.0035 * moonPhaseOffset));
			ang = (ang + (cos(ang * 3.14159265358979) * -0.5 + 0.5 - ang) / 3.0) * 6.28318530717959;
			vec2 sunRotationData2 = vec2(cos(sunPathRotation * 0.01745329251994), -sin(sunPathRotation * 0.01745329251994));
			vec3 rawSunVec2 = (gbufferModelView * vec4(vec3(-sin(ang), cos(ang) * sunRotationData2) * 2000.0, 1.0)).xyz;
		
			float moonPhaseCosS = dot(nViewPos, normalize(rawSunVec2.xyz));
			moonPhaseCosS = pow(abs(moonPhaseCosS), 750.0);
			sun = mix(sun, 0.0, min(moonPhaseCosS * 2.0, 1.0));
		}
	}

	#ifdef SUN_MOON_HORIZON
		float horizonFactor = smoothstep(-0.012, 0.035, NdotU);
		sun *= horizonFactor;
		moonColor *= 1.0 - sunVisibility;
		sunColor *= sunVisibility;
	#endif

	vec3 sunMoonCol = mix(moonColor, sunColor, float(cosS > 0.0));

	vec3 finalSunMoon = sun * sunMoonCol * 32.0;
	finalSunMoon = pow(finalSunMoon, vec3(2.0 - min(finalSunMoon.r + finalSunMoon.g + finalSunMoon.b, 1.0)));

	return finalSunMoon;
}

float GetStarNoise(vec2 pos) {
	return fract(sin(dot(pos, vec2(12.9898, 4.1414))) * 43758.54953);
}

float EventHorizonStarHash(vec3 p) {
	p = fract(p * 0.1031);
	p += dot(p, p.yzx + 33.33);
	return fract((p.x + p.y) * p.z);
}

float EventHorizonSkyNoise3D(vec3 p) {
	// Continuous quintic value noise. The former triplanar 512x512 white-noise
	// lookup exposed thousands of source texels and produced a sandy pattern in
	// the night sky. Eight smooth 3D lattice values retain detail without seams.
	vec3 cell = floor(p) + vec3(37.0, 17.0, 53.0);
	vec3 local = fract(p);
	local = local * local * local
			* (local * (local * 6.0 - 15.0) + 10.0);
	float n000 = EventHorizonStarHash(cell + vec3(0.0, 0.0, 0.0));
	float n100 = EventHorizonStarHash(cell + vec3(1.0, 0.0, 0.0));
	float n010 = EventHorizonStarHash(cell + vec3(0.0, 1.0, 0.0));
	float n110 = EventHorizonStarHash(cell + vec3(1.0, 1.0, 0.0));
	float n001 = EventHorizonStarHash(cell + vec3(0.0, 0.0, 1.0));
	float n101 = EventHorizonStarHash(cell + vec3(1.0, 0.0, 1.0));
	float n011 = EventHorizonStarHash(cell + vec3(0.0, 1.0, 1.0));
	float n111 = EventHorizonStarHash(cell + vec3(1.0, 1.0, 1.0));
	float lower = mix(mix(n000, n100, local.x),
					  mix(n010, n110, local.x), local.y);
	float upper = mix(mix(n001, n101, local.x),
					  mix(n011, n111, local.x), local.y);
	return mix(lower, upper, local.z);
}

vec3 EventHorizonStarLayer(vec3 direction, float scale, float threshold,
							float radius, float animationTime) {
	vec3 p = direction * scale;
	vec3 cell = floor(p);
	vec3 local = fract(p) - 0.5;
	float seed = EventHorizonStarHash(cell + vec3(scale * 0.013));
	if (seed < threshold) return vec3(0.0);

	vec3 offset = vec3(
		EventHorizonStarHash(cell + 3.1),
		EventHorizonStarHash(cell + 7.7),
		EventHorizonStarHash(cell + 13.9)
	) - 0.5;
	offset *= 0.62;

	float distanceToStar = length(local - offset);
	float footprint = clamp(max(length(dFdx(p)), length(dFdy(p))) * 0.16,
							  0.006, 0.16);
	float core = 1.0 - smoothstep(radius, radius + footprint, distanceToStar);
	float glow = exp(-distanceToStar * distanceToStar * 38.0) * 0.10;
	float energy = pow((seed - threshold) / max(1.0 - threshold, 0.0001), 2.4);

	float temperature = EventHorizonStarHash(cell + 21.4);
	vec3 warmStar = vec3(1.08, 0.92, 0.79);
	vec3 neutralStar = vec3(1.00, 0.985, 0.955);
	vec3 blueStar = vec3(0.84, 0.92, 1.10);
	vec3 starColor = mix(warmStar, neutralStar, smoothstep(0.08, 0.48, temperature));
	starColor = mix(starColor, blueStar, smoothstep(0.60, 0.96, temperature));
	starColor = mix(vec3(GetLuminance(starColor)), starColor,
					clamp(0.62 * STAR_COLOR_VARIATION, 0.0, 1.0));

	float twinkleSpeed = mix(0.7, 2.4, EventHorizonStarHash(cell + 34.2));
	float horizonTwinkle = 1.0 - clamp(abs(direction.y), 0.0, 1.0);
	float twinkleAmount = mix(0.035, 0.10,
			horizonTwinkle * horizonTwinkle);
	float twinkle = 1.0 + twinkleAmount * STAR_TWINKLE
			* sin(animationTime * twinkleSpeed + seed * 37.0);

	float rareStar = smoothstep(0.9965, 1.0, seed);
	float spikeX = exp(-abs(local.x - offset.x) * 95.0)
				 * exp(-abs(local.y - offset.y) * 12.0);
	float spikeY = exp(-abs(local.y - offset.y) * 95.0)
				 * exp(-abs(local.x - offset.x) * 12.0);
	float diffraction = (spikeX + spikeY) * rareStar * 0.22;

	return starColor * (core + glow + diffraction) * energy * twinkle;
}

vec3 GetGalaxy(vec3 viewPos, float NdotU, float cosS, vec3 lightNight) {
	vec3 worldDirection = normalize(mat3(gbufferModelViewInverse) * normalize(viewPos));
	vec3 wpos = vec3(gbufferModelViewInverse * vec4(viewPos * 70.0, 1.0));
	vec3 planeCoord = wpos / (wpos.y + length(wpos.xz) * 0.5);

	vec3 moonPos = vec3(gbufferModelViewInverse * vec4(- sunVec * 70.0, 1.0));
	vec3 moonCoord = moonPos / (moonPos.y + length(moonPos.xz));
	vec2 wind = vec2(frametime, 0.0);
	vec2 skyCoord = planeCoord.xz - moonCoord.xz;
	skyCoord *= 0.35;

	float starDeletionTime = 1.0 - min(timeBrightness * 16.0, 1.0) * 0.15 - sunVisibility * 0.85;
	float starNdotU = pow2(pow2(clamp(NdotU * 3.0, 0.0, 1.0)));
	float starFactor = starNdotU * starDeletionTime;

	vec3 result = vec3(0.0);
	#ifdef SHADER_STARS
		float densityShift = float(STAR_AMOUNT - 1) * 0.006;
		vec3 starLayerA = EventHorizonStarLayer(worldDirection, 145.0,
				0.981 - densityShift, 0.042, frameTimeCounter);
		vec3 starLayerB = EventHorizonStarLayer(worldDirection, 330.0,
				0.989 - densityShift * 0.70, 0.036, frameTimeCounter * 0.83);
		vec3 starLayerC = EventHorizonStarLayer(worldDirection, 720.0,
				0.995 - densityShift * 0.45, 0.030, frameTimeCounter * 1.17);
		vec3 stellarColor = (starLayerA * 0.92 + starLayerB * 0.58 + starLayerC * 0.32);
		stellarColor *= mix(vec3(0.88, 0.93, 1.02), lightNight * lightNight * 12.0,
						  0.18);
		result += stellarColor * starFactor * (1.0 - isEyeInCave)
				* STAR_BRIGHTNESS * 0.58;
	#else
		result += vanillaStars * lightNight * lightNight * 1.5 * starFactor;
	#endif

	#ifndef SUNSET_STARS
		result *= 1.0 - sunVisibility;
	#endif

	#ifdef GALAXIES
		if (sunVisibility < 1.0) {
			vec3 galacticPole = normalize(vec3(0.29, 0.83, 0.47));
			float latitude = abs(dot(worldDirection, galacticPole));
			float broadBand = exp(-pow(latitude / 0.205, 1.55));
			float coreBand = exp(-pow(latitude / 0.060, 1.35));
			float dustA = EventHorizonSkyNoise3D(worldDirection * 1.35);
			float dustB = EventHorizonSkyNoise3D(worldDirection * 3.80 + vec3(1.7, 3.1, 5.3));
			float dustC = EventHorizonSkyNoise3D(worldDirection * 9.50 + vec3(4.2, 0.8, 2.6));
			float dust = smoothstep(0.27, 0.76,
					dustA * 0.55 + dustB * 0.31 + dustC * 0.14);
			float darkLane = smoothstep(0.51, 0.77, dustB) * coreBand;

			vec3 coolNebula = vec3(0.007, 0.012, 0.025);
			vec3 warmCore = vec3(0.026, 0.017, 0.015);
			vec3 proceduralGalaxy = mix(coolNebula, warmCore,
										  smoothstep(0.45, 0.95, dustA));
			proceduralGalaxy *= broadBand * (0.12 + 0.46 * dust);
			proceduralGalaxy += vec3(0.014, 0.012, 0.018) * coreBand * 0.30;
			proceduralGalaxy *= 1.0 - darkLane * 0.88;

			vec3 galaxy = proceduralGalaxy;
			galaxy += EventHorizonStarLayer(worldDirection, 92.0, 0.992,
					0.045, frameTimeCounter * 0.35) * broadBand * 0.11;
			result += galaxy * starNdotU * (1.0 - sunVisibility)
					* GALAXY_BRIGHTNESS * 0.62;
		}
	#endif

	result *= clamp(1.0 - pow(abs(cosS) * 1.002, 100.0), 0.0, 1.0);

	result *= pow2(pow2(1.0 - max(rainStrength, rainStrengthS)));

	return result;
}

//Includes//
#include "/lib/color/lightColor.glsl"
#include "/lib/color/skyColor.glsl"
#include "/lib/color/waterColor.glsl"
#include "/lib/color/endColor.glsl"
#include "/lib/util/dither.glsl"

#ifdef AURORA
#include "/lib/color/auroraColor.glsl"
#endif

#if (defined CLOUDS || defined AURORA) && defined OVERWORLD
#include "/lib/atmospherics/skyboxEffects.glsl"
#endif
#include "/lib/atmospherics/sky.glsl"
#include "/lib/atmospherics/sunGlare.glsl"

//Program//
void main() {
	vec3 albedo = vec3(0.0);

	vec4 screenPos = vec4(gl_FragCoord.xy / vec2(viewWidth, viewHeight), gl_FragCoord.z, 1.0);
	vec4 viewPos = gbufferProjectionInverse * (screenPos * 2.0 - 1.0);
	viewPos /= viewPos.w;

	vec3 nViewPos = normalize(viewPos.xyz);

	float NdotU = dot(nViewPos, upVec);
	
	#ifdef OVERWORLD
		#if defined CLOUDS || defined AURORA
			float dither = Bayer64(gl_FragCoord.xy);
		#endif
		#ifdef CLOUDS
			#if AA > 1
				vec4 cloud = DrawCloud(viewPos.xyz * 1000000.0, dither,
					lightCol, ambientCol, NdotU, CLOUD_TEMPORAL_SAMPLES);
			#else
				// Without TAA, two extra deterministic layers prevent the six-sample
				// cloud shell from breaking into radial filaments.
				vec4 cloud = DrawCloud(viewPos.xyz * 1000000.0, dither,
					lightCol, ambientCol, NdotU, CLOUD_STABLE_SAMPLES);
			#endif
			float cloudMaskR = cloud.a / CLOUD_OPACITY;
			if (NdotU > 0.025) cloudMaskR /= pow2(pow2(1.0 - exp(-(10.0 - 8.2 * rainStrengthS) * NdotU)));
		#endif
		
		albedo = GetSkyColor(lightCol, NdotU, nViewPos, false);
		
		float cosS = dot(nViewPos, sunVec);

		vec3 galaxy = GetGalaxy(viewPos.xyz, NdotU, cosS, lightNight);
		#ifdef CLOUDS
			galaxy *= smoothstep(0.0, 1.0, 1.0 - cloudMaskR);
		#endif
		albedo.rgb += galaxy;
		
		#ifdef ROUND_SUN_MOON
			float sunHeight = dot(sunVec, upVec);
			vec3 sunColor = mix(vec3(1.00, 0.22, 0.035),
					vec3(1.00, 0.88, 0.68), smoothstep(0.02, 0.42, sunHeight));
			if (isEyeInWater == 1) sunColor = 2.0 * normalize(underwaterColor.rgb);
			vec3 moonColor = vec3(12.0, 13.8, 15.9) / 35.7;
			
			vec3 roundSunMoon = RoundSunMoon(nViewPos, sunColor, moonColor, NdotU, cosS);

			#ifdef CLOUDS
				roundSunMoon *= pow2(pow2(pow2(pow2(pow2(1.0 - cloudMaskR * cloudMaskR * rainStrengthS)))));
				//This should still be faster than pow()

				roundSunMoon *= pow2(1.0 - rainStrengthS);
			#else
				roundSunMoon *= 1.0 - max(rainStrength, rainStrengthS);
			#endif
			albedo.rgb += roundSunMoon;
		#endif

		albedo = SunGlare(albedo, nViewPos, lightCol);
		
		#ifdef AURORA
			vec3 aurora = DrawAurora(viewPos.xyz * 1000000.0, dither, 20, NdotU);
			#ifdef CLOUDS
				aurora *= 1.0 - cloudMaskR;
			#endif
			albedo.rgb += aurora;
		#endif

		#if defined CLOUDS && THE_FORBIDDEN_OPTION == 0
			albedo.rgb = mix(albedo.rgb, cloud.rgb, cloud.a);
		#endif
	#endif

	#ifdef GBUFFER_CODING
		albedo.rgb = vec3(0.0, 0.0, 0.0);
	#endif

	#ifdef SHADER_STARS
		float alpha = 1.0 - vanillaStars;
	#else
		#ifdef SUNSET_STARS
			float alpha = max(1.0 - vanillaStars * timeBrightness * 10.0, 0.0);
		#else
			float alpha = 1.0 - vanillaStars * sunVisibility;
		#endif
		float vanillaStarsM = vanillaStars * (1.0 - clamp(NdotU * 3.0, 0.0, 1.0));
		alpha = max(alpha - vanillaStarsM, 0.0);
	#endif

	#if MC_VERSION >= 11700
		if (isEyeInWater == 3 && blindFactor == 0) albedo.rgb = vec3(0.1, 0.15, 0.2);
	#endif

	#if defined CAVE_SKY_FIX && defined OVERWORLD
		albedo.rgb *= 1.0 - isEyeInCave;
	#endif

	#if MC_VERSION >= 11900
		if (darknessFactor > 0.001) albedo.rgb = mix(albedo.rgb, darknessColor, darknessFactor);
	#endif

    /* DRAWBUFFERS:0 */
	gl_FragData[0] = vec4(albedo, alpha);
}

#endif

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

//Uniforms//

uniform mat4 gbufferModelView;

//Common Variables//
#ifdef OVERWORLD
	float timeAngleM = timeAngle;
#else
	#if !defined SEVEN && !defined SEVEN_2
		float timeAngleM = 0.25;
	#else
		float timeAngleM = 0.5;
	#endif
#endif

//Program//
void main() {
    const vec2 sunRotationData = vec2(cos(sunPathRotation * 0.01745329251994), -sin(sunPathRotation * 0.01745329251994));
	float ang = fract(timeAngleM - 0.25);
	ang = (ang + (cos(ang * 3.14159265358979) * -0.5 + 0.5 - ang) / 3.0) * 6.28318530717959;
	vec3 rawSunVec = (gbufferModelView * vec4(vec3(-sin(ang), cos(ang) * sunRotationData) * 2000.0, 1.0)).xyz;
	sunVec = normalize(rawSunVec);

	upVec = normalize(gbufferModelView[1].xyz);
	
	gl_Position = ftransform();

	vec3 color = gl_Color.rgb;
	
	//Vanilla Star Dedection by Builderb0y
	vanillaStars = float(color.r == color.g && color.g == color.b && color.r > 0.0 && color.r < 0.51);
}

#endif
