#version 430 compatibility
#include "/lib/settings.glsl"
#include "/lib/emission_color.glsl"
#include "/lib/voxel_light_blend.glsl"

layout(local_size_x = 4, local_size_y = 4, local_size_z = 8) in;
const ivec3 workGroups = ivec3(8, 8, 4);

layout(rgba8) readonly uniform image3D voxelLightTempY;
layout(rgba8) writeonly uniform image3D voxelLightFinal;
uniform vec3 cameraPosition;
uniform int heldItemId;
uniform int heldItemId2;
uniform int heldBlockLightValue;
uniform int heldBlockLightValue2;

vec3 heldLightSource() {
    float mainStrength = clamp(
        float(heldBlockLightValue) / 15.0,
        0.0,
        1.0
    );
    float offStrength = clamp(
        float(heldBlockLightValue2) / 15.0,
        0.0,
        1.0
    );
    if (max(mainStrength, offStrength) <= 0.0001) {
        return vec3(0.0);
    }

    vec3 mainLight = psxaEmissionColor(heldItemId);
    vec3 offLight = psxaEmissionColor(heldItemId2);
    mainLight /= max(psxaVoxelLightMagnitude(mainLight), 0.0001);
    offLight /= max(psxaVoxelLightMagnitude(offLight), 0.0001);

    vec3 weightedChroma = vec3(0.0);
    float totalWeight = 0.0;
    float strongestMagnitude = 0.0;
    psxaAccumulateVoxelLight(
        mainLight * mainStrength,
        weightedChroma,
        totalWeight,
        strongestMagnitude
    );
    psxaAccumulateVoxelLight(
        offLight * offStrength,
        weightedChroma,
        totalWeight,
        strongestMagnitude
    );
    return psxaResolveVoxelLight(
        weightedChroma,
        totalWeight,
        strongestMagnitude
    );
}

void main() {
    ivec3 position = ivec3(gl_GlobalInvocationID);
    vec3 weightedChroma = vec3(0.0);
    float totalWeight = 0.0;
    float strongestMagnitude = 0.0;
    int firstOffset = max(-VOXEL_LIGHT_RADIUS, -position.z);
    int lastOffset = min(VOXEL_LIGHT_RADIUS, 31 - position.z);
    for (int offset = firstOffset; offset <= lastOffset; ++offset) {
        ivec3 samplePosition = position + ivec3(0, 0, offset);
        float falloff = 1.0 - float(abs(offset)) / float(VOXEL_LIGHT_RADIUS + 1);
        vec3 candidate = imageLoad(voxelLightTempY, samplePosition).rgb * falloff;
        psxaAccumulateVoxelLight(
            candidate,
            weightedChroma,
            totalWeight,
            strongestMagnitude
        );
    }

    // Placed emitters remain on the stable integer grid. Held emitters move
    // continuously with the camera and cover the whole near cascade so their
    // chroma overlaps the gbuffer's 12-15 block near/far transition.
    vec3 heldSourcePosition = vec3(16.0) + fract(cameraPosition);
    vec3 heldAxisDistance = abs(vec3(position) - heldSourcePosition);
    const float heldReach = 16.0;
    if (all(lessThan(heldAxisDistance, vec3(heldReach)))) {
        vec3 heldAxisFalloff =
            vec3(1.0) - heldAxisDistance / heldReach;
        float heldFalloff =
            heldAxisFalloff.x *
            heldAxisFalloff.y *
            heldAxisFalloff.z;
        psxaAccumulateVoxelLight(
            heldLightSource() * heldFalloff,
            weightedChroma,
            totalWeight,
            strongestMagnitude
        );
    }

    vec3 blended = psxaResolveVoxelLight(
        weightedChroma,
        totalWeight,
        strongestMagnitude
    );
    imageStore(voxelLightFinal, position, vec4(blended, 1.0));
}
