#ifndef VOXELIZATION_GLSL
#define VOXELIZATION_GLSL

// Voxelization.glsl - Scene Voxelization for VXGI
// Converts rendered geometry into 3D voxel grid

#include "VoxelStructure.glsl"

//============================================================================//
// VOXELIZATION SETTINGS
//============================================================================//

#ifndef VOXEL_OPACITY_THRESHOLD
#define VOXEL_OPACITY_THRESHOLD 0.1
#endif

#ifndef VOXEL_CONSERVATIVE_RASTER
#define VOXEL_CONSERVATIVE_RASTER 1 // [0 1]
#endif

//============================================================================//
// OPACITY CALCULATION
//============================================================================//

// Calculate voxel opacity from alpha and material properties
float CalculateVoxelOpacity(float alpha, float materialID, float depth) {
    // Full opacity for solid blocks
    if (alpha > 0.99) return 1.0;
    
    // Partial opacity for transparent materials
    if (alpha > VOXEL_OPACITY_THRESHOLD) return alpha;
    
    // Skip fully transparent
    return 0.0;
}

//============================================================================//
// VOXELIZATION - ATOMIC OPERATIONS
//============================================================================//

#ifdef VOXEL_USE_IMAGE_ATOMIC

// Atomically update voxel with max operation
void VoxelAtomicMax(image3D voxelImage, ivec3 voxel, vec4 value) {
    // Note: GLSL doesn't have native imageAtomicMax for float
    // This is a simplified version - full implementation requires integer packing
    
    // Read current value
    vec4 current = imageLoad(voxelImage, voxel);
    
    // Take maximum RGB components and sum alpha
    vec4 result;
    result.rgb = max(current.rgb, value.rgb);
    result.a = min(1.0, current.a + value.a);
    
    // Write back
    imageStore(voxelImage, voxel, result);
}

#endif

//============================================================================//
// FRAGMENT VOXELIZATION
//============================================================================//

// Voxelize a single fragment
void VoxelizeFragment(
    vec3 worldPos,
    vec3 albedo,
    vec3 normal,
    float alpha,
    float materialID,
    writeonly image3D voxelAlbedoImage
) {
    // Convert world position to voxel coordinates
    ivec3 voxel = WorldToVoxel(worldPos);
    
    // Check bounds
    if (!IsVoxelInBounds(voxel)) return;
    
    // Calculate opacity
    float opacity = CalculateVoxelOpacity(alpha, materialID, 0.0);
    if (opacity < VOXEL_OPACITY_THRESHOLD) return;
    
    // Pack voxel data
    vec4 voxelData = PackVoxelData(albedo, opacity);
    
    #ifdef VOXEL_USE_IMAGE_ATOMIC
        VoxelAtomicMax(voxelAlbedoImage, voxel, voxelData);
    #else
        // Simple overwrite (may cause flickering)
        imageStore(voxelAlbedoImage, voxel, voxelData);
    #endif
}

//============================================================================//
// CONSERVATIVE RASTERIZATION
//============================================================================//

#if VOXEL_CONSERVATIVE_RASTER == 1

// Voxelize with conservative rasterization
// Ensures no voxels are missed for thin geometry
void VoxelizeFragmentConservative(
    vec3 worldPos,
    vec3 albedo,
    vec3 normal,
    float alpha,
    float materialID,
    vec2 screenSize,
    writeonly image3D voxelAlbedoImage
) {
    // Base voxel
    ivec3 baseVoxel = WorldToVoxel(worldPos);
    
    // Calculate screen-space derivatives
    vec3 dFdxPos = dFdx(worldPos);
    vec3 dFdyPos = dFdy(worldPos);
    
    // Estimate voxel coverage
    float coverage = max(length(dFdxPos), length(dFdyPos)) / VOXEL_SIZE;
    
    if (coverage > 0.5) {
        // Large coverage - voxelize neighboring voxels too
        float opacity = CalculateVoxelOpacity(alpha, materialID, 0.0);
        if (opacity < VOXEL_OPACITY_THRESHOLD) return;
        
        vec4 voxelData = PackVoxelData(albedo, opacity * 0.5); // Reduce opacity for neighbors
        
        // Voxelize 3x3x3 neighborhood
        for (int x = -1; x <= 1; x++) {
            for (int y = -1; y <= 1; y++) {
                for (int z = -1; z <= 1; z++) {
                    ivec3 voxel = baseVoxel + ivec3(x, y, z);
                    if (IsVoxelInBounds(voxel)) {
                        vec4 data = (x == 0 && y == 0 && z == 0) ? 
                                    PackVoxelData(albedo, opacity) : voxelData;
                        imageStore(voxelAlbedoImage, voxel, data);
                    }
                }
            }
        }
    } else {
        // Normal voxelization
        VoxelizeFragment(worldPos, albedo, normal, alpha, materialID, voxelAlbedoImage);
    }
}

