use core::{cell::RefCell, time::Duration};

use alloc::rc::Rc;
use esp_hal::{time::Instant};
use log::info;
use slint::{PhysicalSize, WindowSize, platform::{Key, software_renderer::{RenderingRotation, RepaintBufferType, Rgb565Pixel, SoftwareRenderer}}};

use super::window_adapter::SoftwareWindowAdapter;

pub struct FramebufferPtr(pub *mut [Rgb565Pixel]);

unsafe impl Send for FramebufferPtr {}

pub struct SlintBackend {
    pub window_size: [u32; 2],
    pub window: RefCell<Option<Rc<SoftwareWindowAdapter>>>,
    pub framebuffer: FramebufferPtr,
    // pub peripherals: RefCell<Option<Peripherals>>,
}

impl slint::platform::Platform for SlintBackend {
    fn create_window_adapter(&self) -> Result<Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> {
        // TODO: Custom window adapter impl needs to be implemented, so we can change `rotation` on
        // `SoftwareRenderer`.
        let renderer = SoftwareRenderer::new_with_repaint_buffer_type(RepaintBufferType::ReusedBuffer /* TODO: Implement a swapchain */);
        renderer.set_rendering_rotation(RenderingRotation::Rotate270);
        let window = SoftwareWindowAdapter::new(renderer);
        // window.set_scale_factor(4.0);
        window.set_size(WindowSize::Physical(PhysicalSize::new(self.window_size[0], self.window_size[1])));
        self.window.replace(Some(window.clone()));
        Ok(window)
    }

    fn duration_since_start(&self) -> Duration {
        Duration::from_millis(Instant::now().duration_since_epoch().as_millis())
    }

    fn run_event_loop(&self) -> Result<(), slint::PlatformError> {
        // Instead of `loop`ing here, we execute a single iteration and handle `loop`ing
        // in `crate::run_renderer_task`, where we can make use of `await`.

        /* loop */ {
            if let Some(window) = self.window.borrow().clone() {
                // TODO: Event dispatch. Here or in `run_renderer_task`?
                // window.try_dispatch_event(todo!())?;

                window.draw_if_needed(|renderer| {
                    // TODO: Proper synchronization.
                    let framebuffer = unsafe { &mut *self.framebuffer.0 };
                    renderer.render(framebuffer, self.window_size[1] as usize);
                    info!("UI rendered.");
                });
            }
        }

        Ok(())
    }
}
