#version 330 core
in vec2 vUV;
in float vValue;
in vec3 vWorldPos;
out vec4 FragColor;

uniform float uMinV;
uniform float uMaxV;
uniform sampler2D uDotTex;
uniform int uHasData; // 0 = no data, 1 = has data
uniform vec3 uCameraPos;
uniform float uDotRadius;
uniform int uRenderMode; // 0=realistic, 1=dataViz

const float PI = 3.14159265359;

float saturate(float x) { return clamp(x, 0.0, 1.0); }

vec3 dataColorRamp(float t) {
    t = saturate(t);
    vec3 c0 = vec3(0.10, 0.75, 1.00); // cyan-blue (low)
    vec3 c1 = vec3(0.10, 0.95, 0.35); // green
    vec3 c2 = vec3(1.00, 0.92, 0.22); // yellow
    vec3 c3 = vec3(1.00, 0.22, 0.10); // red (high)

    if (t < 0.33) return mix(c0, c1, t / 0.33);
    if (t < 0.66) return mix(c1, c2, (t - 0.33) / 0.33);
    return mix(c2, c3, (t - 0.66) / 0.34);
}

vec3 fresnelSchlick(float cosTheta, vec3 F0) {
    return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}

float D_GGX(float NdotH, float roughness) {
    float a = max(0.04, roughness);
    float alpha = a * a;
    float alpha2 = alpha * alpha;
    float denom = (NdotH * NdotH) * (alpha2 - 1.0) + 1.0;
    return alpha2 / (PI * denom * denom + 1e-7);
}

float G_SchlickGGX(float NdotV, float roughness) {
    float r = roughness + 1.0;
    float k = (r * r) / 8.0;
    return NdotV / (NdotV * (1.0 - k) + k + 1e-7);
}

float G_Smith(float NdotV, float NdotL, float roughness) {
    float ggx1 = G_SchlickGGX(NdotV, roughness);
    float ggx2 = G_SchlickGGX(NdotL, roughness);
    return ggx1 * ggx2;
}

float D_GGX_Aniso(vec3 N, vec3 H, vec3 T, vec3 B, float ax, float ay) {
    float NdotH = saturate(dot(N, H));
    float TdotH = dot(T, H);
    float BdotH = dot(B, H);
    float ax2 = ax * ax;
    float ay2 = ay * ay;
    float denom = (TdotH * TdotH) / (ax2 + 1e-7) + (BdotH * BdotH) / (ay2 + 1e-7) + NdotH * NdotH;
    return 1.0 / (PI * ax * ay * denom * denom + 1e-7);
}

vec3 evalLight(
    vec3 N,
    vec3 V,
    vec3 L,
    vec3 lightColor,
    vec3 baseColor,
    float metallic,
    float roughness,
    float aniso,
    vec3 brushDir
) {
    float NdotL = saturate(dot(N, L));
    float NdotV = saturate(dot(N, V));
    if (NdotL <= 0.0 || NdotV <= 0.0) return vec3(0.0);

    vec3 H = normalize(V + L);
    float NdotH = saturate(dot(N, H));
    float VdotH = saturate(dot(V, H));

    vec3 F0 = mix(vec3(0.04), baseColor, metallic);
    vec3 F = fresnelSchlick(VdotH, F0);

    float D = D_GGX(NdotH, roughness);
    if (aniso > 0.001) {
        vec3 T = normalize(brushDir - N * dot(brushDir, N));
        vec3 B = normalize(cross(N, T));
        float alpha = max(0.04, roughness);
        float a = alpha * alpha;
        float ax = mix(a, a * 0.30, aniso);
        float ay = mix(a, a * 2.00, aniso);
        D = D_GGX_Aniso(N, H, T, B, ax, ay);
    }

    float G = G_Smith(NdotV, NdotL, roughness);
    vec3 spec = (D * G * F) / max(4.0 * NdotV * NdotL, 1e-6);

    vec3 kD = (vec3(1.0) - F) * (1.0 - metallic);
    vec3 diff = kD * baseColor / PI;

    return (diff + spec) * lightColor * NdotL;
}

void main() {
    vec2 p = vUV * 2.0 - 1.0;
    float r = length(p);
    if (r > 1.0) discard;
    float r01 = saturate(r);

    // Industrial engineering model: simple plated metal pad (brass/gold-ish).
    // When no data, keep a bright gold base. When data is present, render the
    // data color directly (no remaining gold tint), while preserving depth cues.
    vec3 metalBase = vec3(0.98, 0.82, 0.30);
    float value01 = clamp((vValue - uMinV) / max(1e-6, (uMaxV - uMinV)), 0.0, 1.0);
    vec3 dataCol = dataColorRamp(value01);

    // bool hasData = (uHasData != 0);
    // vec3 baseColor = hasData ? dataCol : metalBase;
    vec3 baseColor = metalBase;

    // dataViz: flat/unlit, no lighting modulation (keep pure baseColor)
    if (uRenderMode == 1) {
        FragColor = vec4(clamp(baseColor, 0.0, 1.0), 1.0);
        return;
    }

    // Mostly flat, with a slight bevel near the edge to catch highlights.
    float slope = mix(0.06, 0.28, smoothstep(0.55, 1.0, r01));
    // Face the camera: dots live on the panel front face (XY plane), so the base normal points -Z.
    // vec3 N = normalize(vec3(p.x * slope, p.y * slope, -1.0));
    vec3 N = normalize(vec3(0.0, 0.15, -1.0));
    vec3 V = normalize(uCameraPos - vWorldPos);

    // float metallic = hasData ? 0.0 : 0.90;
    // float roughness = hasData ? 0.78 : ((uRenderMode == 1) ? 0.70 : 0.55);
    float metallic = 0.90;
    float roughness = 0.55;

    // "Front light": make the light come from the camera direction (like a headlight/flashlight).
    vec3 keyL = V;
    vec3 fillL = V;
    vec3 keyC = vec3(1.00, 0.98, 0.95) * 1.8;
    vec3 fillC = vec3(0.85, 0.90, 1.00) * 0.9;

    vec3 Lo = vec3(0.0);
    Lo += evalLight(N, V, keyL, keyC, baseColor, metallic, roughness, 0.0, vec3(1.0, 0.0, 0.0));
    Lo += evalLight(N, V, fillL, fillC, baseColor, metallic, roughness, 0.0, vec3(1.0, 0.0, 0.0));

    vec3 F0 = mix(vec3(0.04), baseColor, metallic);
    vec3 ambient = baseColor * 0.10 + F0 * 0.04;

    float edgeAO = smoothstep(0.88, 1.0, r01);
    float ao = 1.0 - edgeAO * 0.10;

    // Subtle boundary ring (engineering-model crispness, not a UI outline).
    float ring = smoothstep(0.82, 0.92, r01) - smoothstep(0.92, 1.00, r01);

    vec3 col = (ambient + Lo) * ao;
    col = mix(col, col * 0.82, ring * 0.35);

    FragColor = vec4(clamp(col, 0.0, 1.0), 1.0);
}
