#ifndef VOXEL_DIRECT_LIGHTING_GLSL
#define VOXEL_DIRECT_LIGHTING_GLSL

// DirectLighting.glsl - Direct Lighting Injection for VXGI
// Injects sunlight into voxel grid using shadowmap
// Based on Document 12 reference implementation

#include "VoxelStructure.glsl"

//============================================================================//
// DIRECT LIGHTING SETTINGS
//============================================================================//

#ifndef VXGI_DIRECTIONAL_WEIGHT
#define VXGI_DIRECTIONAL_WEIGHT 1.0 // [0.0 0.5 1.0 1.5 2.0]
#endif

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

#ifndef VXGI_SHADOW_BIAS
#define VXGI_SHADOW_BIAS -0.00001
#endif

//============================================================================//
// SHADOW DISTORTION (from reference)
//============================================================================//

// Shadow distortion function - must match your shader's distortion
vec3 DistortShadowSpace(vec3 shadowPos) {
    // Implement your shader's shadow distortion here
    // This is a placeholder - use your actual distortion from ShadowDistortion.glsl
    float dist = length(shadowPos.xy);
    float distortFactor = 1.0 - 0.85 * dist;
    shadowPos.xy /= max(distortFactor, 0.1);
    return shadowPos;
}

//============================================================================//
// SUN DIRECTION AND COLOR
//============================================================================//

// Get sun direction in world space
vec3 GetSunDirection() {
    // Convert shadow light position to world space direction
    vec3 sunDir = normalize((gbufferModelViewInverse * vec4(shadowLightPosition, 1.0)).xyz);
    return sunDir;
}

// Calculate sun color based on time of day
vec3 CalculateSunColor(float skyLightLevel) {
    // Sample lightmap for sun color
    vec2 lmCoord = vec2(1.0 / 32.0, max(0.0, skyLightLevel / 240.0 - 1.0 / 32.0));
    vec3 sunColor = texture(lightmap, lmCoord).rgb;
    
    // Apply sky light level
    sunColor *= skyLightLevel;
    
    return sunColor;
}

//============================================================================//
// SHADOWMAP SAMPLING
//============================================================================//

// Sample shadowmap at voxel position
float SampleShadowmap(vec3 worldPos, out vec3 shadowProjectedPos) {
    // Convert to shadow space
    vec3 relativePos = worldPos - fract(cameraPosition);
    vec4 shadowViewPos = shadowModelView * vec4(relativePos, 1.0);
    vec4 shadowClipPos = shadowProjection * shadowViewPos;
    
    // Apply distortion
    shadowClipPos.xyz = DistortShadowSpace(shadowClipPos.xyz);
    
    // Convert to texture coordinates [0,1]
    shadowProjectedPos = shadowClipPos.xyz * 0.5 + 0.5;
    
    // Check if in shadow map bounds
    if (any(lessThan(shadowProjectedPos, vec3(0.0))) || 
        any(greaterThan(shadowProjectedPos, vec3(1.0)))) {
        return 0.0;
    }
    
    // Sample shadow map
    float shadowDepth = texture(shadowtex1, shadowProjectedPos.xy).r;
    
    // Shadow comparison
    float bias = VXGI_SHADOW_BIAS;
    float shadow = (shadowProjectedPos.z - shadowDepth < bias) ? 1.0 : 0.0;
    
    return shadow;
}

//============================================================================//
// COLORED SHADOWS
//============================================================================//

#if VXGI_COLORED_SHADOWS == 1

vec3 SampleColoredShadow(vec3 shadowProjectedPos) {
    // Sample both shadow textures
    float shadowDepth0 = texture(shadowtex0, shadowProjectedPos.xy).r;
    float shadowDepth1 = texture(shadowtex1, shadowProjectedPos.xy).r;
    
    // Check if there's a translucent object
    if (abs(shadowDepth0 - shadowDepth1) > 0.000001) {
        // Sample colored shadow
        vec4 shadowColor = texture(shadowcolor0, shadowProjectedPos.xy);
        
        // Mix based on alpha
        return mix(vec3(1.0), shadowColor.rgb, min(1.0, shadowColor.a * 2.0));
    }
    
    return vec3(1.0);
}

