// ============================================================================
// SKY AND LIGHTING SYSTEM
// ============================================================================
#ifndef SKY_GLSL
#define SKY_GLSL

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

bool checkIsSunrise() {
    return (worldTime > 18000 || worldTime < 6000);
}

vec3 applySaturation(vec3 color, float saturation) {
    float luma = dot(color, vec3(0.299, 0.587, 0.114));
    return max(mix(vec3(luma), color, saturation), vec3(0.0));
}

// Calculate day/night transition factors.
void getTransitionFactor(float sunHeight, bool isSunrise, out float sunVis, out float nightVis, out float orangeVis) {
    float start = isSunrise ? float(SUNRISE_END) : float(SUNSET_START);
    float fadeSpeed = isSunrise ? float(SUNRISE_FADE_SPEED) : float(SUNSET_FADE_SPEED);

    sunVis = smoothstep(0.0, start, sunHeight);
    float nightFull = float(NIGHT_START) - 0.15;
    nightVis = 1.0 - smoothstep(nightFull, float(NIGHT_START), sunHeight);

    float x = abs(sunHeight * fadeSpeed);
	float curve = exp(-(x * x));
	orangeVis = curve * (1.0 - sunVis) * (1.0 - nightVis);
}

float getDirectionalFade(float sunHeight) {
    float dayPhysics = smoothstep(0.0, 0.05, sunHeight);
    float nightPhysics = 1.0 - smoothstep(-0.15, -0.05, sunHeight);
    return max(dayPhysics, nightPhysics);
}

// Compute dynamic sky and horizon colors.
void getSkyColors(vec3 worldViewDir, vec3 worldSunDir, float rainStrength, out vec3 zenithColor, out vec3 horizonColor) {
    bool isSunrise = checkIsSunrise();
    float sunVis, nightVis, orangeVis;
    getTransitionFactor(worldSunDir.y, isSunrise, sunVis, nightVis, orangeVis);

    vec3 viewDirStretched = worldViewDir;
    viewDirStretched.y *= float(SUNRISESET_STRETCH);
    viewDirStretched = normalize(viewDirStretched);

    vec3 sunDirStretched = worldSunDir;
    sunDirStretched.y *= float(SUNRISESET_STRETCH);
    sunDirStretched.y -= float(SUNRISESET_Y_OFFSET); 
    sunDirStretched = normalize(sunDirStretched);

    float sunDot = dot(viewDirStretched, sunDirStretched);
    float sunDrop = min(worldSunDir.y, 0.0) * 1.5; 
    float spreadStart = 1.0 - float(SUNRISESET_SPREAD);
    float sunsetGlow = smoothstep(spreadStart - sunDrop, 1.0 - sunDrop, sunDot);

    vec3 zDay = vec3(SKY_ZENITH_R, SKY_ZENITH_G, SKY_ZENITH_B);
    vec3 hDay = vec3(SKY_HORIZON_R, SKY_HORIZON_G, SKY_HORIZON_B);
    vec3 zNight = vec3(SKY_NIGHT_ZENITH_R, SKY_NIGHT_ZENITH_G, SKY_NIGHT_ZENITH_B);
    vec3 hNight = vec3(SKY_NIGHT_HORIZON_R, SKY_NIGHT_HORIZON_G, SKY_NIGHT_HORIZON_B);
    
    vec3 hBase = mix(hDay, hNight, nightVis);
    vec3 zBase = mix(zDay, zNight, nightVis);

    // 1. Calculate overcast rain colors first.
    float skyDim = mix(1.0, 1.0 - float(SKY_RAIN_COVERAGE), rainStrength);
    vec3 dayRainColor = vec3(SKY_RAIN_HORIZON_R, SKY_RAIN_HORIZON_G, SKY_RAIN_HORIZON_B);
    vec3 nightRainColor = vec3(SKY_RAIN_NIGHT_HORIZON_R, SKY_RAIN_NIGHT_HORIZON_G, SKY_RAIN_NIGHT_HORIZON_B);
    vec3 rainColor = mix(dayRainColor, nightRainColor, nightVis);

    vec3 overcastHorizon = mix(applySaturation(hBase, skyDim), rainColor, rainStrength);
    vec3 overcastZenith  = mix(applySaturation(zBase, skyDim), rainColor * 0.85, rainStrength);

    // 2. Fetch celestial twilight colors.
    vec3 sunriseColor = vec3(SUN_SUNRISE_R, SUN_SUNRISE_G, SUN_SUNRISE_B);
    vec3 sunsetColor  = vec3(SUN_SUNSET_R, SUN_SUNSET_G, SUN_SUNSET_B);
    vec3 baseCelestialColor = isSunrise ? sunriseColor : sunsetColor;
    
    float skySat = isSunrise ? float(SUN_SUNRISE_SATURATION) : float(SUN_SUNSET_SATURATION);
    vec3 finalSkyColor = applySaturation(baseCelestialColor, skySat);

    // 3. Layer the twilight glow on top, diffused by rain intensity.
    float twilightRainFade = mix(1.0, float(RAIN_TWILIGHT_MIX), rainStrength);
    horizonColor = mix(overcastHorizon, finalSkyColor, orangeVis * sunsetGlow * twilightRainFade);
    zenithColor  = mix(overcastZenith,  finalSkyColor, orangeVis * sunsetGlow * 0.5 * twilightRainFade);
}

