godoru 0.1.0

UI Framework for Rust using Godot
use crate::Color;

use std::mem::{MaybeUninit, transmute_copy};
use std::os::raw::{c_char, c_void};

pub(crate) type GDExtensionInterfaceFunctionPtr = Option<unsafe extern "C" fn()>;
pub(crate) type GDExtensionInterfaceGetProcAddress =
    Option<unsafe extern "C" fn(*const c_char) -> GDExtensionInterfaceFunctionPtr>;
pub(crate) type GDExtensionClassLibraryPtr = *mut c_void;
pub(crate) type GDExtensionMethodBindPtr = *const c_void;
pub(crate) type GDExtensionConstStringNamePtr = *const c_void;
pub(crate) type GDExtensionUninitializedStringNamePtr = *mut c_void;
pub(crate) type GDExtensionConstTypePtr = *const c_void;
pub(crate) type GDExtensionTypePtr = *mut c_void;
pub(crate) type GDExtensionObjectPtr = *mut c_void;
pub(crate) type GDExtensionPtrDestructor = unsafe extern "C" fn(GDExtensionTypePtr);
pub(crate) type GDExtensionInterfaceStringNewWithUtf8Chars =
    unsafe extern "C" fn(GDExtensionTypePtr, *const c_char);
pub(crate) type GDExtensionInterfaceStringToUtf8Chars =
    unsafe extern "C" fn(GDExtensionConstTypePtr, *mut c_char, i64) -> i64;
pub(crate) type GDExtensionInterfaceStringNameNewWithUtf8Chars =
    unsafe extern "C" fn(GDExtensionUninitializedStringNamePtr, *const c_char);
pub(crate) type GDExtensionInterfaceClassdbGetMethodBind =
    unsafe extern "C" fn(
        GDExtensionConstStringNamePtr,
        GDExtensionConstStringNamePtr,
        i64,
    ) -> GDExtensionMethodBindPtr;
pub(crate) type GDExtensionInterfaceObjectMethodBindPtrcall = unsafe extern "C" fn(
    GDExtensionMethodBindPtr,
    GDExtensionObjectPtr,
    *const GDExtensionConstTypePtr,
    GDExtensionTypePtr,
);
pub(crate) type GDExtensionInterfaceClassdbConstructObject =
    unsafe extern "C" fn(GDExtensionConstStringNamePtr) -> GDExtensionObjectPtr;
pub(crate) type GDExtensionInterfaceGlobalGetSingleton =
    unsafe extern "C" fn(GDExtensionConstStringNamePtr) -> GDExtensionObjectPtr;
pub(crate) type GDExtensionInterfaceVariantGetPtrDestructor =
    unsafe extern "C" fn(i32) -> Option<GDExtensionPtrDestructor>;
pub(crate) type GDExtensionInterfaceVariantNewNil = unsafe extern "C" fn(GDExtensionTypePtr);
pub(crate) type GDExtensionInterfaceVariantDestroy = unsafe extern "C" fn(GDExtensionTypePtr);
pub(crate) type GDExtensionVariantFromTypeConstructorFunc =
    unsafe extern "C" fn(GDExtensionTypePtr, GDExtensionTypePtr);
pub(crate) type GDExtensionInterfaceGetVariantFromTypeConstructor =
    unsafe extern "C" fn(i32) -> Option<GDExtensionVariantFromTypeConstructorFunc>;
pub(crate) type GDExtensionInterfaceObjectMethodBindCall = unsafe extern "C" fn(
    GDExtensionMethodBindPtr,
    GDExtensionObjectPtr,
    *const GDExtensionConstTypePtr,
    i64,
    GDExtensionTypePtr,
    *mut GDExtensionCallError,
);

pub(crate) struct GDExtensionCallError {
    pub(crate) error: i32,
    pub(crate) _argument: i32,
    pub(crate) _expected: i32,
}

#[derive(Clone, Copy)]
pub(crate) struct GodotObject(pub(crate) GDExtensionObjectPtr);

pub(crate) struct StringNameStorage {
    storage: MaybeUninit<[usize; 1]>,
    destructor: GDExtensionPtrDestructor,
}

