use bevy::{
    core_pipeline::{
        core_2d::graph::{Core2d, Node2d},
        fullscreen_vertex_shader::fullscreen_shader_vertex_state,
    },
    ecs::query::QueryItem,
    prelude::*,
    render::{
        extract_component::{
            ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,
        },
        render_graph::{
            NodeRunError, RenderGraphApp, RenderGraphContext, RenderLabel, ViewNode, ViewNodeRunner,
        },
        render_resource::{
            binding_types::{sampler, texture_2d, uniform_buffer},
            BindGroupEntries, BindGroupLayout, BindGroupLayoutEntries, CachedRenderPipelineId,
            ColorTargetState, ColorWrites, FragmentState, MultisampleState, Operations,
            PipelineCache, PrimitiveState, RenderPassColorAttachment, RenderPassDescriptor,
            RenderPipelineDescriptor, Sampler, SamplerBindingType, SamplerDescriptor, ShaderStages,
            ShaderType, TextureFormat, TextureSampleType,
        },
        renderer::{RenderContext, RenderDevice},
        texture::BevyDefault,
        view::ViewTarget,
        RenderApp,
    },
};
pub struct ChromaticAberrationPlugin;
impl Plugin for ChromaticAberrationPlugin {
    fn build(&self, app: &mut App) {
        app.add_plugins((
            ExtractComponentPlugin::<ChromaticAberattionSettings>::default(),
            UniformComponentPlugin::<ChromaticAberattionSettings>::default(),
        ));
                let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
            return;
        };
        render_app
            .add_render_graph_node::<ViewNodeRunner<ChromaticAberrationNode>>(
                Core2d,
                ChromaticAberrationLabel,
            )
            .add_render_graph_edges(
                Core2d,
                (
                    Node2d::Tonemapping,
                    ChromaticAberrationLabel,
                    Node2d::EndMainPassPostProcessing,
                ),
            );
    }
    fn finish(&self, app: &mut App) {
                let Ok(render_app) = app.get_sub_app_mut(RenderApp) else {
            return;
        };
        render_app
                        .init_resource::<ChromaticAberrationPipeline>();
    }
}
#[derive(Component, Clone, Copy, ExtractComponent, ShaderType)]
pub struct ChromaticAberattionSettings {
    pub intensity: f32,
            pub red_offset: Vec2,
    pub green_offset: Vec2,
    pub blue_offset: Vec2,
        #[cfg(feature = "webgl2")]
    pub _webgl_padding: f32,
}
impl Default for ChromaticAberattionSettings {
    fn default() -> Self {
        Self {
            intensity: default(),
            red_offset: Vec2::NEG_Y + Vec2::X,
            green_offset: Vec2::NEG_X,
            blue_offset: Vec2::Y,
            #[cfg(feature = "webgl2")]
            _webgl_padding: default(),
        }
    }
}
#[derive(Debug, Hash, PartialEq, Eq, Clone, RenderLabel)]
struct ChromaticAberrationLabel;
#[derive(Default)]
struct ChromaticAberrationNode;
impl ViewNode for ChromaticAberrationNode {
    type ViewQuery = (&'static ViewTarget, &'static ChromaticAberattionSettings);
    fn run<'w>(
        &self,
        _graph: &mut RenderGraphContext,
        render_context: &mut RenderContext<'w>,
        (view_target, _settings): QueryItem<'w, Self::ViewQuery>,
        world: &'w World,
    ) -> Result<(), NodeRunError> {
        let chromatic_aberration_pipeline = world.resource::<ChromaticAberrationPipeline>();
        let pipeline_cache = world.resource::<PipelineCache>();
        let Some(pipeline) =
            pipeline_cache.get_render_pipeline(chromatic_aberration_pipeline.pipeline_id)
        else {
            return Ok(());
        };
        let settings_uniform = world.resource::<ComponentUniforms<ChromaticAberattionSettings>>();
        let Some(settings_binding) = settings_uniform.uniforms().binding() else {
            return Ok(());
        };
                let post_process = view_target.post_process_write();
                                                                let bind_group = render_context.render_device().create_bind_group(
            "chromatic_aberration_bind_group",
            &chromatic_aberration_pipeline.layout,
                        &BindGroupEntries::sequential((
                                post_process.source,
                                &chromatic_aberration_pipeline.sampler,
                                settings_binding.clone(),
            )),
        );
                let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
            label: Some("post_process_pass"),
            color_attachments: &[Some(RenderPassColorAttachment {
                                                view: post_process.destination,
                resolve_target: None,
                ops: Operations::default(),
            })],
            depth_stencil_attachment: None,
            timestamp_writes: None,
            occlusion_query_set: None,
        });
                        render_pass.set_render_pipeline(pipeline);
        render_pass.set_bind_group(0, &bind_group, &[]);
        render_pass.draw(0..3, 0..1);
        Ok(())
    }
}
#[derive(Resource)]
struct ChromaticAberrationPipeline {
    layout: BindGroupLayout,
    sampler: Sampler,
    pipeline_id: CachedRenderPipelineId,
}
impl FromWorld for ChromaticAberrationPipeline {
    fn from_world(world: &mut World) -> Self {
        let render_device = world.resource::<RenderDevice>();
        let layout = render_device.create_bind_group_layout(
            "chromatic_aberration_bind_group_layout",
            &BindGroupLayoutEntries::sequential(
                ShaderStages::FRAGMENT,
                (
                                        texture_2d(TextureSampleType::Float { filterable: true }),
                                        sampler(SamplerBindingType::Filtering),
                                        uniform_buffer::<ChromaticAberattionSettings>(false),
                ),
            ),
        );
                let sampler = render_device.create_sampler(&SamplerDescriptor::default());
                let shader = world
            .resource::<AssetServer>()
            .load("shaders/chromatic_aberration.wgsl");
        let pipeline_id = world
            .resource_mut::<PipelineCache>()
                        .queue_render_pipeline(RenderPipelineDescriptor {
                label: Some("chromatic_aberration_pipeline".into()),
                layout: vec![layout.clone()],
                                vertex: fullscreen_shader_vertex_state(),
                fragment: Some(FragmentState {
                    shader,
                    shader_defs: vec![],
                                                            entry_point: "fragment".into(),
                    targets: vec![Some(ColorTargetState {
                        format: TextureFormat::bevy_default(),
                        blend: None,
                        write_mask: ColorWrites::ALL,
                    })],
                }),
                                                primitive: PrimitiveState::default(),
                depth_stencil: None,
                multisample: MultisampleState::default(),
                push_constant_ranges: vec![],
            });
        Self {
            layout,
            sampler,
            pipeline_id,
        }
    }
}