use euclid::Size2D;
use femtovg::{renderer::OpenGl, Canvas};
use std::num::NonZeroU32;
use std::sync::{Mutex, RwLock};
use std::{cell::RefCell, sync::Arc};
use surfman::{device, Adapter, Connection, Context, Device, Error};

pub struct OffscreenRenderer {
    context: Arc<RwLock<Context>>,
    device: Device,
    // renderer: Arc<Mutex<femtovg::renderer::OpenGl>>,
    canvas: Arc<RwLock<Canvas<OpenGl>>>,
}

impl OffscreenRenderer {
    pub fn new() -> Result<Self, surfman::Error> {
        let connection = Connection::new()?;
        let adapter = connection.create_adapter()?;
        let mut device = connection.create_device(&adapter)?;

        let api = device.gl_api();

        let descriptor = device.create_context_descriptor(&surfman::ContextAttributes {
            version: surfman::GLVersion::new(3, 3),
            flags: surfman::ContextAttributeFlags::empty(),
        })?;

        let mut context = device.create_context(&descriptor, None)?;
        let mut surface = device.create_surface(
            &context,
            surfman::SurfaceAccess::GPUOnly,
            surfman::SurfaceType::Generic {
                size: euclid::Size2D::new(500, 500),
            },
        )?;

        let surface_info = device.surface_info(&surface);
        device
            .bind_surface_to_context(&mut context, surface)
            .expect("Failed to bind surface to context");
        device.make_context_current(&context).unwrap();

        static LOAD_FN: fn(&str) -> *const std::ffi::c_void =
            |s| epoxy::get_proc_addr(s) as *const _;

        let (mut renderer, fbo) = unsafe {
            let renderer = OpenGl::new_from_function(LOAD_FN).expect("Cannot create renderer");
            let fbo =
                glow::NativeFramebuffer(NonZeroU32::new(surface_info.framebuffer_object).unwrap());
            (renderer, fbo)
        };

        renderer.set_screen_target(Some(fbo));

        let mut canvas = Canvas::new(renderer).expect("Cannot create canvas");
        canvas.set_size(500, 500, 1.0);

        Ok(Self {
            context: Arc::new(RwLock::new(context)),
            device,
            canvas: Arc::new(RwLock::new(canvas)),
        })
    }

    pub fn canvas(&self) -> Arc<RwLock<Canvas<OpenGl>>> {
        self.canvas.clone()
    }
}

impl Drop for OffscreenRenderer {
    fn drop(&mut self) {
        let mut context = self.context.write().unwrap();
        self.device.destroy_context(&mut context).unwrap();
        let _ = self;
    }
}

unsafe impl Send for OffscreenRenderer {}
unsafe impl Sync for OffscreenRenderer {}