// Compute unified direct and ambient lighting.
void getLightColors(vec3 worldSunDir, out vec3 directLight, out vec3 ambientLight, out float shadowOpacity) {
    bool isSunrise = checkIsSunrise();
    float sunHeight = worldSunDir.y;

    float sunVis, nightVis, orangeVis;
    getTransitionFactor(sunHeight, isSunrise, sunVis, nightVis, orangeVis);

    vec3 cSun = vec3(SUN_R, SUN_G, SUN_B);
    vec3 cMoon = vec3(MOON_R, MOON_G, MOON_B);

    vec3 sunriseColor = vec3(SUN_SUNRISE_R, SUN_SUNRISE_G, SUN_SUNRISE_B);
    vec3 sunsetColor  = vec3(SUN_SUNSET_R, SUN_SUNSET_G, SUN_SUNSET_B);
    vec3 baseCelestialColor = isSunrise ? sunriseColor : sunsetColor;
    
    // Apply light-specific saturation.
    vec3 lightCelestialColor = applySaturation(baseCelestialColor, float(SUNRISESET_LIGHT_SATURATION));
    
    // Apply multiplier strictly to direct terrain lighting.
    vec3 cSunsetSun = lightCelestialColor * float(SUNRISESET_LIGHT_MULTIPLIER);

    float dirFade = getDirectionalFade(sunHeight);
    float dayPhysics = smoothstep(0.0, 0.05, sunHeight);
    float nightPhysics = 1.0 - smoothstep(-0.15, -0.05, sunHeight);

    // Blend celestial light sources based on time of day.
    directLight = (cSun * sunVis * dayPhysics) 
                + (cSunsetSun * orangeVis * dayPhysics) 
                + (cMoon * nightVis * nightPhysics);

    vec3 aDay = vec3(SKY_HORIZON_R, SKY_HORIZON_G, SKY_HORIZON_B);
    vec3 aNight = vec3(SKY_NIGHT_HORIZON_R, SKY_NIGHT_HORIZON_G, SKY_NIGHT_HORIZON_B);
	vec3 aSunset = lightCelestialColor * float(SUNRISESET_AMB_INTENSITY);

    // Apply distinct intensities for day and night.
    float dayInt = float(AMBIENT_INTENSITY);
    float nightInt = float(AMBIENT_NIGHT_INTENSITY);
    ambientLight = (aDay * sunVis * dayInt) + (aNight * nightVis * nightInt) + (aSunset * orangeVis * dayInt);
    shadowOpacity = float(SHADOW_OPACITY) * dirFade;
}
#endif