#version 450 core
// Purpose: Lit-model vertex shader with optional water displacement.
// The water path computes the same wave height and its analytic derivatives,
// then derives a world-space normal from the displaced surface.

layout(location=0) in vec3 aPos;
layout(location=1) in vec3 aNormal;
layout(location=2) in vec2 aUv;

uniform mat4 uModel;
uniform mat4 uMvp;
uniform mat4 uViewProj;

uniform int uWaterWaveEnabled;
uniform float uWaterWaveTime;
uniform float uWaterWaveAmplitude;
uniform float uWaterWaveFrequency;
uniform float uWaterWaveSpeed;

out vec3 vWorldPos;
out vec3 vWorldNormal;
out vec2 vUv;

float WaterWaveHeight(vec3 worldPos)
{
    float waveA = sin(worldPos.x * uWaterWaveFrequency + uWaterWaveTime * uWaterWaveSpeed);
    float waveB = sin((worldPos.z + worldPos.x * 0.35) * (uWaterWaveFrequency * 0.65) +
                      uWaterWaveTime * (uWaterWaveSpeed * 0.72));

    return uWaterWaveAmplitude * (0.70 * waveA + 0.30 * waveB);
}

vec3 WaterWaveNormal(vec3 worldPos)
{
    float argA = worldPos.x * uWaterWaveFrequency + uWaterWaveTime * uWaterWaveSpeed;
    float argB = (worldPos.z + worldPos.x * 0.35) * (uWaterWaveFrequency * 0.65) +
                 uWaterWaveTime * (uWaterWaveSpeed * 0.72);

    float dHeightDx =
        uWaterWaveAmplitude *
        (0.70 * cos(argA) * uWaterWaveFrequency +
         0.30 * cos(argB) * (0.35 * uWaterWaveFrequency * 0.65));

    float dHeightDz =
        uWaterWaveAmplitude *
        (0.30 * cos(argB) * (uWaterWaveFrequency * 0.65));

    // Surface P(x,z) = vec3(x, height(x,z), z).
    // dP/dx = (1, dHeightDx, 0), dP/dz = (0, dHeightDz, 1).
    // cross(dP/dz, dP/dx) gives the upward-facing normal.
    return normalize(vec3(-dHeightDx, 1.0, -dHeightDz));
}

void main()
{
    vec4 worldPos = uModel * vec4(aPos, 1.0);

    if (uWaterWaveEnabled != 0)
    {
        worldPos.y += WaterWaveHeight(worldPos.xyz);
        vWorldPos = worldPos.xyz;
        vWorldNormal = WaterWaveNormal(worldPos.xyz);
        gl_Position = uViewProj * worldPos;
    }
    else
    {
        mat3 normalMat = mat3(transpose(inverse(uModel)));
        vWorldPos = worldPos.xyz;
        vWorldNormal = normalize(normalMat * aNormal);
        gl_Position = uMvp * vec4(aPos, 1.0);
    }

    vUv = aUv;
}