impl StringNameStorage {
    pub(crate) fn new(destructor: GDExtensionPtrDestructor) -> Self {
        Self {
            storage: MaybeUninit::uninit(),
            destructor,
        }
    }

    pub(crate) fn as_ptr(&self) -> GDExtensionConstStringNamePtr {
        self.storage.as_ptr() as GDExtensionConstStringNamePtr
    }

    pub(crate) fn as_mut_ptr(&mut self) -> GDExtensionUninitializedStringNamePtr {
        self.storage.as_mut_ptr() as GDExtensionUninitializedStringNamePtr
    }
}

impl Drop for StringNameStorage {
    fn drop(&mut self) {
        unsafe {
            (self.destructor)(self.storage.as_mut_ptr() as GDExtensionTypePtr);
        }
    }
}

#[repr(C)]
pub(crate) struct GodotString {
    storage: MaybeUninit<[usize; 1]>,
    destructor: GDExtensionPtrDestructor,
}

impl GodotString {
    pub(crate) fn new(destructor: GDExtensionPtrDestructor) -> Self {
        Self {
            storage: MaybeUninit::uninit(),
            destructor,
        }
    }

    pub(crate) fn as_ptr(&self) -> GDExtensionConstTypePtr {
        self.storage.as_ptr() as GDExtensionConstTypePtr
    }

    pub(crate) fn as_mut_ptr(&mut self) -> GDExtensionTypePtr {
        self.storage.as_mut_ptr() as GDExtensionTypePtr
    }
}

impl Drop for GodotString {
    fn drop(&mut self) {
        unsafe {
            (self.destructor)(self.storage.as_mut_ptr() as GDExtensionTypePtr);
        }
    }
}

#[repr(C)]
pub(crate) struct GodotVariant {
    storage: MaybeUninit<[usize; 3]>,
    destructor: GDExtensionInterfaceVariantDestroy,
}

impl GodotVariant {
    pub(crate) fn nil(
        newNil: GDExtensionInterfaceVariantNewNil,
        destructor: GDExtensionInterfaceVariantDestroy,
    ) -> Self {
        let mut value = Self {
            storage: MaybeUninit::uninit(),
            destructor,
        };
        unsafe {
            newNil(value.as_mut_ptr());
        }
        value
    }

    pub(crate) fn fromType(
        newNil: GDExtensionInterfaceVariantNewNil,
        destructor: GDExtensionInterfaceVariantDestroy,
        constructor: GDExtensionVariantFromTypeConstructorFunc,
        value: GDExtensionTypePtr,
    ) -> Self {
        let mut variant = Self::nil(newNil, destructor);
        unsafe {
            constructor(variant.as_mut_ptr(), value);
        }
        variant
    }

    pub(crate) fn as_ptr(&self) -> GDExtensionConstTypePtr {
        self.storage.as_ptr() as GDExtensionConstTypePtr
    }

    pub(crate) fn as_mut_ptr(&mut self) -> GDExtensionTypePtr {
        self.storage.as_mut_ptr() as GDExtensionTypePtr
    }
}

impl Drop for GodotVariant {
    fn drop(&mut self) {
        unsafe {
            (self.destructor)(self.as_mut_ptr());
        }
    }
}

#[repr(C)]
pub(crate) struct GodotVector2 {
    pub(crate) x: f32,
    pub(crate) y: f32,
}

#[repr(C)]
pub(crate) struct GodotColor {
    pub(crate) red: f32,
    pub(crate) green: f32,
    pub(crate) blue: f32,
    pub(crate) alpha: f32,
}

impl From<&Color> for GodotColor {
    fn from(color: &Color) -> Self {
        Self {
            red: color.red,
            green: color.green,
            blue: color.blue,
            alpha: color.alpha,
        }
    }
}

pub(crate) unsafe fn loadProc<T: Copy>(
    getProcAddress: GDExtensionInterfaceGetProcAddress,
    name: &[u8],
) -> Option<T> {
    let getProcAddress = getProcAddress?;
    let function = unsafe { getProcAddress(name.as_ptr() as *const c_char) }?;
    Some(unsafe { transmute_copy(&function) })
}

pub(crate) fn boolArg(value: bool) -> u8 {
    if value { 1 } else { 0 }
}