// ============================================================================
// Selective (hue-isolated) color grading, ported from Photon Shaders
// (c) 2021-2025 Benjamin Stott ("SixthSurge") - see NOTICE_PHOTON_GRADE_PORT.txt
// Source: shaders/program/c14_color_grading.fsh (grade_output, vignette)
//         and shaders/include/utility/color.glsl (rgb_to_hsl, hsl_to_rgb,
//         isolate_hue) / shaders/include/global.glsl (pulse, cubic_smooth)
// from Photon Shaders v1.3b, ported and adapted to Unbound Visual + PT v9.6's pipeline.
//
// This is deliberately kept separate from Unbound's existing
// DoBSLColorSaturation()/DoWarmMidtoneBlueShadowGrade() - it's a different
// technique (per-HUE isolate-and-boost "color blending", not a flat global
// vibrance push), meant to sit alongside them, not replace them.
// ============================================================================
#ifndef INCLUDE_SELECTIVE_COLOR_GRADE
    #define INCLUDE_SELECTIVE_COLOR_GRADE

    // --- small math helpers (Photon: include/global.glsl) ---
    float SCG_CubicSmooth(float x) { return pow2(x) * (3.0 - 2.0 * x); }

    // Remaps center +/- 0.5*width to zero and center to 1 (cyclic over `period`,
    // e.g. degrees on a 360 hue wheel) - used to build a soft "is this pixel
    // roughly hue X?" mask.
    float SCG_Pulse(float x, float center, float width, float period) {
        x = (x - center + 0.5 * period) / period;
        x = fract(x) * period - (0.5 * period);
        x = abs(x) / width;
        return x > 1.0 ? 0.0 : 1.0 - SCG_CubicSmooth(x);
    }

    // --- RGB <-> HSL (Photon: include/utility/color.glsl) ---
    vec3 SCG_RgbToHsl(vec3 c) {
        const vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
        vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
        vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
        float d = q.x - min(q.w, q.y);
        float e = 1e-6;
        return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
    }

    vec3 SCG_HslToRgb(vec3 c) {
        const vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
        c.yz = clamp01(c.yz);
        vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
        return c.z * mix(K.xxx, clamp01(p - K.xxx), c.y);
    }

    // How strongly a pixel belongs to a hue band centered at `center` degrees
    float SCG_IsolateHue(vec3 hsl, float center, float width) {
        if (hsl.y < 1e-2 || hsl.z < 1e-2) return 0.0; // skip black/gray, no defined hue
        return SCG_Pulse(hsl.x * 360.0, center, width, 360.0);
    }

    // Soft-contrast S-curve (Photon: gain()), used as a gentle final polish pass
    vec3 SCG_Gain(vec3 x, float k) {
        vec3 a = 0.5 * pow(2.0 * mix(x, 1.0 - x, step(0.5, x)), vec3(k));
        return mix(a, 1.0 - a, step(0.5, x));
    }

    // ------------------------------------------------------------------
    // Main entry point. Call AFTER tonemapping, on display-referred [0,1]
    // color (same stage Photon applies grade_output at).
    // ------------------------------------------------------------------
    void DoSelectiveColorGrade(inout vec3 color) {
        // Work in roughly perceptual space, like Photon does, so the hue
        // masks and saturation pushes behave consistently across the
        // brightness range instead of being skewed toward shadows.
        vec3 c = sqrt(clamp01(color));

        vec3 hsl = SCG_RgbToHsl(c);

        // Oranges (skin, torches, wood, golden-hour light) - centered 30deg
        float orange = SCG_IsolateHue(hsl, 30.0, 20.0);
        hsl.y *= 1.0 + SELECTIVE_GRADE_ORANGE * orange;

        // Teals/cyans (the "too blue/blue-green" range) - centered 210deg.
        // Negative SELECTIVE_GRADE_TEAL actively de-saturates this band.
        float teal = SCG_IsolateHue(hsl, 210.0, 20.0);
        hsl.y *= 1.0 + SELECTIVE_GRADE_TEAL * teal;

        // Greens (grass/foliage) - centered 90deg, with its own hue shift so
        // it can be pulled toward lime/yellow-green independent of saturation.
        float green = SCG_IsolateHue(hsl, 90.0, 44.0);
        hsl.x += (SELECTIVE_GRADE_GREEN_HUE_SHIFT / 360.0) * green;
        hsl.y *= 1.0 + SELECTIVE_GRADE_GREEN_SAT * green;

        c = SCG_HslToRgb(hsl);
        c = SCG_Gain(c, 1.0 + 0.05 * SELECTIVE_GRADE_GAIN);

        color = pow2(clamp01(c));
    }

    // ------------------------------------------------------------------
    // Vignette (Photon: c14_color_grading.fsh, vignette()), adapted to
    // Unbound's uniforms (no biome_cave uniform here, so that term is
    // dropped; darknessFactor/blindness cover the same "player is
    // impaired" intent).
    // ------------------------------------------------------------------
    float SCG_Vignette(vec2 uv, float frameTimeCounter, float blindnessM, float darknessFactorM) {
        const float vignetteSize = 16.0;
        const float vignetteIntensity = 0.08 * VIGNETTE_INTENSITY;

        float darknessPulse = 1.0 - SCG_CubicSmooth(clamp01(abs(cos(2.0 * frameTimeCounter))));

        float v = vignetteSize * (uv.x * uv.y - uv.x) * (uv.x * uv.y - uv.y);
        v = pow(max0(v), vignetteIntensity + 0.3 * blindnessM + 0.2 * darknessPulse * darknessFactorM);

        return v;
    }

#endif //INCLUDE_SELECTIVE_COLOR_GRADE
