//! Cubism Core FFI 绑定
//!
//! 直接对应 `Live2DCubismCore.h` 的 C API。
//! 动态加载 libLive2DCubismCore.so（需 libm.so 预加载，因 Core 内部使用 powf）。

use libloading::Library;
use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_void};

/// moc 对齐要求（字节）
pub const CSM_ALIGNOF_MOC: usize = 64;
/// model 对齐要求（字节）
pub const CSM_ALIGNOF_MODEL: usize = 16;

/// 可绘制常量标志位
pub mod constant_flags {
    pub const BLEND_ADDITIVE: u8 = 1 << 0;
    pub const BLEND_MULTIPLICATIVE: u8 = 1 << 1;
    pub const IS_DOUBLE_SIDED: u8 = 1 << 2;
    pub const IS_INVERTED_MASK: u8 = 1 << 3;
}

/// 可绘制动态标志位
pub mod dynamic_flags {
    pub const IS_VISIBLE: u8 = 1 << 0;
    pub const VISIBILITY_DID_CHANGE: u8 = 1 << 1;
    pub const OPACITY_DID_CHANGE: u8 = 1 << 2;
    pub const DRAW_ORDER_DID_CHANGE: u8 = 1 << 3;
    pub const RENDER_ORDER_DID_CHANGE: u8 = 1 << 4;
    pub const VERTEX_POSITIONS_DID_CHANGE: u8 = 1 << 5;
    pub const BLEND_COLOR_DID_CHANGE: u8 = 1 << 6;
}