#endif

//============================================================================//
// DIRECTIONAL WEIGHTING
//============================================================================//

// Calculate weight based on surface normal and sun direction
float CalculateDirectionalWeight(vec3 normal, vec3 sunDir) {
    return max(0.0, dot(normal, sunDir));
}

// Calculate neighbor weights for 6 cardinal directions
void CalculateNeighborWeights(vec3 sunDir, out float weights[6]) {
    for (int i = 0; i < 6; i++) {
        vec3 neighborDir = -vec3(NEIGHBOR_OFFSETS[i]);
        weights[i] = max(0.0, dot(sunDir, neighborDir));
    }
}

//============================================================================//
// NEIGHBOR DATA GATHERING
//============================================================================//



//============================================================================//
// DIRECT LIGHTING INJECTION
//============================================================================//

// Inject direct sunlight into voxel
vec3 InjectDirectLighting(
    ivec3 voxel,
    vec3 worldPos,
    vec4 voxelAlbedo,
    vec3 sunDir,
    vec3 sunColor,
    sampler3D voxelAlbedoTex
) {
    // Check if voxel has geometry
    if (voxelAlbedo.a < 0.01) return vec3(0.0);
    
    // Sample shadowmap
    vec3 shadowPos;
    float shadow = SampleShadowmap(worldPos, shadowPos);
    
    if (shadow < 0.001) return vec3(0.0);
    
    // Apply colored shadows
    vec3 shadowColor = vec3(1.0);
    #if VXGI_COLORED_SHADOWS == 1
        shadowColor = SampleColoredShadow(shadowPos);
    #endif
    
    // Get neighbor data with directional weighting
    vec4 neighborData = GatherNeighborData(voxel, sunDir, voxelAlbedoTex);
    
    // Calculate lighting
    vec3 directLight = sunColor * shadowColor * shadow;
    
    // Apply neighbor albedo (first bounce approximation)
    vec3 bouncedLight = neighborData.rgb * directLight;
    
    return bouncedLight;
}

//============================================================================//
// MAIN INJECTION FUNCTION
//============================================================================//

// Process direct lighting for entire voxel grid
void ProcessDirectLighting(
    ivec3 voxel,
    sampler3D voxelAlbedoTex,
    writeonly image3D voxelGIImage,
    float skyLightLevel
) {
    // Check bounds
    if (!IsVoxelInBounds(voxel)) return;
    
    // Get world position
    vec3 worldPos = VoxelToWorld(voxel);
    
    // Get voxel data
    vec3 texCoord = VoxelToTexCoord(voxel);
    vec4 voxelAlbedo = texture(voxelAlbedoTex, texCoord);
    
    // Skip empty voxels
    if (voxelAlbedo.a < 0.01) return;
    
    // Get sun parameters
    vec3 sunDir = GetSunDirection();
    vec3 sunColor = CalculateSunColor(skyLightLevel);
    
    // Inject direct lighting
    vec3 directGI = InjectDirectLighting(
        voxel,
        worldPos,
        voxelAlbedo,
        sunDir,
        sunColor,
        voxelAlbedoTex
    );
    
    // Store in GI buffer
    imageStore(voxelGIImage, voxel, vec4(directGI, 1.0));
}

//============================================================================//
// BATCH PROCESSING
//============================================================================//

// Process multiple voxels (for compute shader dispatch)
void ProcessDirectLightingBatch(
    ivec3 baseVoxel,
    ivec3 batchSize,
    sampler3D voxelAlbedoTex,
    writeonly image3D voxelGIImage,
    float skyLightLevel
) {
    for (int x = 0; x < batchSize.x; x++) {
        for (int y = 0; y < batchSize.y; y++) {
            for (int z = 0; z < batchSize.z; z++) {
                ivec3 voxel = baseVoxel + ivec3(x, y, z);
                ProcessDirectLighting(voxel, voxelAlbedoTex, voxelGIImage, skyLightLevel);
            }
        }
    }
}

#endif // VOXEL_DIRECT_LIGHTING_GLSL
