import package::shared::{VertexInput, VertexOutput, sample_shadow_map, flowmap_path_lighting_with_shadow, all_spotlights_lighting, uniforms, blue_noise_texture, blue_noise_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;

    let instance_position = vec3<f32>(
        instance_model[3][0],
        instance_model[3][1],
        instance_model[3][2]
    );

    let to_player = uniforms.player_position - uniforms.camera_position;
    let distance_to_player = length(to_player);

    var dissolve_amount = 0.0;

    if distance_to_player > 0.01 {
        let ray_dir = to_player / distance_to_player;
        let ray_origin = uniforms.camera_position;

        let tree_height = 16.0;
        let occlusion_radius = 6.5;

        let w = instance_position - ray_origin;
        let projection_t = dot(w, ray_dir);

        if projection_t > 0.0 && projection_t < distance_to_player {
            let closest_on_ray = ray_origin + ray_dir * projection_t;

            let diff = closest_on_ray - instance_position;
            let height_on_trunk = clamp(diff.y, 0.0, tree_height);
            let closest_on_trunk = instance_position + vec3<f32>(0.0, height_on_trunk, 0.0);

            let perp_distance = length(closest_on_trunk - closest_on_ray);

            if perp_distance < occlusion_radius {
                let dissolve_t = pow(perp_distance / occlusion_radius, 0.5);
                dissolve_amount = 1.0 - clamp(dissolve_t, 0.0, 1.0);
            }
        }
    }

    output.dissolve_amount = dissolve_amount;

    return output;
}

@fragment
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
    
    let debug = 0u;

    if debug == 1u {
        return vec4<f32>(input.dissolve_amount);
    }

    if input.dissolve_amount > 0.0 {
        let screen_pos = input.clip_position.xy;
        let noise_uv = fract(screen_pos / 128.0);
        let noise_value = textureSampleLevel(blue_noise_texture, blue_noise_sampler, noise_uv, 0.0).r;

        if noise_value < input.dissolve_amount {
            discard;
        }
    }

    let shadow = sample_shadow_map(input.light_space_position);

    let tile_scale = 4.0;
    let spotlight_strokes = all_spotlights_lighting(input.world_position, input.clip_position, input.world_normal, tile_scale, shadow);
    let brightness = spotlight_strokes;

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