struct LineData {
    start: vec4<f32>,
    end: vec4<f32>,
    color: vec4<f32>,
    entity_id: u32,
    visible: u32,
    _padding: vec2<u32>,
}

struct DrawIndexedIndirectCommand {
    index_count: u32,
    instance_count: u32,
    first_index: u32,
    vertex_offset: i32,
    first_instance: u32,
}

struct CullingData {
    view_proj: mat4x4<f32>,
    frustum_planes: array<vec4<f32>, 6>,
    line_count: u32,
    _padding0: u32,
    _padding1: u32,
    _padding2: u32,
    _padding3: vec4<f32>,
    _padding4: vec4<f32>,
    _padding5: vec4<f32>,
}

@group(0) @binding(0)
var<storage, read> lines: array<LineData>;

@group(0) @binding(1)
var<storage, read_write> draw_commands: array<DrawIndexedIndirectCommand>;

@group(0) @binding(2)
var<storage, read_write> draw_count: atomic<u32>;

@group(0) @binding(3)
var<uniform> culling_data: CullingData;

fn point_in_frustum(point: vec3<f32>) -> bool {
    for (var i = 0u; i < 6u; i++) {
        let plane = culling_data.frustum_planes[i];
        let distance = dot(vec4<f32>(point, 1.0), plane);
        if (distance < 0.0) {
            return false;
        }
    }
    return true;
}

fn line_in_frustum(start: vec3<f32>, end: vec3<f32>) -> bool {
    if (point_in_frustum(start) || point_in_frustum(end)) {
        return true;
    }

    let samples = 8u;
    for (var i = 1u; i < samples; i++) {
        let t = f32(i) / f32(samples);
        let sample_point = mix(start, end, t);
        if (point_in_frustum(sample_point)) {
            return true;
        }
    }

    return false;
}

@compute @workgroup_size(256)
fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
    let line_index = global_id.x;

    if (line_index >= culling_data.line_count) {
        return;
    }

    let line = lines[line_index];

    if (line.visible == 0u) {
        return;
    }

    let start = line.start.xyz;
    let end = line.end.xyz;

    if (!line_in_frustum(start, end)) {
        return;
    }

    let command_index = atomicAdd(&draw_count, 1u);

    var command: DrawIndexedIndirectCommand;
    command.index_count = 2u;
    command.instance_count = 1u;
    command.first_index = 0u;
    command.vertex_offset = 0;
    command.first_instance = line_index;

    draw_commands[command_index] = command;
}
