out vec3 sceneData;

/* DRAWBUFFERS:4 */

#include "/lib/Head/Common.inc"
#include "/lib/Head/Uniforms.inc"

// ======================================================================
// DOF CONFIG
// ======================================================================   
#define DOF_CHROMA_DISPERSION
#define DOF_SAMPLES 48 // [0.0 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0 21.0 22.0 23.0 24.0 25.0 26.0 27.0 28.0 29.0 30.0 35.0 40.0 48.0 50.0]
#define DOF_INTENSITY 0.02 // [0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0]
#define DOF_ANAMORPHIC_RATIO 1.0 // [0.0 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0 21.0 22.0 23.0 24.0 25.0 26.0 27.0 28.0 29.0 30.0]
#define FOCUS_MODE 0
#define MANUAL_FOCUS 5.0 // [0.0 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0 21.0 22.0 23.0 24.0 25.0 26.0 27.0 28.0 29.0 30.0]
#define CAMERA_APERTURE 2.8 // [0.0 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0 21.0 22.0 23.0 24.0 25.0 26.0 27.0 28.0 29.0 30.0]
#define DOF_APERTURE_SHAPE 2 // [0 1 2]
// 0 = circle
// 1 = hexagon
// 2 = octagon
#define DOF_CAT_EYE
#define CAT_EYE_STRENGTH 0.6 // [0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0]
#define MAX_COC_RADIUS 25.0 // [0.0 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0 6.5 7.0 7.5 8.0 8.5 9.0 9.5 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 19.0 20.0 21.0 22.0 23.0 24.0 25.0 26.0 27.0 28.0 29.0 30.0]
#define COC_SMOOTH_START 0.6 // [0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0]

// NEW ADVANCED FEATURES
#define DOF_BOKEH_HIGHLIGHTS // Підсилення яскравих областей в bokeh
#define BOKEH_HIGHLIGHT_THRESHOLD 0.8 // [0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0]
#define BOKEH_HIGHLIGHT_GAIN 2.5 // [1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0]

#define DOF_BOKEH_RING // Кільцевий ефект на краях bokeh (spherical aberration)
#define BOKEH_RING_INTENSITY 0.3 // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0]

#define DOF_CINEMATIC_BLUR // Кінематографічний розмитий перехід
#define CINEMATIC_BLUR_STRENGTH 1.2 // [0.5 0.8 1.0 1.2 1.5 2.0 2.5 3.0]

#define DOF_VIGNETTE_BLUR // Додатковий blur на краях екрану
#define VIGNETTE_BLUR_STRENGTH 0.4 // [0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0]

#define DOF_ADAPTIVE_SAMPLING // Адаптивна кількість семплів залежно від CoC
#define DOF_NEAR_BLUR_BOOST 1.5 // [1.0 1.2 1.4 1.6 1.8 2.0 2.5 3.0]

//----// FUNCTIONS //-----------------------------------------------------------------------------//

#include "/lib/Head/Functions.inc"

// Physical CoC
float CalculateCoC(float p, float z, float a, float f) {
    return oneMinus(p / z) * a * f * rcp(p - f);
}

// UE5-style CoC with better near-field handling
float CalculateCoCAdvanced(float p, float z, float a, float f) {
    float coc = oneMinus(p / z) * a * f * rcp(p - f);
    
    // Boost near-field blur (UE5 technique)
    if (z < p) {
        coc *= DOF_NEAR_BLUR_BOOST;
    }
    
    return coc;
}

// UE soft clamp
float ClampCoC(float coc) {
    float a = abs(coc);
    float c = mix(a, MAX_COC_RADIUS,
        smoothstep(MAX_COC_RADIUS * COC_SMOOTH_START, MAX_COC_RADIUS, a));
    return sign(coc) * c;
}

// ======================================================================
// APERTURE SHAPES
// ======================================================================

float PolygonMask(vec2 p, int sides) {
    float a = atan(p.y, p.x);
    float r = length(p);
    float k = TAU / float(sides);
    float d = cos(floor(0.5 + a / k) * k - a) * r;
    return smoothstep(1.0, 0.98, d);
}

// ======================================================================
// BOKEH EFFECTS (UE5-inspired)
// ======================================================================

// Spherical aberration ring effect
float BokehRing(vec2 offset, vec2 CoC) {
    float dist = length(offset / CoC);
    #ifdef DOF_BOKEH_RING
        return 1.0 + BOKEH_RING_INTENSITY * smoothstep(0.7, 1.0, dist);
    #else
        return 1.0;
    #endif
}

// Highlight boost for bright areas (creates cinematic bokeh)
float BokehHighlightWeight(vec3 color) {
    #ifdef DOF_BOKEH_HIGHLIGHTS
        float luma = dot(color, vec3(0.299, 0.587, 0.114));
        float highlight = smoothstep(BOKEH_HIGHLIGHT_THRESHOLD, 1.0, luma);
        return 1.0 + highlight * BOKEH_HIGHLIGHT_GAIN;
    #else
        return 1.0;
    #endif
}

// ======================================================================
// CAT-EYE
// ======================================================================

vec2 ApplyCatEye(vec2 offset, vec2 uv) {
    vec2 fromCenter = uv - 0.5;
    float vignette = length(fromCenter) * 2.0;
    float squeeze = mix(1.0, 1.0 - CAT_EYE_STRENGTH, vignette);
    return vec2(offset.x * squeeze, offset.y);
}