#endif

//============================================================================//
// VOXEL GRID CLEARING
//============================================================================//

// Clear voxel region (for camera movement updates)
void ClearVoxelRegion(
    ivec3 start,
    ivec3 end,
    writeonly image3D voxelAlbedoImage,
    writeonly image3D voxelGIImage
) {
    for (int x = start.x; x < end.x; x++) {
        for (int y = start.y; y < end.y; y++) {
            for (int z = start.z; z < end.z; z++) {
                ivec3 voxel = ivec3(x, y, z);
                if (IsVoxelInBounds(voxel)) {
                    imageStore(voxelAlbedoImage, voxel, vec4(0.0));
                    imageStore(voxelGIImage, voxel, vec4(0.0));
                }
            }
        }
    }
}

//============================================================================//
// VOXEL GRID UPDATE ON CAMERA MOVEMENT
//============================================================================//

// Shift voxel grid when camera moves
void ShiftVoxelGrid(
    ivec3 offset,
    writeonly image3D voxelAlbedoImage,
    writeonly image3D voxelGIImage,
    image3D tempBuffer
) {
    // Copy to temp buffer with offset
    for (int x = 0; x < VOXEL_AREA; x++) {
        for (int y = 0; y < VOXEL_AREA; y++) {
            for (int z = 0; z < VOXEL_AREA; z++) {
                ivec3 srcVoxel = ivec3(x, y, z);
                ivec3 dstVoxel = srcVoxel - offset;
                
                if (IsVoxelInBounds(dstVoxel)) {
                    vec4 albedo = imageLoad(voxelAlbedoImage, srcVoxel);
                    vec4 gi = imageLoad(voxelGIImage, srcVoxel);
                    imageStore(tempBuffer, dstVoxel, albedo);
                } else {
                    imageStore(tempBuffer, srcVoxel, vec4(0.0));
                }
            }
        }
    }
    
    // Copy back from temp
    for (int x = 0; x < VOXEL_AREA; x++) {
        for (int y = 0; y < VOXEL_AREA; y++) {
            for (int z = 0; z < VOXEL_AREA; z++) {
                ivec3 voxel = ivec3(x, y, z);
                vec4 data = imageLoad(tempBuffer, voxel);
                imageStore(voxelAlbedoImage, voxel, data);
            }
        }
    }
}

//============================================================================//
// INTEGRATION HELPERS
//============================================================================//

// Main voxelization function to call from fragment shader
void VoxelizeScene(
    vec3 worldPos,
    vec3 viewPos,
    vec3 albedo,
    vec3 normal,
    float alpha,
    float depth,
    vec2 screenSize,
    writeonly image3D voxelAlbedoImage
) {
    // Skip sky
    if (depth >= 1.0) return;
    
    // Skip if too far from camera
    float distToCamera = length(viewPos);
    if (distToCamera > float(VOXEL_RADIUS) * 0.9) return;
    
    #if VOXEL_CONSERVATIVE_RASTER == 1
        VoxelizeFragmentConservative(worldPos, albedo, normal, alpha, 0.0, screenSize, voxelAlbedoImage);
    #else
        VoxelizeFragment(worldPos, albedo, normal, alpha, 0.0, voxelAlbedoImage);
    #endif
}

#endif // VOXELIZATION_GLSL
