use crate::errors::{EGLError, LayerShikaError, Result};
use glutin::{
    api::egl::{config::Config, context::PossiblyCurrentContext, display::Display},
    config::ConfigTemplateBuilder,
    context::ContextAttributesBuilder,
    prelude::*,
};
use log::{info, warn};
use raw_window_handle::{RawDisplayHandle, WaylandDisplayHandle};
use std::{ffi::c_void, ptr::NonNull, rc::Rc};
use wayland_client::backend::ObjectId;

pub struct RenderContextManager {
    display: Display,
    config: Config,
    root_context: PossiblyCurrentContext,
}

impl RenderContextManager {
    pub fn new(display_id: &ObjectId) -> Result<Rc<Self>> {
        info!("Initializing RenderContextManager with independent root context");

        let display_handle = create_wayland_display_handle(display_id)?;
        let display = unsafe { Display::new(display_handle) }
            .map_err(|e| EGLError::DisplayCreation { source: e.into() })?;

        let config_template = ConfigTemplateBuilder::default();
        let config = select_config(&display, config_template)?;

        let root_context = Self::create_root_context(&display, &config)?;

        info!("RenderContextManager initialized successfully");

        Ok(Rc::new(Self {
            display,
            config,
            root_context,
        }))
    }

    fn create_root_context(display: &Display, config: &Config) -> Result<PossiblyCurrentContext> {
        if let Ok(context) = Self::try_create_surfaceless_context(display, config) {
            info!("Created surfaceless root EGL context");
            return Ok(context);
        }

        warn!(
            "Surfaceless context not available, using workaround with make_current_surfaceless anyway"
        );
        Self::create_surfaceless_fallback(display, config)
    }

    fn try_create_surfaceless_context(
        display: &Display,
        config: &Config,
    ) -> Result<PossiblyCurrentContext> {
        let context_attributes = ContextAttributesBuilder::default();
        let not_current =
            unsafe { display.create_context(config, &context_attributes.build(None)) }
                .map_err(|e| EGLError::ContextCreation { source: e.into() })?;

        not_current
            .make_current_surfaceless()
            .map_err(|e| EGLError::MakeCurrent { source: e.into() }.into())
    }

    fn create_surfaceless_fallback(
        display: &Display,
        config: &Config,
    ) -> Result<PossiblyCurrentContext> {
        let context_attributes = ContextAttributesBuilder::default();
        let not_current =
            unsafe { display.create_context(config, &context_attributes.build(None)) }
                .map_err(|e| EGLError::ContextCreation { source: e.into() })?;

        not_current
            .make_current_surfaceless()
            .map_err(|e| EGLError::MakeCurrent { source: e.into() }.into())
    }

    pub fn display(&self) -> &Display {
        &self.display
    }

    pub fn config(&self) -> &Config {
        &self.config
    }

    pub fn root_context(&self) -> &PossiblyCurrentContext {
        &self.root_context
    }
}

fn create_wayland_display_handle(display_id: &ObjectId) -> Result<RawDisplayHandle> {
    let display = NonNull::new(display_id.as_ptr().cast::<c_void>()).ok_or_else(|| {
        LayerShikaError::InvalidInput {
            message: "Failed to create NonNull pointer for display".into(),
        }
    })?;
    let handle = WaylandDisplayHandle::new(display);
    Ok(RawDisplayHandle::Wayland(handle))
}

fn select_config(
    glutin_display: &Display,
    config_template: ConfigTemplateBuilder,
) -> Result<Config> {
    let mut configs = unsafe { glutin_display.find_configs(config_template.build()) }
        .map_err(|e| EGLError::ConfigSelection { source: e.into() })?;
    configs
        .next()
        .ok_or_else(|| EGLError::NoCompatibleConfig.into())
}
