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

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<crate::ecs::world::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!["swapchain"]
    }

    fn prepare(
        &mut self,
        device: &wgpu::Device,
        queue: &wgpu::Queue,
        _world: &crate::ecs::world::World,
    ) {
        if !self.paint_jobs.is_empty() {
            let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
                label: Some("Egui Prepare Encoder"),
            });

            self.renderer.update_buffers(
                device,
                queue,
                &mut encoder,
                &self.paint_jobs,
                &self.screen_descriptor,
            );

            queue.submit(std::iter::once(encoder.finish()));
        }
    }

    fn execute<'r, 'e>(
        &mut self,
        context: PassExecutionContext<'r, 'e, crate::ecs::world::World>,
    ) -> crate::render::wgpu::rendergraph::Result<
        Vec<crate::render::wgpu::rendergraph::SubGraphRunCommand<'r>>,
    > {
        let (swapchain_view, swapchain_load, swapchain_store) =
            context.get_color_attachment("swapchain")?;

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

        Ok(context.into_sub_graph_commands())
    }
}
