import { SKY_GLSL, SKY_UNIFORMS_GLSL } from './skyCommon';

// Mercury sea: gerstner waves displaced in the vertex shader (analytic
// normals), fbm ripple detail in the fragment shader, and mirror reflections
// computed by evaluating the shared procedural sky along reflected rays.

export const OCEAN_VERT = /* glsl */ `
uniform float uTime;
uniform float uWaveAmp;
varying vec3 vWorldPos;
varying vec3 vNormal;
varying float vCrest;

// 5 gerstner waves: direction (xy), steepness, wavelength
const vec4 W0 = vec4( 1.00,  0.25, 0.18, 62.0);
const vec4 W1 = vec4(-0.62,  0.78, 0.22, 31.0);
const vec4 W2 = vec4( 0.31, -0.95, 0.20, 18.5);
const vec4 W3 = vec4(-0.88, -0.42, 0.25, 11.0);
const vec4 W4 = vec4( 0.52,  0.86, 0.22,  6.5);

vec3 gerstner(vec4 w, vec2 p, float t, inout vec3 tang, inout vec3 binv) {
  vec2 d = normalize(w.xy);
  float k = 6.2831853 / w.w;
  float c = sqrt(9.8 / k) * 0.8;
  float f = k * (dot(d, p) - c * t);
  float a = w.z / k;
  float sf = sin(f), cf = cos(f);
  tang += vec3(-d.x * d.x * w.z * sf, d.x * w.z * cf, -d.x * d.y * w.z * sf);
  binv += vec3(-d.x * d.y * w.z * sf, d.y * w.z * cf, -d.y * d.y * w.z * sf);
  return vec3(d.x * a * cf, a * sf, d.y * a * cf);
}

void main() {
  vec3 wp = (modelMatrix * vec4(position, 1.0)).xyz;
  vec2 p = wp.xz;
  vec3 tang = vec3(1.0, 0.0, 0.0);
  vec3 binv = vec3(0.0, 0.0, 1.0);
  vec3 off = vec3(0.0);
  off += gerstner(W0, p, uTime, tang, binv);
  off += gerstner(W1, p, uTime, tang, binv);
  off += gerstner(W2, p, uTime, tang, binv);
  off += gerstner(W3, p, uTime, tang, binv);
  off += gerstner(W4, p, uTime, tang, binv);
  off *= uWaveAmp;
  wp += off;
  vNormal = normalize(cross(binv, tang));
  vCrest = off.y;
  vWorldPos = wp;
  gl_Position = projectionMatrix * viewMatrix * vec4(wp, 1.0);
}
`;

export const OCEAN_FRAG = /* glsl */ `
precision highp float;
#define SKY_CHEAP 1
varying vec3 vWorldPos;
varying vec3 vNormal;
varying float vCrest;
${SKY_UNIFORMS_GLSL}
uniform vec3 uDeepCol;
uniform vec3 uGlintCol;
uniform float uMirror;   // 0..1 how metallic the sea is
${SKY_GLSL}

// finite-difference ripple normal from two scrolling noise octaves
vec2 rippleGrad(vec2 p, float t) {
  vec2 q1 = p * 0.55 + vec2(t * 0.55, t * 0.31);
  vec2 q2 = p * 1.45 - vec2(t * 0.42, t * 0.66);
  float e = 0.09;
  float h  = vnoise2(q1) + 0.55 * vnoise2(q2);
  float hx = vnoise2(q1 + vec2(e, 0.0)) + 0.55 * vnoise2(q2 + vec2(e, 0.0));
  float hz = vnoise2(q1 + vec2(0.0, e)) + 0.55 * vnoise2(q2 + vec2(0.0, e));
  return vec2(hx - h, hz - h) / e;
}

void main() {
  vec3 V = cameraPosition - vWorldPos;
  float dist = length(V);
  V /= dist;

  // detail ripples fade with distance to avoid shimmer
  float detailFade = exp(-dist * 0.012);
  vec2 g = rippleGrad(vWorldPos.xz, uTime) * 0.075 * detailFade;
  vec3 N = normalize(vNormal + vec3(-g.x, 0.0, -g.y));

  vec3 R = reflect(-V, N);
  R.y = abs(R.y) + 0.018; // keep reflected rays above the mirror plane
  R = normalize(R);
  vec3 refl = skyColor(R, uTime);

  // mercury fresnel: high base reflectance, brighter at grazing angles
  float ndv = clamp(dot(N, V), 0.0, 1.0);
  float F = mix(0.55, 1.0, pow(1.0 - ndv, 3.0)) * uMirror;

  vec3 col = mix(uDeepCol, refl, F);

  // moonlight glitter paths — tight + broad lobes per moon
  vec3 HA = normalize(uMoonADir + V);
  vec3 HB = normalize(uMoonBDir + V);
  float specA = pow(max(dot(N, HA), 0.0), 720.0) * 5.0 + pow(max(dot(N, HA), 0.0), 48.0) * 0.08;
  float specB = pow(max(dot(N, HB), 0.0), 900.0) * 3.5 + pow(max(dot(N, HB), 0.0), 64.0) * 0.05;
  col += uMoonACol * specA * uGlintCol;
  col += uMoonBCol * specB * uGlintCol;

  // quicksilver crest streaks (clamped so a reflected moon disc can't blow up)
  float crest = smoothstep(0.45, 1.25, vCrest + vnoise2(vWorldPos.xz * 0.8 + uTime * 0.2) * 0.4);
  col += min(refl, vec3(0.9)) * crest * 0.30 * detailFade;

  // lightning sheen on the whole surface
  col += vec3(0.6, 0.68, 0.85) * uFlash * 0.06;

  // blend into the sky haze at the horizon (mesh edge is ~300m out, and the
  // dome continues with the same uSkyHorizon below the horizon line)
  float fog = 1.0 - exp(-pow(dist * 0.005, 1.8));
  col = mix(col, uSkyHorizon, fog);

  gl_FragColor = vec4(col, 1.0);
}
`;