/// 混合模式
pub mod blend_mode {
    use std::os::raw::c_int;
    pub const NORMAL: c_int = 0;
    pub const ADDITIVE: c_int = 1;
    pub const MULTIPLICATIVE: c_int = 2;
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct CsmVector2 {
    pub x: f32,
    pub y: f32,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct CsmVector4 {
    pub x: f32,
    pub y: f32,
    pub z: f32,
    pub w: f32,
}

/// Cubism Core 动态加载封装
pub struct CubismCore {
    _libm: Library,
    _lib: Library,
    pub csm_get_version: unsafe extern "C" fn() -> c_uint,
    pub csm_get_latest_moc_version: unsafe extern "C" fn() -> c_uint,
    pub csm_get_moc_version:
        unsafe extern "C" fn(address: *const c_void, size: c_uint) -> c_uint,
    pub csm_has_moc_consistency:
        unsafe extern "C" fn(address: *mut c_void, size: c_uint) -> c_int,
    pub csm_revive_moc_in_place:
        unsafe extern "C" fn(address: *mut c_void, size: c_uint) -> *mut c_void,
    pub csm_get_sizeof_model: unsafe extern "C" fn(moc: *const c_void) -> c_uint,
    pub csm_initialize_model_in_place: unsafe extern "C" fn(
        moc: *const c_void,
        address: *mut c_void,
        size: c_uint,
    ) -> *mut c_void,
    pub csm_update_model: unsafe extern "C" fn(model: *mut c_void),
    pub csm_get_render_orders: unsafe extern "C" fn(model: *const c_void) -> *const c_int,
    pub csm_read_canvas_info: unsafe extern "C" fn(
        model: *const c_void,
        out_size: *mut CsmVector2,
        out_origin: *mut CsmVector2,
        out_ppu: *mut f32,
    ),
    pub csm_get_parameter_count: unsafe extern "C" fn(model: *const c_void) -> c_int,
    pub csm_get_parameter_ids:
        unsafe extern "C" fn(model: *const c_void) -> *const *const c_char,
    pub csm_get_parameter_minimum_values:
        unsafe extern "C" fn(model: *const c_void) -> *const f32,
    pub csm_get_parameter_maximum_values:
        unsafe extern "C" fn(model: *const c_void) -> *const f32,
    pub csm_get_parameter_default_values:
        unsafe extern "C" fn(model: *const c_void) -> *const f32,
    pub csm_get_parameter_values: unsafe extern "C" fn(model: *mut c_void) -> *mut f32,
    pub csm_get_part_count: unsafe extern "C" fn(model: *const c_void) -> c_int,
    pub csm_get_part_ids:
        unsafe extern "C" fn(model: *const c_void) -> *const *const c_char,
    pub csm_get_part_opacities: unsafe extern "C" fn(model: *mut c_void) -> *mut f32,
    pub csm_get_drawable_count: unsafe extern "C" fn(model: *const c_void) -> c_int,
    pub csm_get_drawable_ids:
        unsafe extern "C" fn(model: *const c_void) -> *const *const c_char,
    pub csm_get_drawable_constant_flags:
        unsafe extern "C" fn(model: *const c_void) -> *const c_uchar,
    pub csm_get_drawable_dynamic_flags:
        unsafe extern "C" fn(model: *const c_void) -> *const c_uchar,
    pub csm_get_drawable_blend_modes:
        unsafe extern "C" fn(model: *const c_void) -> *const c_int,
    pub csm_get_drawable_texture_indices:
        unsafe extern "C" fn(model: *const c_void) -> *const c_int,
    pub csm_get_drawable_draw_orders:
        unsafe extern "C" fn(model: *const c_void) -> *const c_int,
    pub csm_get_drawable_opacities:
        unsafe extern "C" fn(model: *const c_void) -> *const f32,
    pub csm_get_drawable_mask_counts:
        unsafe extern "C" fn(model: *const c_void) -> *const c_int,
    pub csm_get_drawable_masks:
        unsafe extern "C" fn(model: *const c_void) -> *const *const c_int,
    pub csm_get_drawable_vertex_counts:
        unsafe extern "C" fn(model: *const c_void) -> *const c_int,
    pub csm_get_drawable_vertex_positions:
        unsafe extern "C" fn(model: *const c_void) -> *const *const CsmVector2,
    pub csm_get_drawable_vertex_uvs:
        unsafe extern "C" fn(model: *const c_void) -> *const *const CsmVector2,
    pub csm_get_drawable_index_counts:
        unsafe extern "C" fn(model: *const c_void) -> *const c_int,
    pub csm_get_drawable_indices:
        unsafe extern "C" fn(model: *const c_void) -> *const *const u16,
    pub csm_get_drawable_multiply_colors:
        unsafe extern "C" fn(model: *const c_void) -> *const CsmVector4,
    pub csm_get_drawable_screen_colors:
        unsafe extern "C" fn(model: *const c_void) -> *const CsmVector4,
    pub csm_reset_drawable_dynamic_flags: unsafe extern "C" fn(model: *mut c_void),
}

impl CubismCore {
    /// 加载 Cubism Core 动态库
    ///
    /// `so_path` 为 libLive2DCubismCore.so 的路径。
    /// 内部会先 dlopen libm.so（Core 依赖 powf 但 NEEDED 未声明）。
    pub fn load(so_path: &str) -> anyhow::Result<Self> {
        // 预加载 libm，RTLD_GLOBAL 让后续 .so 能解析到 powf
        let libm = unsafe {
            Library::new("libm.so.6").or_else(|_| Library::new("libm.so.6.1"))?
        };

        let lib = unsafe { Library::new(so_path)? };

        unsafe {
            Ok(Self {
                csm_get_version: *lib.get(b"csmGetVersion\0")?,
                csm_get_latest_moc_version: *lib.get(b"csmGetLatestMocVersion\0")?,
                csm_get_moc_version: *lib.get(b"csmGetMocVersion\0")?,
                csm_has_moc_consistency: *lib.get(b"csmHasMocConsistency\0")?,
                csm_revive_moc_in_place: *lib.get(b"csmReviveMocInPlace\0")?,
                csm_get_sizeof_model: *lib.get(b"csmGetSizeofModel\0")?,
                csm_initialize_model_in_place: *lib.get(b"csmInitializeModelInPlace\0")?,
                csm_update_model: *lib.get(b"csmUpdateModel\0")?,
                csm_get_render_orders: *lib.get(b"csmGetRenderOrders\0")?,
                csm_read_canvas_info: *lib.get(b"csmReadCanvasInfo\0")?,
                csm_get_parameter_count: *lib.get(b"csmGetParameterCount\0")?,
                csm_get_parameter_ids: *lib.get(b"csmGetParameterIds\0")?,
                csm_get_parameter_minimum_values: *lib
                    .get(b"csmGetParameterMinimumValues\0")?,
                csm_get_parameter_maximum_values: *lib
                    .get(b"csmGetParameterMaximumValues\0")?,
                csm_get_parameter_default_values: *lib
                    .get(b"csmGetParameterDefaultValues\0")?,
                csm_get_parameter_values: *lib.get(b"csmGetParameterValues\0")?,
                csm_get_part_count: *lib.get(b"csmGetPartCount\0")?,
                csm_get_part_ids: *lib.get(b"csmGetPartIds\0")?,
                csm_get_part_opacities: *lib.get(b"csmGetPartOpacities\0")?,
                csm_get_drawable_count: *lib.get(b"csmGetDrawableCount\0")?,
                csm_get_drawable_ids: *lib.get(b"csmGetDrawableIds\0")?,
                csm_get_drawable_constant_flags: *lib
                    .get(b"csmGetDrawableConstantFlags\0")?,
                csm_get_drawable_dynamic_flags: *lib
                    .get(b"csmGetDrawableDynamicFlags\0")?,
                csm_get_drawable_blend_modes: *lib.get(b"csmGetDrawableBlendModes\0")?,
                csm_get_drawable_texture_indices: *lib
                    .get(b"csmGetDrawableTextureIndices\0")?,
                csm_get_drawable_draw_orders: *lib.get(b"csmGetDrawableDrawOrders\0")?,
                csm_get_drawable_opacities: *lib.get(b"csmGetDrawableOpacities\0")?,
                csm_get_drawable_mask_counts: *lib.get(b"csmGetDrawableMaskCounts\0")?,
                csm_get_drawable_masks: *lib.get(b"csmGetDrawableMasks\0")?,
                csm_get_drawable_vertex_counts: *lib
                    .get(b"csmGetDrawableVertexCounts\0")?,
                csm_get_drawable_vertex_positions: *lib
                    .get(b"csmGetDrawableVertexPositions\0")?,
                csm_get_drawable_vertex_uvs: *lib.get(b"csmGetDrawableVertexUvs\0")?,
                csm_get_drawable_index_counts: *lib.get(b"csmGetDrawableIndexCounts\0")?,
                csm_get_drawable_indices: *lib.get(b"csmGetDrawableIndices\0")?,
                csm_get_drawable_multiply_colors: *lib
                    .get(b"csmGetDrawableMultiplyColors\0")?,
                csm_get_drawable_screen_colors: *lib
                    .get(b"csmGetDrawableScreenColors\0")?,
                csm_reset_drawable_dynamic_flags: *lib
                    .get(b"csmResetDrawableDynamicFlags\0")?,
                _libm: libm,
                _lib: lib,
            })
        }
    }

    /// 查询 Core 版本（返回原始 u32，格式 0xMMmmpppp）
    pub fn version(&self) -> u32 {
        unsafe { (self.csm_get_version)() }
    }

    /// 格式化版本号为 "major.minor.patch"
    pub fn version_string(&self) -> String {
        let v = self.version();
        format!(
            "{}.{}.{}",
            (v >> 24) & 0xFF,
            (v >> 16) & 0xFF,
            v & 0xFFFF
        )
    }
}

/// 分配对齐内存（返回对齐指针和底层分配，底层分配需保持存活）
pub fn alloc_aligned(size: usize, align: usize) -> anyhow::Result<AlignedBuffer> {
    let layout = std::alloc::Layout::from_size_align(size, align)?;
    let ptr = unsafe { std::alloc::alloc(layout) };
    if ptr.is_null() {
        anyhow::bail!("aligned alloc failed for size={size} align={align}");
    }
    Ok(AlignedBuffer { ptr, layout })
}

pub struct AlignedBuffer {
    ptr: *mut u8,
    layout: std::alloc::Layout,
}

impl AlignedBuffer {
    pub fn as_ptr(&self) -> *mut u8 {
        self.ptr
    }
    pub fn as_mut_void(&mut self) -> *mut c_void {
        self.ptr as *mut c_void
    }
    pub fn as_void(&self) -> *const c_void {
        self.ptr as *const c_void
    }
}

impl Drop for AlignedBuffer {
    fn drop(&mut self) {
        unsafe { std::alloc::dealloc(self.ptr, self.layout) }
    }
}

unsafe impl Send for AlignedBuffer {}
unsafe impl Sync for AlignedBuffer {}

/// 将 C 字符串指针转换为 Rust String（空指针返回 None）
pub unsafe fn cstr_ptr_to_string(ptr: *const c_char) -> Option<String> {
    if ptr.is_null() {
        return None;
    }
    let raw = std::slice::from_raw_parts(ptr as *const u8, libc_strlen(ptr));
    Some(String::from_utf8_lossy(raw).into_owned())
}

/// 简易 strlen（避免依赖 libc crate）
unsafe fn libc_strlen(ptr: *const c_char) -> usize {
    let mut p = ptr as *const u8;
    let mut n = 0;
    while *p != 0 {
        p = p.add(1);
        n += 1;
    }
    n
}
