use crate::ecs::world::World;
use crate::render::wgpu::rendergraph::{PassExecutionContext, PassNode};

/// Render-graph node that draws the tessellated egui frame on top of the
/// swapchain. Built into the graph after the retained [`UiPass`](super::UiPass)
/// so immediate-mode panels composite over everything else. The renderer holds
/// no depth attachment because egui is a flat overlay.
pub struct EguiPass {
    renderer: egui_wgpu::Renderer,
    screen_descriptor: egui_wgpu::ScreenDescriptor,
    paint_jobs: Vec<egui::ClippedPrimitive>,
}

impl EguiPass {
    pub fn new(device: &wgpu::Device, surface_format: wgpu::TextureFormat) -> Self {
        let renderer = egui_wgpu::Renderer::new(
            device,
            surface_format,
            egui_wgpu::RendererOptions {
                depth_stencil_format: None,
                msaa_samples: 1,
                ..Default::default()
            },
        );

        Self {
            renderer,
            screen_descriptor: egui_wgpu::ScreenDescriptor {
                size_in_pixels: [1, 1],
                pixels_per_point: 1.0,
            },
            paint_jobs: Vec::new(),
        }
    }

    pub fn update_textures(
        &mut self,
        device: &wgpu::Device,
        queue: &wgpu::Queue,
        textures_delta: &egui::TexturesDelta,
    ) {
        for (id, image_delta) in &textures_delta.set {
            self.renderer
                .update_texture(device, queue, *id, image_delta);
        }
        for id in &textures_delta.free {
            self.renderer.free_texture(id);
        }
    }

    pub fn set_paint_jobs(&mut self, paint_jobs: Vec<egui::ClippedPrimitive>) {
        self.paint_jobs = paint_jobs;
    }

    pub fn set_screen_descriptor(&mut self, screen_descriptor: egui_wgpu::ScreenDescriptor) {
        self.screen_descriptor = screen_descriptor;
    }
}

impl PassNode<World> for EguiPass {
    fn name(&self) -> &str {
        "egui_pass"
    }

    fn reads(&self) -> Vec<&str> {
        vec![]
    }

    fn writes(&self) -> Vec<&str> {
        vec![]
    }

    fn reads_writes(&self) -> Vec<&str> {
        vec!["color"]
    }

    fn execute<'r, 'e>(
        &mut self,
        context: PassExecutionContext<'r, 'e, World>,
    ) -> Result<
        Vec<crate::render::wgpu::rendergraph::SubGraphRunCommand<'r>>,
        crate::render::wgpu::rendergraph::RenderGraphError,
    > {
        if !context.is_pass_enabled() {
            return Ok(context.into_sub_graph_commands());
        }

        let (color_view, color_load, color_store) = context.get_color_attachment("color")?;
        let (target_width, target_height) = context.get_texture_size("color")?;
        self.screen_descriptor.size_in_pixels = [target_width, target_height];

        if !self.paint_jobs.is_empty() {
            self.renderer.update_buffers(
                context.device,
                context.queue,
                context.encoder,
                &self.paint_jobs,
                &self.screen_descriptor,
            );

            self.renderer.render(
                &mut context
                    .encoder
                    .begin_render_pass(&wgpu::RenderPassDescriptor {
                        label: Some("Egui Pass"),
                        color_attachments: &[Some(wgpu::RenderPassColorAttachment {
                            view: color_view,
                            resolve_target: None,
                            ops: wgpu::Operations {
                                load: color_load,
                                store: color_store,
                            },
                            depth_slice: None,
                        })],
                        depth_stencil_attachment: None,
                        timestamp_writes: None,
                        occlusion_query_set: None,
                        multiview_mask: None,
                    })
                    .forget_lifetime(),
                &self.paint_jobs,
                &self.screen_descriptor,
            );
        }

        Ok(context.into_sub_graph_commands())
    }
}
