import package::shared::{
    VertexInput,
    VertexOutput,
    uniforms,
    sample_shadow_map,
    all_spotlights_lighting,
    is_in_any_spotlight_light_area,
    blue_noise_texture,
    blue_noise_sampler,
    TERRAIN_BOUNDS,
    Spotlight
};

@group(1) @binding(0)
var persistent_light_texture: texture_2d<f32>;

@group(1) @binding(1)
var persistent_light_sampler: sampler;

@vertex
fn vs_main(input: VertexInput) -> VertexOutput {
    var output: VertexOutput;

    let instance_model = mat4x4<f32>(
        input.instance_model_0,
        input.instance_model_1,
        input.instance_model_2,
        input.instance_model_3
    );

    let world_pos = instance_model * vec4<f32>(input.position, 1.0);
    output.world_position = world_pos.xyz;
    output.clip_position = uniforms.projection * uniforms.view * world_pos;

    let normal_matrix = mat3x3<f32>(
        instance_model[0].xyz,
        instance_model[1].xyz,
        instance_model[2].xyz
    );
    output.world_normal = normalize(normal_matrix * input.normal);

    output.light_space_position = uniforms.light_view_projection * world_pos;

    return output;
}

@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
    let shadow = sample_shadow_map(in.light_space_position);

    let tile_scale = 2.0;
    let in_spotlight_light_area = is_in_any_spotlight_light_area(in.world_position, in.world_normal, tile_scale, shadow);
    let spotlight_strokes = all_spotlights_lighting(in.world_position, in.clip_position, in.world_normal, tile_scale, shadow);

    var brightness = spotlight_strokes;

    if !in_spotlight_light_area {
        let terrain_uv = (vec2<f32>(in.world_position.x, in.world_position.z) + TERRAIN_BOUNDS * 0.5) / TERRAIN_BOUNDS;
        let persistent_light = textureSample(persistent_light_texture, persistent_light_sampler, terrain_uv).r;

        if persistent_light > 0.05 {
            let screen_pos = in.clip_position.xy / in.clip_position.w;
            let blue_noise_uv = screen_pos * 0.5 + 0.5;
            let blue_noise = textureSample(blue_noise_texture, blue_noise_sampler, blue_noise_uv * 10.0).r;
            let blue_step = step(blue_noise, persistent_light / 30.0);

            brightness = max(brightness, blue_step);
        }
    }

    return vec4<f32>(brightness, brightness, brightness, 1.0);
}
