#ifndef VOXEL_SAMPLING_GLSL
#define VOXEL_SAMPLING_GLSL

// VoxelSampling.glsl - Sample Global Illumination from Voxel Grid
// Reads propagated light and applies to scene

#include "VoxelStructure.glsl"

//============================================================================//
// SAMPLING SETTINGS
//============================================================================//

#ifndef VXGI_SAMPLE_METHOD
#define VXGI_SAMPLE_METHOD 1 // [0 1 2] 0=Point 1=Trilinear 2=Cone
#endif

#ifndef VXGI_INTENSITY
#define VXGI_INTENSITY 1.0 // [0.5 0.75 1.0 1.25 1.5 2.0]
#endif

#ifndef VXGI_NORMAL_OFFSET
#define VXGI_NORMAL_OFFSET 0.5 // [0.0 0.25 0.5 1.0]
#endif

//============================================================================//
// POINT SAMPLING
//============================================================================//

vec3 SampleVoxelGI_Point(vec3 worldPos, sampler3D voxelGITex) {
    ivec3 voxel = WorldToVoxel(worldPos);
    
    if (!IsVoxelInBounds(voxel)) return vec3(0.0);
    
    vec3 texCoord = VoxelToTexCoord(voxel);
    return texture(voxelGITex, texCoord).rgb;
}

//============================================================================//
// TRILINEAR SAMPLING
//============================================================================//

vec3 SampleVoxelGI_Trilinear(vec3 worldPos, sampler3D voxelGITex) {
    vec3 texCoord = WorldToTexCoord(worldPos);
    
    // Clamp to valid range
    texCoord = clamp(texCoord, 
                     vec3(0.5 / float(VOXEL_AREA)), 
                     vec3(1.0 - 0.5 / float(VOXEL_AREA)));
    
    return texture(voxelGITex, texCoord).rgb;
}

//============================================================================//
// CONE TRACING (Higher Quality)
//============================================================================//

#if VXGI_SAMPLE_METHOD == 2

struct ConeTraceResult {
    vec3 color;
    float occlusion;
};

// Trace a cone through voxel grid
ConeTraceResult TraceCone(
    vec3 origin,
    vec3 direction,
    float aperture,
    float maxDistance,
    sampler3D voxelAlbedoTex,
    sampler3D voxelGITex
) {
    ConeTraceResult result;
    result.color = vec3(0.0);
    result.occlusion = 0.0;
    
    float dist = VOXEL_SIZE; // Start one voxel away
    float diameter = 0.0;
    
    // March along cone
    while (dist < maxDistance && result.occlusion < 0.95) {
        // Calculate cone diameter at current distance
        diameter = 2.0 * tan(aperture) * dist;
        
        // Calculate mip level based on cone diameter
        float mipLevel = log2(diameter / VOXEL_SIZE);
        
        // Sample position
        vec3 samplePos = origin + direction * dist;
        vec3 texCoord = WorldToTexCoord(samplePos);
        
        // Sample voxel data
        vec4 albedo = textureLod(voxelAlbedoTex, texCoord, mipLevel);
        vec4 gi = textureLod(voxelGITex, texCoord, mipLevel);
        
        // Accumulate color
        float weight = (1.0 - result.occlusion);
        result.color += gi.rgb * albedo.a * weight;
        result.occlusion += albedo.a * weight;
        
        // Step forward
        dist += diameter * 0.5;
    }
    
    return result;
}

// Sample using multiple cones for better quality
vec3 SampleVoxelGI_Cone(
    vec3 worldPos,
    vec3 normal,
    sampler3D voxelAlbedoTex,
    sampler3D voxelGITex
) {
    // Offset origin along normal to avoid self-intersection
    vec3 origin = worldPos + normal * VOXEL_SIZE * VXGI_NORMAL_OFFSET;
    
    // Trace multiple cones in hemisphere
    const int NUM_CONES = 6;
    const float coneAperture = 0.57; // ~30 degrees
    const float maxDist = float(VOXEL_RADIUS) * 0.5;
    
    // Cone directions (roughly hemisphere) — используем C-style initializer
    const vec3 coneDirections[6] = {
        normalize(normal),
        normalize(normal + vec3(1.0, 0.0, 0.0) * 0.5),   // заменено на упрощённый tangent
        normalize(normal - vec3(1.0, 0.0, 0.0) * 0.5),
        normalize(normal + vec3(0.0, 0.0, 1.0) * 0.5),
        normalize(normal - vec3(0.0, 0.0, 1.0) * 0.5),
        normalize(normal + vec3(1.0, 0.0, 0.0) * 0.25 + vec3(0.0, 0.0, 1.0) * 0.25)
    };
    
    // Trace cones
    vec3 totalGI = vec3(0.0);
    float totalWeight = 0.0;
    
    for (int i = 0; i < NUM_CONES; i++) {
        ConeTraceResult cone = TraceCone(
            origin,
            coneDirections[i],
            coneAperture,
            maxDist,
            voxelAlbedoTex,
            voxelGITex
        );
        
        totalGI += cone.color;
        totalWeight += 1.0;
    }
    
    if (totalWeight > 0.001) {
        return totalGI / totalWeight;
    }
    return vec3(0.0);
}

