/*
====================================================================
  SXGG: Realis — Shader ultra-realistica per Minecraft
  Autore : Samax.GG
  File   : lib/sky.glsl
  Modulo : 4 — Atmosfera: scattering Rayleigh/Mie analitico,
           stelle + Via Lattea procedurale, luna con fasi.

  Richiede: lib/common.glsl, lib/noise.glsl
  Uniform : sunPosition, moonPosition, moonPhase (int), rainStrength
====================================================================
*/

#ifndef SKY_GLSL
#define SKY_GLSL

// Coefficienti (unità arbitrarie tarate per il look, non fisiche pure)
const vec3  RAYLEIGH_COEFF = vec3(5.8e-2, 13.5e-2, 33.1e-2);
const float MIE_COEFF      = 2.1e-2;
const float MIE_G          = 0.78;

float rayleighPhase(float cosTheta) {
    return 3.0 / (16.0 * PI) * (1.0 + cosTheta * cosTheta);
}

float miePhase(float cosTheta, float g) {
    float g2 = g * g;
    return (1.0 - g2) / (4.0 * PI * pow(1.0 + g2 - 2.0 * g * cosTheta, 1.5));
}

/*
  Cielo analitico: integra approssimativamente lo scattering lungo
  la vista usando la massa d'aria (secante dell'angolo di elevazione).
  viewDir, sunDir in world space normalizzati.
*/
vec3 skyScattering(vec3 viewDir, vec3 sunDir, float rain) {
    float cosTheta = dot(viewDir, sunDir);

    // Massa d'aria: cresce verso l'orizzonte (clamp per sotto-orizzonte)
    float elev    = max(viewDir.y, 0.001);
    float airMass = 1.0 / (elev + 0.15 * pow(elev + 0.15, -1.63));

    // Altezza del sole: guida il colore globale (tramonto = più massa)
    float sunElev   = saturate(sunDir.y * 0.5 + 0.5);
    float sunHeight = saturate(sunDir.y);

    // Trasmittanza: il blu viene assorbito prima all'orizzonte -> arancio/rosso
    vec3 transmittance = exp(-RAYLEIGH_COEFF * airMass * (1.0 - sunHeight * 0.5) * 1.2);

    // Rayleigh: il blu del cielo
    vec3 rayleigh = RAYLEIGH_COEFF * rayleighPhase(cosTheta) * airMass;
    rayleigh *= mix(vec3(0.3, 0.45, 1.4), vec3(1.0), sunHeight) * 22.0;

    // Mie: alone luminoso intorno al sole, forte al tramonto
    float mie = MIE_COEFF * miePhase(cosTheta, MIE_G) * airMass * 14.0;
    vec3 sunTint = mix(vec3(1.0, 0.35, 0.12), vec3(1.0, 0.9, 0.75), sunHeight);

    vec3 sky = rayleigh * transmittance + mie * sunTint * transmittance;

    // Notte: cielo blu scurissimo, non nero assoluto
    float daylight = smoothstep(-0.12, 0.12, sunDir.y);
    vec3 nightSky = vec3(0.012, 0.017, 0.032) * (1.0 + saturate(-viewDir.y + 1.0) * 0.4);
    sky = mix(nightSky, sky, daylight);

    // Pioggia: desatura e scurisce
    sky = mix(sky, vec3(luminance(sky)) * 0.55, rain * 0.8);

    return sky;
}

// --- Colore/intensità della luce solare o lunare per il lighting ---
vec3 lightColorForSun(vec3 sunDir, float rain) {
    float h = saturate(sunDir.y);
    // dall'arancio del tramonto al bianco caldo dello zenit
    vec3 color = mix(vec3(1.0, 0.42, 0.15), vec3(1.0, 0.96, 0.90),
                     smoothstep(0.0, 0.4, h));
    float intensity = smoothstep(-0.05, 0.15, sunDir.y) * 3.2;
    return color * intensity * (1.0 - rain * 0.75);
}

vec3 lightColorForMoon(vec3 moonDir, float rain) {
    float intensity = smoothstep(-0.05, 0.15, moonDir.y) * 0.14;
    return vec3(0.55, 0.65, 0.95) * intensity * (1.0 - rain * 0.75);
}

