use core::{
    cell::Cell,
    ops::{Deref, DerefMut},
};

use alloc::rc::{Rc, Weak};
use slint::{
    PhysicalSize, Window, WindowSize,
    platform::{Renderer, WindowAdapter, WindowEvent, software_renderer::SoftwareRenderer},
};

/// This is a minimal adapter for a Window that doesn't have any other feature than rendering
/// using the software renderer.
pub struct SoftwareWindowAdapter {
    pub window: Window,
    pub renderer: SoftwareRenderer,
    needs_redraw: Cell<bool>,
    size: Cell<PhysicalSize>,
}

impl SoftwareWindowAdapter {
    /// Instantiate a new MinimalWindowAdaptor
    ///
    /// The `repaint_buffer_type` parameter specify what kind of buffer are passed to the [`SoftwareRenderer`]
    pub fn new(renderer: SoftwareRenderer) -> Rc<Self> {
        Rc::new_cyclic(|w: &Weak<Self>| Self {
            window: Window::new(w.clone()),
            renderer,
            needs_redraw: Cell::new(true),
            size: Default::default(),
        })
    }

    /// If the window needs to be redrawn, the callback will be called with the
    /// [renderer](SoftwareRenderer) that should be used to do the drawing.
    ///
    /// [`SoftwareRenderer::render()`] or [`SoftwareRenderer::render_by_line()`] should be called
    /// in that callback.
    ///
    /// Return true if something was redrawn.
    pub fn draw_if_needed(&self, render_callback: impl FnOnce(&SoftwareRenderer)) -> bool {
        if self.needs_redraw.replace(false)
        /*|| self.renderer.rendering_metrics_collector.is_some()*/
        {
            render_callback(&self.renderer);
            true
        } else {
            false
        }
    }

    // pub fn set_scale_factor(&self, scale_factor: f32) {
    //     self.window.dispatch_event(WindowEvent::ScaleFactorChanged { scale_factor });
    // }
}

impl WindowAdapter for SoftwareWindowAdapter {
    fn window(&self) -> &Window {
        &self.window
    }

    fn renderer(&self) -> &dyn Renderer {
        &self.renderer
    }

    fn size(&self) -> PhysicalSize {
        self.size.get()
    }

    fn set_size(&self, size: WindowSize) {
        let sf = self.window.scale_factor();
        self.size.set(size.to_physical(sf));
        self.window.dispatch_event(WindowEvent::Resized {
            size: size.to_logical(sf),
        })
    }

    fn request_redraw(&self) {
        self.needs_redraw.set(true);
    }
}

impl Deref for SoftwareWindowAdapter {
    type Target = Window;

    fn deref(&self) -> &Self::Target {
        &self.window
    }
}

impl DerefMut for SoftwareWindowAdapter {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.window
    }
}
