// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: MIT

//! FFI-safe event types shared between C firmware and the Rust/Slint library.
//!
//! The canonical definitions live in the C header `slint-safeui-event.h`.
//! Rust bindings are auto-generated by `bindgen` at build time. This module
//! re-exports the generated types and adds Rust-only conveniences
//! (constructors) plus compile-time layout assertions.

// Re-export bindgen-generated types from the crate-wide bindings module.
pub use crate::bindings::{FfiEvent, FfiEventPayload, FfiEventTag, FfiPointerButton};

// Short aliases for the verbose bindgen variant names. Keeps match arms
// and constructors readable without `use FfiEventTag::*` everywhere.
use FfiEventTag::FfiEventTag_KeyPressRepeated as KeyPressRepeated;
use FfiEventTag::FfiEventTag_KeyPressed as KeyPressed;
use FfiEventTag::FfiEventTag_KeyReleased as KeyReleased;
use FfiEventTag::FfiEventTag_PointerExited as PointerExited;
use FfiEventTag::FfiEventTag_PointerMoved as PointerMoved;
use FfiEventTag::FfiEventTag_PointerPressed as PointerPressed;
use FfiEventTag::FfiEventTag_PointerReleased as PointerReleased;
use FfiEventTag::FfiEventTag_PointerScrolled as PointerScrolled;
use FfiEventTag::FfiEventTag_Quit as Quit;
use FfiEventTag::FfiEventTag_Resized as Resized;

// ---------------------------------------------------------------------------
// Convenience constructors (Rust-side only, never cross FFI)
// ---------------------------------------------------------------------------

impl FfiEvent {
    /// Create a zero-initialized event. Equivalent to C `= {0}`.
    #[inline]
    pub const fn zeroed() -> Self {
        // SAFETY: FfiEvent is a plain C struct of ints, floats, u32s, and
        // C enums. All-zeros is a valid bit pattern.
        unsafe { core::mem::zeroed() }
    }

    /// Quit event — no payload needed.
    #[inline]
    pub const fn quit() -> Self {
        let mut e = Self::zeroed();
        e.tag = Quit;
        e
    }

    /// Pointer pressed at physical position `(x, y)` with the given button.
    #[inline]
    pub const fn pointer_pressed(x: i32, y: i32, button: FfiPointerButton) -> Self {
        let mut e = Self::zeroed();
        e.tag = PointerPressed;
        e.payload.pos_x = x;
        e.payload.pos_y = y;
        e.payload.button = button;
        e
    }

    /// Pointer released at physical position `(x, y)` with the given button.
    #[inline]
    pub const fn pointer_released(x: i32, y: i32, button: FfiPointerButton) -> Self {
        let mut e = Self::zeroed();
        e.tag = PointerReleased;
        e.payload.pos_x = x;
        e.payload.pos_y = y;
        e.payload.button = button;
        e
    }

    /// Pointer moved to physical position `(x, y)`.
    #[inline]
    pub const fn pointer_moved(x: i32, y: i32) -> Self {
        let mut e = Self::zeroed();
        e.tag = PointerMoved;
        e.payload.pos_x = x;
        e.payload.pos_y = y;
        e
    }

    /// Pointer scrolled at physical position `(x, y)` by `(delta_x, delta_y)`.
    #[inline]
    pub const fn pointer_scrolled(x: i32, y: i32, dx: f32, dy: f32) -> Self {
        let mut e = Self::zeroed();
        e.tag = PointerScrolled;
        e.payload.pos_x = x;
        e.payload.pos_y = y;
        e.payload.delta_x = dx;
        e.payload.delta_y = dy;
        e
    }

    /// Pointer exited the window area.
    #[inline]
    pub const fn pointer_exited() -> Self {
        let mut e = Self::zeroed();
        e.tag = PointerExited;
        e
    }

    /// Key pressed (initial). `code_point` is a Unicode scalar value.
    #[inline]
    pub const fn key_pressed(code_point: u32) -> Self {
        let mut e = Self::zeroed();
        e.tag = KeyPressed;
        e.payload.key_code = code_point;
        e
    }

    /// Key press repeated (held). `code_point` is a Unicode scalar value.
    #[inline]
    pub const fn key_press_repeated(code_point: u32) -> Self {
        let mut e = Self::zeroed();
        e.tag = KeyPressRepeated;
        e.payload.key_code = code_point;
        e
    }

    /// Key released. `code_point` is a Unicode scalar value.
    #[inline]
    pub const fn key_released(code_point: u32) -> Self {
        let mut e = Self::zeroed();
        e.tag = KeyReleased;
        e.payload.key_code = code_point;
        e
    }

    /// Display resized to `(width, height)` physical pixels.
    #[inline]
    pub const fn resized(width: i32, height: i32) -> Self {
        let mut e = Self::zeroed();
        e.tag = Resized;
        e.payload.width = width;
        e.payload.height = height;
        e
    }
}

// ---------------------------------------------------------------------------
// Compile-time layout assertions
// Guard the ABI contract between the C header and the Rust side.
// ---------------------------------------------------------------------------

const _: () = {
    assert!(core::mem::size_of::<FfiEventTag>() == 4);
    assert!(core::mem::size_of::<FfiPointerButton>() == 4);
    assert!(core::mem::size_of::<FfiEventPayload>() == 36);
    assert!(core::mem::size_of::<FfiEvent>() == 40);
    assert!(core::mem::align_of::<FfiEvent>() == 4);
};
