// SPDX-License-Identifier: GPL-3.0-or-later

//! Abstraction layer for UI components.

use std::rc::Rc;

use crate::{annotations::Annotations, props::ExtendingProps, Style};

#[cfg(feature = "gtk")]
pub use super::gtk_components::*;

pub type Component = Rc<dyn ComponentExt>;

/// Base trait of all components.
/// Every grx component is a ComponentExt.
pub trait ComponentExt {
    /// Whether this component is visible or not.
    fn visible(&self) -> bool;

    /// The actual height of the component
    fn height(&self) -> i32;

    /// The actual width of the component
    fn width(&self) -> i32;

    /// Set the visibility of this component.
    fn set_visible(&self, visible: bool);

    /// Add a CSS class to this component.
    fn add_class(&self, class: &str);
    /// Get the list of CSS classes of this component.
    fn classes(&self) -> Vec<String>;

    /// Remove a CSS from this component.
    fn remove_class(&self, class: &str);

    /// Set the `crate::styles::Style`s of this component.
    ///
    /// This does not remove styles that are already set but not included in the given vec.
    fn set_styles(&self, styles: Vec<Style>);

    /// Get the inner component (may be platform specific thing).
    fn inner(&self) -> Rc<dyn std::any::Any>;

    /// Get the props of this component.
    fn props(&self) -> &dyn ExtendingProps;
    /// Get the annotations of this component.
    fn annotations(&self) -> std::cell::Ref<'_, Annotations>;
    /// Get a mutable ref to the annotations of this component.
    fn annotations_mut(&self) -> std::cell::RefMut<'_, Annotations>;
    /// Like `annotations_mut` but with a Result. Errors when the annotations can not be borrowed mutably.
    fn try_annotations_mut(
        &self,
    ) -> Result<std::cell::RefMut<'_, Annotations>, std::cell::BorrowMutError>;

    /// Get the children of this component.
    fn children(&self) -> std::cell::Ref<Vec<Component>>;
    /// Get a mutable ref to the children of this component.
    fn children_mut(&self) -> std::cell::RefMut<Vec<Component>>;

    /// Cast to any.
    fn into_any(self: Rc<Self>) -> Rc<dyn std::any::Any>;
}

/// An Item is a thing in a list. Like an entry in a dropdown or a item in a menu.
pub trait ItemExt: std::fmt::Debug {
    fn id(&self) -> String;
    fn value(&self) -> String;
}

impl std::fmt::Debug for dyn ComponentExt {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Component")
            .field("props", &self.props())
            .finish()
    }
}

/// A Component that can contain children.
///
/// This is a `div` in HTML or a `Box` in GTK.
pub trait ContainerExt {
    fn append<W>(self: &Rc<Self>, child: Rc<W>)
    where
        W: ComponentExt + ?Sized;

    fn remove<W>(self: &Rc<Self>, child: Rc<W>)
    where
        W: ComponentExt + ?Sized;

    fn clear(self: &Rc<Self>);
}

/// Components that display text implement `Textual`.
/// For example a simple label but also a input/entry field displays text.
pub trait Textual {
    fn text(self: &Rc<Self>) -> String;
    fn set_text(self: &Rc<Self>, text: &str);
}

/// Components that can have some kind of interaction implement this trait.
///
/// For example a Button will implement the `on_click` function.
pub trait Interactable
where
    Self: Sized,
{
    /// Run the given handler when a component is clicked.
    #[allow(unused_variables)]
    fn on_click(self: &Rc<Self>, handler: impl Fn(&Rc<Self>) + 'static) {
        // optional
    }
    /// Run the given handler when a component changes it's value or state.
    #[allow(unused_variables)]
    fn on_change(self: &Rc<Self>, handler: impl Fn(&Rc<Self>) + 'static) {
        // optional
    }
    /// Run the given handler when a component is swiped.
    #[allow(unused_variables)]
    fn on_swipe(self: &Rc<Self>, handler: impl Fn(&Rc<Self>, f64, f64) + 'static) {
        // optional
    }
    /// Run the given handler when a component loses focus.
    #[allow(unused_variables)]
    fn on_blur(self: &Rc<Self>, handler: impl Fn(&Rc<Self>) + 'static) {
        // optional
    }
}
