//! Common WINGs widget fields.
//!
//! WINGs widgets all have a [`WINGsP::WMView`] object that tracks common attributes
//! like size and location and a [`WINGsP::W_Class`] field that identifies the widget
//! type (button, list, etc.). This module provides access to these fields.
//!
//! ## Rust rewrite notes
//!
//! The original WINGs implementation uses casting and a common layout for
//! "primitive" widget structs, such that the first two fields are always a
//! `W_Class` and a `*mut WMView`. We rely on this convention when implementing
//! [`Widget`] for widgets that are defined in C.
use crate::WINGsP;
use std::{
    ptr::NonNull,
};

/// Base WINGs widget type.
///
/// All primitive widgets (like buttons and lists) should implement this.
pub trait Widget {
    fn class(&self) -> WINGsP::W_Class;
    fn view(&self) -> NonNull<WINGsP::WMView>;
}

/// Common widget header fields. It should be safe to cast opaque WINGs widget
/// structs defined in C to this type to access these fields. Before writing
/// code that casts anything to this, double-check the widget definition!
#[repr(C)]
struct WidgetLike {
    class: WINGsP::W_Class,
    view: *mut WINGsP::WMView,
}

/// Casts `$widget` to [`WidgetLike`]. Provided for convenience.
///
/// ## Safety
///
/// The caller of this macro is responsible for ensuring that the layout of `$widget` is compatible
/// with `WidgetLike`.
macro_rules! impl_widget {
    ($widget:ty) => {
        impl Widget for $widget {
            fn class(&self) -> crate::WINGsP::W_Class {
                unsafe { ::std::mem::transmute::<&$widget, &WidgetLike>(self).class }
            }

            fn view(&self) -> ::std::ptr::NonNull<WINGsP::WMView> {
                let widget = unsafe { ::std::mem::transmute::<&$widget, &WidgetLike>(self) };
                NonNull::new(widget.view).expect("WMView field of widget struct cannot be null")
            }
        }
    };
}

impl_widget!(WINGsP::WMWindow);
impl_widget!(WINGsP::WMFrame);