/*
  Stelle: griglia celeste con hash; twinkle temporale leggero.
  Via Lattea: banda FBM inclinata con densità di micro-stelle.
*/
vec3 stars(vec3 viewDir, float time, float sunHeight) {
    float nightFactor = 1.0 - smoothstep(-0.15, 0.05, sunHeight);
    if (nightFactor < EPSILON || viewDir.y < -0.05) return vec3(0.0);

    // Proiezione su griglia sferica
    vec3 dir = normalize(viewDir);
    vec2 cell = vec2(atan(dir.x, dir.z), asin(dir.y)) * 60.0;
    vec2 id   = floor(cell);
    vec2 f    = fract(cell) - 0.5;

    float star = 0.0;
    float h = hash12(id);
    if (h > 0.90) {                       // ~10% delle celle ha una stella
        vec2 starPos = vec2(hash12(id + 17.1), hash12(id + 31.7)) - 0.5;
        float d = length(f - starPos * 0.8);
        float size = 0.03 + hash12(id + 5.3) * 0.035;
        star = smoothstep(size, 0.0, d);
        // Twinkle
        star *= 0.6 + 0.4 * sin(time * (1.5 + h * 3.0) + h * TAU);
        star *= (h - 0.90) * 10.0;        // magnitudini diverse
    }

    #ifdef MILKY_WAY
        // Banda galattica: piano inclinato nel cielo
        vec3 galaxyNormal = normalize(vec3(0.35, 0.85, 0.40));
        float band = 1.0 - abs(dot(dir, galaxyNormal));
        band = smoothstep(0.72, 1.0, band);
        float dust = fbm2D5(vec2(atan(dir.x, dir.z), dir.y) * 6.0);
        vec3 milky = vec3(0.55, 0.62, 0.85) * band * dust * dust * 0.35;
        // Polveri scure dentro la banda
        milky *= 0.4 + 0.6 * smoothstep(0.3, 0.7, fbm2D5(vec2(atan(dir.x, dir.z), dir.y) * 14.0 + 3.7));
        return (vec3(star) + milky) * nightFactor;
    #else
        return vec3(star) * nightFactor;
    #endif
}

/*
  Luna procedurale con fasi (moonPhase 0..7 di Minecraft).
  Disegna un disco con terminatore calcolato via illuminazione sferica.
*/
vec3 moonDisc(vec3 viewDir, vec3 moonDir, int phase, float sunHeight) {
    float nightFactor = 1.0 - smoothstep(-0.15, 0.10, sunHeight);
    float d = dot(viewDir, moonDir);
    const float MOON_SIZE = 0.9985;
    if (d < MOON_SIZE || nightFactor < EPSILON) return vec3(0.0);

    // Coordinate locali sul disco
    vec3 right = normalize(cross(moonDir, vec3(0.0, 1.0, 0.0)));
    vec3 up    = cross(right, moonDir);
    vec2 uv    = vec2(dot(viewDir, right), dot(viewDir, up)) / (1.0 - MOON_SIZE);

    float r2 = dot(uv, uv);
    if (r2 > 1.0) return vec3(0.0);

    // Sfera: normale della superficie lunare
    vec3 sphereN = vec3(uv, sqrt(1.0 - r2));

    // Direzione del "sole" rispetto alla luna in base alla fase
    #ifdef MOON_PHASES
        float phaseAngle = float(phase) / 8.0 * TAU;     // 0 = piena
        vec3 phaseSun = vec3(sin(phaseAngle), 0.0, cos(phaseAngle));
        float lit = saturate(dot(sphereN, phaseSun) * 1.4);
    #else
        float lit = 1.0;
    #endif

    // Crateri: FBM sulla superficie
    float craters = 0.75 + 0.25 * fbm2D5(uv * 7.0 + 13.0);

    float edge = smoothstep(1.0, 0.94, r2);              // antialiasing bordo
    return vec3(0.95, 0.93, 0.88) * lit * craters * edge * 1.6 * nightFactor;
}

#endif // SKY_GLSL