// ======================================================================
// CINEMATIC EFFECTS
// ======================================================================

// Vignette-based CoC modifier
float VignetteBlur(vec2 uv) {
    #ifdef DOF_VIGNETTE_BLUR
        vec2 fromCenter = (uv - 0.5) * 2.0;
        float vignette = length(fromCenter);
        return 1.0 + VIGNETTE_BLUR_STRENGTH * vignette * vignette;
    #else
        return 1.0;
    #endif
}

// Cinematic smooth falloff
float CinematicFalloff(float coc) {
    #ifdef DOF_CINEMATIC_BLUR
        float normalized = abs(coc) / MAX_COC_RADIUS;
        return pow(normalized, 1.0 / CINEMATIC_BLUR_STRENGTH);
    #else
        return abs(coc) / MAX_COC_RADIUS;
    #endif
}

//----// MAIN //----------------------------------------------------------------------------------//

void main() {
    ivec2 texel = ivec2(gl_FragCoord.xy);
    vec2 screenCoord = gl_FragCoord.xy * screenPixelSize;

    float depth = texelFetch(depthtex1, texel, 0).x;
    if (depth < 0.56) {
        sceneData = texelFetch(colortex4, texel, 0).rgb;
        return;
    }

    // ===== FOCUS =====
    #if FOCUS_MODE == 0
        float focusDist = texelFetch(colortex5, ivec2(1), 0).a;
    #else
        float focus = far * (MANUAL_FOCUS - near) / ((far - near) * MANUAL_FOCUS);
        float focusDist = GetDepthLinear(focus);
    #endif

    // ===== CAMERA =====
    const float focalLength = 0.5 * 0.035 * gbufferProjection[1][1];
    const float aperture = focalLength / CAMERA_APERTURE;

    float dist = GetDepthLinear(depth);

    // Use advanced CoC calculation
    float coc = CalculateCoCAdvanced(focusDist, dist, aperture, focalLength);
    coc = ClampCoC(coc);

    // Apply cinematic falloff
    float cocFalloff = CinematicFalloff(coc);
    
    // Apply vignette blur modifier
    float vignetteModifier = VignetteBlur(screenCoord);

    vec2 CoC = vec2(coc * DOF_ANAMORPHIC_RATIO, coc * aspectRatio);
    CoC *= screenSize * DOF_INTENSITY * 1e3 * vignetteModifier;

    // ===== ADAPTIVE SAMPLING =====
    #ifdef DOF_ADAPTIVE_SAMPLING
        float cocSize = length(CoC);
        uint adaptiveSamples = uint(mix(float(DOF_SAMPLES) * 0.5, float(DOF_SAMPLES), 
            smoothstep(0.0, MAX_COC_RADIUS * 0.5, cocSize)));
        adaptiveSamples = max(adaptiveSamples, 8u);
    #else
        uint adaptiveSamples = DOF_SAMPLES;
    #endif

    // ===== SAMPLING =====
    const float goldenAngle = TAU / (PHI1 + 1.0);
    const mat2 goldenRot = mat2(
        cos(goldenAngle), -sin(goldenAngle),
        sin(goldenAngle),  cos(goldenAngle)
    );

    float noise = BlueNoiseTemporal();
    vec2 rot = cossin(noise * TAU) * CoC;

    sceneData = vec3(0.0);
    float weightSum = 0.0;

    const float rSteps = 1.0 / float(adaptiveSamples);

    #ifdef DOF_CHROMA_DISPERSION
        vec2 chromaDir = normalize(screenCoord - 0.5);
        float chromaStrength = length(CoC) * 0.5;
        ivec2 chromaOffset = ivec2(chromaDir * chromaStrength);
    #endif

    for (uint i = 0u; i < adaptiveSamples; ++i, rot *= goldenRot) {
        float r = sqrt((noise + float(i)) * rSteps);
        vec2 offset = rot * r;

        #ifdef DOF_CAT_EYE
            offset = ApplyCatEye(offset, screenCoord);
        #endif

        float shapeMask = 1.0;

        #if DOF_APERTURE_SHAPE == 1
            shapeMask = PolygonMask(offset / CoC, 6);
        #elif DOF_APERTURE_SHAPE == 2
            shapeMask = PolygonMask(offset / CoC, 8);
        #endif

        // Apply bokeh ring effect
        float ringEffect = BokehRing(offset, CoC);

        #ifdef DOF_CHROMA_DISPERSION
            ivec2 sc = texel + ivec2(offset);
            vec3 sampleColor;
            sampleColor.r = texelFetch(colortex4, sc + chromaOffset, 0).r;
            sampleColor.g = texelFetch(colortex4, sc, 0).g;
            sampleColor.b = texelFetch(colortex4, sc - chromaOffset, 0).b;
        #else
            vec3 sampleColor = texelFetch(colortex4, texel + ivec2(offset), 0).rgb;
        #endif

        // Calculate sample weight with all effects
        float highlightWeight = BokehHighlightWeight(sampleColor);
        float w = shapeMask * ringEffect * highlightWeight;
        
        weightSum += w;
        sceneData += sampleColor * w;
    }

    sceneData /= max(weightSum, 1e-4);
}