#endif

//============================================================================//
// MAIN SAMPLING FUNCTION
//============================================================================//

// Sample voxel GI at world position with normal
vec3 SampleVoxelGI(
    vec3 worldPos,
    vec3 normal,
    sampler3D voxelAlbedoTex,
    sampler3D voxelGITex
) {
    // Offset position along normal to avoid self-shadowing
    vec3 samplePos = worldPos + normal * VOXEL_SIZE * VXGI_NORMAL_OFFSET;
    
    // Choose sampling method
    vec3 gi;
    
    #if VXGI_SAMPLE_METHOD == 0
        // Point sampling (fastest)
        gi = SampleVoxelGI_Point(samplePos, voxelGITex);
        
    #elif VXGI_SAMPLE_METHOD == 1
        // Trilinear sampling (good quality/performance)
        gi = SampleVoxelGI_Trilinear(samplePos, voxelGITex);
        
    #elif VXGI_SAMPLE_METHOD == 2
        // Cone tracing (highest quality)
        gi = SampleVoxelGI_Cone(samplePos, normal, voxelAlbedoTex, voxelGITex);
    #endif
    
    // Apply intensity
    gi *= VXGI_INTENSITY;
    
    return gi;
}

//============================================================================//
// SIMPLIFIED INTERFACE
//============================================================================//

// Simple sampling without normal (uses trilinear)
vec3 SampleVoxelGI_Simple(vec3 worldPos, sampler3D voxelGITex) {
    return SampleVoxelGI_Trilinear(worldPos, voxelGITex) * VXGI_INTENSITY;
}

//============================================================================//
// OCCLUSION SAMPLING
//============================================================================//

// Sample voxel occlusion (for contact shadows)
float SampleVoxelOcclusion(
    vec3 worldPos,
    vec3 normal,
    float radius,
    sampler3D voxelAlbedoTex
) {
    vec3 samplePos = worldPos + normal * VOXEL_SIZE * 0.5;
    
    float occlusion = 0.0;
    int samples = 0;
    
    // 8 направлений в полусфере — тоже через {}
    const vec3 offsets[8] = {
        vec3( 1.0,  1.0,  0.0),
        vec3(-1.0,  1.0,  0.0),
        vec3( 0.0,  1.0,  1.0),
        vec3( 0.0,  1.0, -1.0),
        vec3( 1.0,  0.5,  1.0),
        vec3(-1.0,  0.5,  1.0),
        vec3( 1.0,  0.5, -1.0),
        vec3(-1.0,  0.5, -1.0)
    };
    
    for (int i = 0; i < 8; i++) {
        vec3 offset = normalize(offsets[i]) * radius;
        
        // Rotate to normal space
        vec3 tangent = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
        tangent = normalize(cross(tangent, normal));
        vec3 bitangent = cross(normal, tangent);
        
        vec3 sampleDir = tangent * offset.x + normal * offset.y + bitangent * offset.z;
        vec3 samplePoint = samplePos + sampleDir;
        
        vec3 texCoord = WorldToTexCoord(samplePoint);
        if (any(lessThan(texCoord, vec3(0.0))) || any(greaterThan(texCoord, vec3(1.0)))) continue;
        
        float opacity = texture(voxelAlbedoTex, texCoord).a;
        
        occlusion += opacity;
        samples++;
    }
    
    if (samples > 0) {
        return 1.0 - (occlusion / float(samples));
    }
    return 1.0;
}

//============================================================================//
// INTEGRATION HELPER
//============================================================================//

// Main function to call from composite shader
vec3 CalculateVXGI(
    vec3 worldPos,
    vec3 viewPos,
    vec3 normal,
    sampler3D voxelAlbedoTex,
    sampler3D voxelGITex
) {
    // Check if position is within voxel grid range
    float distToCamera = length(viewPos);
    if (distToCamera > float(VOXEL_RADIUS) * 0.9) {
        return vec3(0.0);
    }
    
    // Sample voxel GI
    vec3 gi = SampleVoxelGI(worldPos, normal, voxelAlbedoTex, voxelGITex);
    
    // Fade out at grid edges
    float edgeFade = 1.0 - smoothstep(
        float(VOXEL_RADIUS) * 0.7,
        float(VOXEL_RADIUS) * 0.9,
        distToCamera
    );
    
    gi *= edgeFade;
    
    return gi;
}

//============================================================================//
// DEBUG VISUALIZATION
//============================================================================//

// Visualize voxel grid for debugging
vec3 DebugVoxelGrid(vec3 worldPos, sampler3D voxelGITex) {
    ivec3 voxel = WorldToVoxel(worldPos);
    
    if (!IsVoxelInBounds(voxel)) {
        return vec3(1.0, 0.0, 0.0); // Red = out of bounds
    }
    
    vec3 texCoord = VoxelToTexCoord(voxel);
    vec3 gi = texture(voxelGITex, texCoord).rgb;
    
    // Show grid edges
    if (IsVoxelGridEdge(voxel, 1)) {
        return vec3(0.0, 1.0, 0.0); // Green = edge
    }
    
    return gi;
}

#endif // VOXEL_SAMPLING_GLSL