use core::num::NonZeroU32;

use asdf_overlay_common::request::UpdateSharedHandle;
use windows::Win32::Graphics::Direct3D11::ID3D11Device;

use crate::{interop::DxInterop, surface::OverlaySurface};

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Renderer {
    Dx12,
    Dx11,
    Dx9,
    Opengl,
    Vulkan,
}

pub struct RenderData {
    pub interop: DxInterop,

    pub position: (i32, i32),
    pub window_size: (u32, u32),
    pub surface: SurfaceState,
    pub renderer: Option<Renderer>,
}

impl RenderData {
    pub(crate) fn new(interop: DxInterop, window_size: (u32, u32)) -> Self {
        Self {
            interop,
            surface: SurfaceState::new(),
            position: (0, 0),
            window_size,
            renderer: None,
        }
    }

    pub fn reset(&mut self) {
        self.surface = SurfaceState::new();
        self.position = (0, 0);
    }

    pub fn update_surface(&mut self, handle: Option<NonZeroU32>) -> anyhow::Result<()> {
        self.surface.update(&self.interop.device, handle)?;
        Ok(())
    }

    pub fn invalidate_surface(&mut self) {
        self.surface.updated = true;
    }
}

pub struct SurfaceState {
    inner: Option<OverlaySurface>,
    updated: bool,
}

impl SurfaceState {
    const fn new() -> Self {
        Self {
            inner: None,
            updated: true,
        }
    }

    #[inline]
    pub const fn get(&self) -> Option<&OverlaySurface> {
        self.inner.as_ref()
    }

    fn update(&mut self, device: &ID3D11Device, handle: Option<NonZeroU32>) -> anyhow::Result<()> {
        self.updated = true;
        self.inner.take();

        let Some(handle) = handle else {
            return Ok(());
        };

        self.inner = Some(OverlaySurface::open_shared(device, handle.get())?);
        Ok(())
    }

    #[inline]
    pub fn take_update(&mut self) -> Option<UpdateSharedHandle> {
        if self.updated {
            self.updated = false;
            Some(UpdateSharedHandle {
                handle: self.get().map(|surface| surface.shared_handle()),
            })
        } else {
            None
        }
    }

    #[inline]
    pub fn invalidate_update(&mut self) -> bool {
        if self.updated {
            self.updated = false;
            true
        } else {
            false
        }
    }
}
