//! Fullscreen post-process effects driven by volume assets.

use bevy::prelude::*;
use bevy::render::extract_component::ExtractComponent;
use bevy::render::render_resource::ShaderType;
use bevy::shader::ShaderRef;
use bevy_core_pipeline::fullscreen_material::{FullscreenMaterial, FullscreenMaterialPlugin};
use bevy_core_pipeline::tonemapping::tonemapping;
use bevy_core_pipeline::Core3dSystems;
use shared::{PostProcessEffectAsset, PostProcessEffectKind};

/// Vignette fullscreen pass.
#[derive(Component, Clone, Copy, Default, ExtractComponent, ShaderType)]
pub struct VignetteFx {
    pub intensity: f32,
    pub radius: f32,
}

impl FullscreenMaterial for VignetteFx {
    fn fragment_shader() -> ShaderRef {
        "post_fx/vignette.wgsl".into()
    }

    fn schedule_configs(
        system: bevy::ecs::schedule::ScheduleConfigs<bevy::ecs::system::BoxedSystem>,
    ) -> bevy::ecs::schedule::ScheduleConfigs<bevy::ecs::system::BoxedSystem> {
        system.in_set(Core3dSystems::PostProcess).after(tonemapping)
    }
}

/// Chromatic aberration fullscreen pass.
#[derive(Component, Clone, Copy, Default, ExtractComponent, ShaderType)]
pub struct ChromaticAberrationFx {
    pub intensity: f32,
    pub radius: f32,
}

impl FullscreenMaterial for ChromaticAberrationFx {
    fn fragment_shader() -> ShaderRef {
        "post_fx/chromatic_aberration.wgsl".into()
    }

    fn schedule_configs(
        system: bevy::ecs::schedule::ScheduleConfigs<bevy::ecs::system::BoxedSystem>,
    ) -> bevy::ecs::schedule::ScheduleConfigs<bevy::ecs::system::BoxedSystem> {
        system.in_set(Core3dSystems::PostProcess).after(tonemapping)
    }
}

pub struct FullscreenEffectsPlugin;

impl Plugin for FullscreenEffectsPlugin {
    fn build(&self, app: &mut App) {
        app.add_plugins((
            FullscreenMaterialPlugin::<VignetteFx>::default(),
            FullscreenMaterialPlugin::<ChromaticAberrationFx>::default(),
        ));
    }
}

fn load_effect(path: &str) -> Option<PostProcessEffectAsset> {
    let catalog = if path.starts_with("assets/") {
        path.to_string()
    } else {
        format!("assets/{path}")
    };
    PostProcessEffectAsset::load_from_path(&catalog).ok()
}

pub fn sync_fullscreen_effect(commands: &mut Commands, entity: Entity, effect_path: Option<&str>) {
    commands
        .entity(entity)
        .remove::<VignetteFx>()
        .remove::<ChromaticAberrationFx>();

    let Some(path) = effect_path else {
        return;
    };
    let Some(asset) = load_effect(path) else {
        warn!("Could not load post FX asset at {path}");
        return;
    };
    match asset.kind {
        PostProcessEffectKind::Vignette => {
            commands.entity(entity).insert(VignetteFx {
                intensity: asset.intensity,
                radius: asset.radius,
            });
        }
        PostProcessEffectKind::ChromaticAberration => {
            commands.entity(entity).insert(ChromaticAberrationFx {
                intensity: asset.intensity,
                radius: asset.radius,
            });
        }
    }
}

pub fn strip_fullscreen_effects(commands: &mut Commands, entity: Entity) {
    commands
        .entity(entity)
        .remove::<VignetteFx>()
        .remove::<ChromaticAberrationFx>();
}
