use anyhow::Result;
use vulkan_rs::prelude::*;

use std::{
    sync::{Arc, Mutex},
    time::Duration,
};

use crate::write_log;

pub struct Rendering {
    swapchain: Arc<Swapchain>,
    images: Vec<Arc<Image>>,

    queue: Arc<Mutex<Queue>>,

    render_callbacks: Vec<Box<dyn Fn(u32) -> Result<Arc<CommandBuffer>>>>,
}

impl Rendering {
    pub fn new(queue: Arc<Mutex<Queue>>, swapchain: Arc<Swapchain>) -> Result<Self> {
        crate::write_log!("-> Rendering ctor: begin");

        let vk_images = swapchain.vk_images()?;

        write_log!(format!(
            "-> Rendering ctor: vk images ({})",
            vk_images.len()
        ));

        let images = match swapchain.wrap_images(&vk_images, &queue, true) {
            Ok(images) => images,
            Err(err) => {
                write_log!(format!("-> Rendering ctor: failed wrapper: {:?}", err));
                return Err(err);
            }
        };

        write_log!("-> Rendering ctor: wrapped images");

        write_log!(format!(
            "-> Rendering swapchain extents ({}, {})",
            swapchain.width(),
            swapchain.height(),
        ));

        Ok(Self {
            swapchain,
            images,

            queue,

            render_callbacks: Vec::new(),
        })
    }

    pub fn swapchain(&self) -> &Arc<Swapchain> {
        &self.swapchain
    }

    pub fn add_render_callback<F>(&mut self, f: F)
    where
        F: Fn(u32) -> Result<Arc<CommandBuffer>> + 'static,
    {
        self.render_callbacks.push(Box::new(f));
    }

    pub fn render(&self) -> Result<()> {
        let image_index = self.swapchain.current_index();

        let command_buffers: Vec<Arc<CommandBuffer>> = self
            .render_callbacks
            .iter()
            .map(|c| c(image_index))
            .collect::<Result<Vec<Arc<CommandBuffer>>>>()?;

        write_log!(format!(
            "submitting {} commandbuffer(s)",
            command_buffers.len()
        ));

        let queue = self.queue.lock().unwrap();
        queue.minimal_submit(Duration::from_secs(10), &command_buffers)?;

        Ok(())
    }

    pub fn images(&self) -> &Vec<Arc<Image>> {
        &self.images
    }
}