// ---------------------------------------------------------------------------
// Shared procedural sky. This chunk is compiled into BOTH the sky dome shader
// and the ocean shader (which evaluates it along reflected rays), so the
// mirror reflections are analytically exact — no cubemaps, no render targets.
// Define SKY_CHEAP before including to drop the most expensive layers for
// reflection rays (they're distorted by waves anyway, nobody can tell).
// ---------------------------------------------------------------------------

export const SKY_UNIFORMS_GLSL = /* glsl */ `
uniform float uTime;
uniform vec3  uMoonADir;
uniform vec3  uMoonBDir;
uniform vec3  uMoonACol;
uniform vec3  uMoonBCol;
uniform vec3  uSkyTop;
uniform vec3  uSkyHorizon;
uniform vec3  uNebulaCol;
uniform vec3  uAuroraA;
uniform vec3  uAuroraB;
uniform float uAuroraAmp;
uniform float uFlash;
uniform vec3  uFlashDir;
`;

export const SKY_GLSL = /* glsl */ `
// ---- hashes / noise -------------------------------------------------------
float hash12(vec2 p) {
  vec3 p3 = fract(vec3(p.xyx) * 0.1031);
  p3 += dot(p3, p3.yzx + 33.33);
  return fract((p3.x + p3.y) * p3.z);
}
float hash13(vec3 p3) {
  p3 = fract(p3 * 0.1031);
  p3 += dot(p3, p3.zyx + 31.32);
  return fract((p3.x + p3.y) * p3.z);
}
float vnoise2(vec2 p) {
  vec2 i = floor(p), f = fract(p);
  f = f * f * (3.0 - 2.0 * f);
  return mix(
    mix(hash12(i),                 hash12(i + vec2(1, 0)), f.x),
    mix(hash12(i + vec2(0, 1)),    hash12(i + vec2(1, 1)), f.x), f.y);
}
float vnoise3(vec3 p) {
  vec3 i = floor(p), f = fract(p);
  f = f * f * (3.0 - 2.0 * f);
  return mix(
    mix(mix(hash13(i),                  hash13(i + vec3(1,0,0)), f.x),
        mix(hash13(i + vec3(0,1,0)),    hash13(i + vec3(1,1,0)), f.x), f.y),
    mix(mix(hash13(i + vec3(0,0,1)),    hash13(i + vec3(1,0,1)), f.x),
        mix(hash13(i + vec3(0,1,1)),    hash13(i + vec3(1,1,1)), f.x), f.y), f.z);
}
float fbm2(vec2 p) {
  float v = 0.0, a = 0.5;
  for (int i = 0; i < 4; i++) {
    v += a * vnoise2(p);
    p = p * 2.07 + vec2(13.7, 7.1);
    a *= 0.5;
  }
  return v;
}
float fbm3(vec3 p) {
  float v = 0.0, a = 0.5;
  #ifdef SKY_CHEAP
  for (int i = 0; i < 2; i++) {
  #else
  for (int i = 0; i < 3; i++) {
  #endif
    v += a * vnoise3(p);
    p = p * 2.11 + 9.4;
    a *= 0.5;
  }
  return v;
}

// ---- stars ----------------------------------------------------------------
float starLayer(vec3 rd, float cells, float density, float t) {
  vec3 p = rd * cells;
  vec3 id = floor(p);
  float h = hash13(id);
  if (h < 1.0 - density) return 0.0;
  vec3 jitter = vec3(hash13(id + 1.7), hash13(id + 4.3), hash13(id + 8.9)) - 0.5;
  float d = length(fract(p) - 0.5 - jitter * 0.55);
  float star = exp(-d * d * 42.0);
  float tw = 0.72 + 0.28 * sin(t * (2.0 + h * 4.0) + h * 71.0);
  return star * tw * (h - (1.0 - density)) / density;
}

// ---- aurora ---------------------------------------------------------------
// planar projection (no azimuth seam); curtains are fbm-gated sheets with
// sharp vertical rays warped by the large-scale shape
vec3 aurora(vec3 rd, float t) {
  if (rd.y < 0.015) return vec3(0.0);
  vec2 q = rd.xz / (rd.y + 0.42);
  vec3 acc = vec3(0.0);
  #ifdef SKY_CHEAP
  const int LAYERS = 2;
  #else
  const int LAYERS = 3;
  #endif
  for (int i = 0; i < LAYERS; i++) {
    float fi = float(i);
    vec2 drift = vec2(t * (0.018 + fi * 0.009), -t * 0.012);
    float w = fbm2(q * (0.5 + fi * 0.22) + drift + fi * 7.3);
    float rc = q.x * 0.8 + q.y * (0.6 - fi * 0.5);
    float rays = vnoise2(vec2(rc * (9.0 + fi * 4.0) + w * 5.0, fi * 31.0 + t * (0.16 + fi * 0.06)));
    rays = pow(rays, 3.0);
    float gate = smoothstep(0.34, 0.72, w);
    float band = smoothstep(0.015, 0.14 + fi * 0.05, rd.y) * exp(-rd.y * (2.2 - fi * 0.3));
    float I = band * gate * (0.35 + 1.8 * rays);
    vec3 col = mix(uAuroraA, uAuroraB, clamp(rd.y * 1.9 + rays * 0.35 - 0.05, 0.0, 1.0));
    acc += col * I * (1.0 - fi * 0.22);
  }
  return acc * uAuroraAmp * 0.5;
}

// ---- moons ----------------------------------------------------------------
vec3 moon(vec3 rd, vec3 md, float angR, vec3 baseCol, float seed, float haloAmt) {
  float c = clamp(dot(rd, md), -1.0, 1.0);
  float ang = acos(c);
  // halo (always)
  float halo = exp(-(ang - angR) * (14.0 / angR)) * step(angR, ang);
  vec3 col = baseCol * halo * haloAmt;
  if (ang < angR) {
    // local disc coordinates
    vec3 uax = normalize(cross(md, vec3(0.0, 1.0, 0.0)));
    vec3 vax = cross(uax, md);
    vec2 lp = vec2(dot(rd, uax), dot(rd, vax)) / angR; // unit disc
    float r2 = dot(lp, lp);
    float z = sqrt(max(1.0 - r2, 0.0));
    vec3 sn = vec3(lp.x, lp.y, z); // sphere normal in disc space
    // craters & maria
    float mare = fbm3(vec3(lp * 2.3, seed) + seed);
    float crat = fbm3(vec3(lp * 7.0, seed * 2.0) - seed);
    float albedo = 0.62 + 0.5 * mare * mare - 0.22 * crat;
    // fixed phase light
    vec3 ldir = normalize(vec3(-0.55, 0.25, 0.8));
    float nl = clamp(dot(sn, ldir) * 0.85 + 0.15, 0.0, 1.0);
    float limb = pow(z, 0.45);
    float aa = smoothstep(angR, angR * 0.965, ang);
    col += baseCol * albedo * nl * limb * 2.5 * aa;
  }
  return col;
}

#ifndef SKY_CHEAP
// ---- shooting stars -------------------------------------------------------
vec3 shootingStar(vec3 rd, float t) {
  float period = 6.5;
  float id = floor(t / period);
  float lt = mod(t, period);
  float dur = 0.9;
  if (lt > dur) return vec3(0.0);
  float h1 = hash12(vec2(id, 3.71));
  float h2 = hash12(vec2(id, 9.13));
  float h3 = hash12(vec2(id, 5.57));
  // start direction in upper sky
  vec3 a = normalize(vec3(h1 * 2.0 - 1.0, 0.35 + 0.45 * h2, h3 * 2.0 - 1.0));
  vec3 side = normalize(cross(a, vec3(0.0, 1.0, 0.0)));
  vec3 fall = normalize(side * (h2 > 0.5 ? 1.0 : -1.0) - vec3(0.0, 0.6, 0.0));
  float span = 0.35; // radians travelled
  float prog = lt / dur;
  float ease = prog * (2.0 - prog);
  vec3 head = normalize(a + fall * span * ease);
  vec3 pn = normalize(cross(a, head + fall * 0.001));
  float dPlane = abs(dot(rd, pn));
  // along-path coordinate
  vec3 rdp = normalize(rd - pn * dot(rd, pn));
  float thetaH = acos(clamp(dot(a, head), -1.0, 1.0));
  float theta = acos(clamp(dot(a, rdp), -1.0, 1.0));
  float behind = thetaH - theta;
  float onPath = step(0.0, behind) * step(behind, 0.10);
  float streak = exp(-dPlane * dPlane * 3.2e5) * exp(-behind * 55.0) * onPath;
  float fade = sin(prog * 3.14159);
  return vec3(0.9, 0.95, 1.0) * streak * fade * 3.0;
}
#endif

// ---- full sky -------------------------------------------------------------
vec3 skyColor(vec3 rd, float t) {
  float y = rd.y;

  // base gradient + below-horizon haze (so the dome blends into the sea fog)
  vec3 col = mix(uSkyHorizon, uSkyTop, smoothstep(-0.02, 0.5, y));
  if (y < 0.0) {
    col = mix(uSkyHorizon, uSkyHorizon * 0.25, smoothstep(0.0, -0.25, y));
  }

  // galactic band: tilted fbm nebula
  vec3 bn = normalize(vec3(0.5, 0.62, -0.35));
  float band = exp(-pow(dot(rd, bn), 2.0) * 7.0);
  float neb = fbm3(rd * 3.1 + vec3(0.0, 0.0, t * 0.004));
  col += uNebulaCol * neb * neb * band * 1.15;

  // stars (fade at horizon; skipped on reflection rays — sub-pixel there)
  #ifndef SKY_CHEAP
  float horizonFade = smoothstep(0.0, 0.12, y);
  float s = starLayer(rd, 90.0, 0.10, t) * 0.8;
  s += starLayer(rd, 41.0, 0.05, t * 0.7) * 1.6;
  col += vec3(0.82, 0.89, 1.0) * s * horizonFade * (0.5 + band);
  #endif

  // aurora
  col += aurora(rd, t);

  // twin moons
  col += moon(rd, uMoonADir, 0.072, uMoonACol, 3.1, 0.05);
  col += moon(rd, uMoonBDir, 0.030, uMoonBCol, 17.7, 0.035);

  #ifndef SKY_CHEAP
  col += shootingStar(rd, t);
  #endif

  // distant heat lightning on the horizon
  float fl = max(dot(normalize(vec3(rd.x, 0.0, rd.z)), uFlashDir), 0.0);
  col += vec3(0.75, 0.82, 1.0) * uFlash * pow(fl, 6.0) * exp(-max(y, 0.0) * 7.0) * 2.2;

  return col;
}
`;
