#version 430 core // Need SSBOs and atomic counters

// Input Texture (the processed image ready for display)
// Binding = 0 matches glBindImageTexture unit
// Use rgba8 format as we assume display texture is 8-bit sRGB (adjust if needed)
layout(binding = 0, rgba8) uniform readonly image2D InputTexture;

// Output Histogram Buffer (SSBO)
// Binding = 1 matches glBindBufferBase index
// Contains 256 bins for R, 256 for G, 256 for B sequentially (total 768 uints)
layout(std430, binding = 1) buffer HistogramBuffer {
    uint bins[]; // Use an unsized array
} histogram;

// Workgroup size (adjust based on GPU architecture for performance, 16x16 is often reasonable)
layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in;


// Helper to convert linear float (potentially HDR 0.0+) to 0-255 bin index
// Applies an approximate gamma curve for perceptual binning.
uint linearToVisualBin(float linearVal) {
    // Clamp linear value to 0.0-1.0 range before gamma/binning
    // This histograms the displayable range after processing.
    float clampedVal = clamp(linearVal, 0.0, 1.0);
    // Apply approximate sRGB gamma for perceptual brightness mapping
    float displayApprox = pow(clampedVal, 1.0/2.2);
    // Convert 0.0-1.0 display value to 0-255 bin index
    return uint(displayApprox * 255.999); // Use 255.999 to ensure 1.0 maps to 255
}

void main() {
    // Get the global invocation ID (like pixel coordinates)
    ivec2 pixelCoord = ivec2(gl_GlobalInvocationID.xy);
    ivec2 textureSize = imageSize(InputTexture);

    // Boundary check: Don't process outside the image bounds
    if (pixelCoord.x >= textureSize.x || pixelCoord.y >= textureSize.y) {
        return;
    }

    // Load the pixel color (values are normalized float 0.0-1.0 from rgba8 image load)
    vec4 pixelColor = imageLoad(InputTexture, pixelCoord);

    // Calculate bin indices (0-255)
    // We clamp just in case, although imageLoad from rgba8 should be in range.
    uint rBin = linearToVisualBin(pixelColor.r);
    uint gBin = linearToVisualBin(pixelColor.g);
    uint bBin = linearToVisualBin(pixelColor.b);
    // Atomically increment the counters in the SSBO
    // Offset Green bins by 256, Blue bins by 512
    atomicAdd(histogram.bins[rBin],       1u);
    atomicAdd(histogram.bins[gBin + 256u], 1u);
    atomicAdd(histogram.bins[bBin + 512u], 1u);

    // Optional: Track Max Value (more complex, requires another SSBO or different strategy)
    // Example: atomicMax(histogram.maxBinValue, histogram.bins[rBin]); // Needs careful sync
}