#version 410 core
in vec3 vRay;
uniform vec3 uZenith;
uniform vec3 uHorizon;
uniform vec3 uSunDir;
uniform vec3 uSunColor;
uniform float uStarAlpha;
uniform float uDayLight;
uniform vec3 uSunsetTint;   // warm sunrise/sunset color
uniform float uSunsetAmt;   // 0..1 sun-near-horizon factor
uniform float uCloudAmt;    // 0..1 cloud coverage (0 = clouds off)
uniform float uFancySky;    // 1 = star twinkle + moon crescent
uniform float uSkyTime;     // slow scroll/twinkle clock (day-cycle derived)
uniform float uCamY;        // camera world height (cloud plane intersection)
out vec4 FragColor;

float hash(vec3 p) {
    p = fract(p * 0.3183099 + 0.1);
    p *= 17.0;
    return fract(p.x * p.y * p.z * (p.x + p.y + p.z));
}

// 2D value noise for the cloud layer (bilinear-smoothed lattice hash).
float vnoise(vec2 p) {
    vec2 i = floor(p), f = fract(p);
    f = f * f * (3.0 - 2.0 * f);
    float a = hash(vec3(i, 7.0));
    float b = hash(vec3(i + vec2(1.0, 0.0), 7.0));
    float c = hash(vec3(i + vec2(0.0, 1.0), 7.0));
    float d = hash(vec3(i + vec2(1.0, 1.0), 7.0));
    return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
}

void main() {
    vec3 dir = normalize(vRay);
    float t = clamp(dir.y, 0.0, 1.0);
    vec3 col = mix(uHorizon, uZenith, pow(t, 0.55));
    if (dir.y < 0.0) col = mix(uHorizon, uHorizon * 0.6, clamp(-dir.y * 2.0, 0.0, 1.0));

    // View-dependent sunrise/sunset: the horizon ring glows warm toward the sun
    // and stays cool on the anti-solar side.
    if (uSunsetAmt > 0.001) {
        vec2 flatDir = normalize(dir.xz + vec2(1e-5));
        vec2 flatSun = normalize(uSunDir.xz + vec2(1e-5));
        float toward = pow(max(dot(flatDir, flatSun), 0.0), 4.0);
        float band = 1.0 - clamp(abs(dir.y) * 3.0, 0.0, 1.0);
        col = mix(col, uSunsetTint, uSunsetAmt * toward * band * 0.85);
    }

    // Sun
    float sd = dot(dir, normalize(uSunDir));
    col += uSunColor * smoothstep(0.9975, 0.9995, sd) * 1.5;       // disk
    col += uSunColor * 0.25 * pow(max(sd, 0.0), 220.0);            // glow

    // Moon (opposite the sun). Fancy sky carves a crescent with an offset disk.
    vec3 moonDir = normalize(-uSunDir);
    float md = dot(dir, moonDir);
    float moon = smoothstep(0.9985, 0.9997, md);
    if (uFancySky > 0.5) {
        float md2 = dot(dir, normalize(moonDir + vec3(0.012, 0.016, 0.0)));
        moon = clamp(moon - smoothstep(0.9985, 0.9997, md2) * 0.85, 0.0, 1.0);
    }
    col += vec3(0.8, 0.85, 0.95) * moon;

    // Stars at night: soft fade-in (no pop) + gentle twinkle on fancy sky.
    if (uStarAlpha > 0.01 && dir.y > 0.0) {
        vec3 sp = floor(dir * 200.0);
        float h = hash(sp);
        float star = smoothstep(0.9965, 0.9985, h) * uStarAlpha;
        if (uFancySky > 0.5) star *= 0.75 + 0.25 * sin(uSkyTime * 3.0 + h * 40.0);
        col += vec3(star);
    }

    // Flat scrolling cloud plane (Minecraft-style), shaded by daylight and
    // faded into the horizon. Applied last so clouds pass over sun and stars.
    if (uCloudAmt > 0.001 && dir.y > 0.02) {
        float planeH = max(192.0 - uCamY, 24.0);
        vec2 hit = dir.xz * (planeH / dir.y);
        vec2 drift = vec2(uSkyTime * 1.6, uSkyTime * 0.9);
        float n = vnoise(hit * 0.004 + drift) * 0.65 +
                  vnoise(hit * 0.011 + drift * 1.7) * 0.35;
        float cover = mix(0.72, 0.48, clamp(uCloudAmt, 0.0, 1.0));
        float cloud = smoothstep(cover, cover + 0.16, n);
        float fade = smoothstep(0.02, 0.14, dir.y);
        vec3 cloudCol = mix(uHorizon * 0.9, vec3(1.0), 0.55) * (0.25 + 0.75 * uDayLight);
        col = mix(col, cloudCol, cloud * fade * 0.85);
    }

    FragColor = vec4(col, 1.0);
}
