use super::*;

/// This trait must be implemented by the `ValueType` generic parameter of the
/// [`Graph`]. The trait allows drawing custom inline widgets for the different
/// types of the node graph.
///
/// The [`Default`] trait bound is required to circumvent borrow checker issues
/// using `std::mem::take` Otherwise, it would be impossible to pass the
/// `node_data` parameter during `value_widget`. The default value is never
/// used, so the implementation is not important, but it should be reasonably
/// cheap to construct.
pub trait WidgetValueTrait: Default {
    type Response;
    type UserState;
    type NodeData;

    /// This method will be called for each input parameter with a widget with
    /// an disconnected input only. To display UI for connected inputs use
    /// [`WidgetValueTrait::value_widget_connected`]. The return value is a
    /// vector of custom response objects which can be used to implement
    /// handling of side effects. If unsure, the response Vec can be empty.
    fn value_widget(
        &mut self,
        param_name: &str,
        node_id: NodeId,
        ui: &mut egui::Ui,
        user_state: &mut Self::UserState,
        node_data: &Self::NodeData,
    ) -> Vec<Self::Response>;

    /// This method will be called for each input parameter with a widget with a
    /// connected input only. To display UI for diconnected inputs use
    /// [`WidgetValueTrait::value_widget`]. The return value is a vector of
    /// custom response objects which can be used to implement handling of
    /// side effects. If unsure, the response Vec can be empty.
    ///
    /// Shows the input name label by default.
    fn value_widget_connected(
        &mut self,
        param_name: &str,
        _node_id: NodeId,
        ui: &mut egui::Ui,
        _user_state: &mut Self::UserState,
        _node_data: &Self::NodeData,
    ) -> Vec<Self::Response> {
        ui.label(param_name);

        Default::default()
    }
}

/// This trait must be implemented by the `DataType` generic parameter of the
/// [`Graph`]. This trait tells the library how to visually expose data types
/// to the user.
pub trait DataTypeTrait<UserState>: PartialEq + Eq {
    /// The associated port color of this datatype
    fn data_type_color(&self, user_state: &mut UserState) -> egui::Color32;

    /// The name of this datatype. Return type is specified as Cow<str> because
    /// some implementations will need to allocate a new string to provide an
    /// answer while others won't.
    ///
    /// ## Example (borrowed value)
    /// Use this when you can get the name of the datatype from its fields or as
    /// a &'static str. Prefer this method when possible.
    /// ```ignore
    /// pub struct DataType { name: String }
    ///
    /// impl DataTypeTrait<()> for DataType {
    ///     fn name(&self) -> std::borrow::Cow<str> {
    ///         Cow::Borrowed(&self.name)
    ///     }
    /// }
    /// ```
    ///
    /// ## Example (owned value)
    /// Use this when you can't derive the name of the datatype from its fields.
    /// ```ignore
    /// pub struct DataType { some_tag: i32 }
    ///
    /// impl DataTypeTrait<()> for DataType {
    ///     fn name(&self) -> std::borrow::Cow<str> {
    ///         Cow::Owned(format!("Super amazing type #{}", self.some_tag))
    ///     }
    /// }
    /// ```
    fn name(&self) -> std::borrow::Cow<'_, str>;
}

/// This trait must be implemented for the `NodeData` generic parameter of the
/// [`Graph`]. This trait allows customizing some aspects of the node drawing.
pub trait NodeDataTrait
where
    Self: Sized,
{
    /// Must be set to the custom user `NodeResponse` type
    type Response;
    /// Must be set to the custom user `UserState` type
    type UserState;
    /// Must be set to the custom user `DataType` type
    type DataType;
    /// Must be set to the custom user `ValueType` type
    type ValueType;

    /// Additional UI elements to draw in the nodes, after the parameters.
    fn bottom_ui(
        &self,
        ui: &mut egui::Ui,
        node_id: NodeId,
        graph: &Graph<Self, Self::DataType, Self::ValueType>,
        user_state: &mut Self::UserState,
    ) -> Vec<NodeResponse<Self::Response, Self>>
    where
        Self::Response: UserResponseTrait;

    /// UI to draw on the top bar of the node.
    fn top_bar_ui(
        &self,
        _ui: &mut egui::Ui,
        _node_id: NodeId,
        _graph: &Graph<Self, Self::DataType, Self::ValueType>,
        _user_state: &mut Self::UserState,
    ) -> Vec<NodeResponse<Self::Response, Self>>
    where
        Self::Response: UserResponseTrait,
    {
        Default::default()
    }

    /// UI to draw for each output
    ///
    /// Defaults to showing param_name as a simple label.
    fn output_ui(
        &self,
        ui: &mut egui::Ui,
        _node_id: NodeId,
        _graph: &Graph<Self, Self::DataType, Self::ValueType>,
        _user_state: &mut Self::UserState,
        param_name: &str,
    ) -> Vec<NodeResponse<Self::Response, Self>>
    where
        Self::Response: UserResponseTrait,
    {
        ui.label(param_name);

        Default::default()
    }

    /// Set background color on titlebar
    /// If the return value is None, the default color is set.
    fn titlebar_color(
        &self,
        _ui: &egui::Ui,
        _node_id: NodeId,
        _graph: &Graph<Self, Self::DataType, Self::ValueType>,
        _user_state: &mut Self::UserState,
    ) -> Option<egui::Color32> {
        None
    }

    /// Separator to put between elements in the node.
    ///
    /// Invoked between inputs, outputs and bottom UI. Useful for
    /// complicated UIs that start to lose structure without explicit
    /// separators. The `param_id` argument is the id of input or output
    /// *preceeding* the separator.
    ///
    /// Default implementation does nothing.
    fn separator(
        &self,
        _ui: &mut egui::Ui,
        _node_id: NodeId,
        _param_id: AnyParameterId,
        _graph: &Graph<Self, Self::DataType, Self::ValueType>,
        _user_state: &mut Self::UserState,
    ) {
    }

    fn can_delete(
        &self,
        _node_id: NodeId,
        _graph: &Graph<Self, Self::DataType, Self::ValueType>,
        _user_state: &mut Self::UserState,
    ) -> bool {
        true
    }
}

/// This trait can be implemented by any user type. The trait tells the library
/// how to enumerate the node templates it will present to the user as part of
/// the node finder.
pub trait NodeTemplateIter {
    type Item;
    fn all_kinds(&self) -> Vec<Self::Item>;
}

/// Describes a category of nodes.
///
/// Used by [`NodeTemplateTrait::node_finder_categories`] to categorize nodes
/// templates into groups.
///
/// If all nodes in a program are known beforehand, it's usefult to define
/// an enum containing all categories and implement [`CategoryTrait`] for it.
/// This will make it impossible to accidentally create a new category by
/// mis-typing an existing one, like in the case of using string types.
pub trait CategoryTrait {
    /// Name of the category.
    fn name(&self) -> String;
}

impl CategoryTrait for () {
    fn name(&self) -> String {
        String::new()
    }
}

impl CategoryTrait for &str {
    fn name(&self) -> String {
        self.to_string()
    }
}

impl CategoryTrait for String {
    fn name(&self) -> String {
        self.clone()
    }
}

/// This trait must be implemented by the `NodeTemplate` generic parameter of
/// the [`GraphEditorState`]. It allows the customization of node templates. A
/// node template is what describes what kinds of nodes can be added to the
/// graph, what is their name, and what are their input / output parameters.
pub trait NodeTemplateTrait: Clone {
    /// Must be set to the custom user `NodeData` type
    type NodeData;
    /// Must be set to the custom user `DataType` type
    type DataType;
    /// Must be set to the custom user `ValueType` type
    type ValueType;
    /// Must be set to the custom user `UserState` type
    type UserState;
    /// Must be a type that implements the [`CategoryTrait`] trait.
    ///
    /// `&'static str` is a good default if you intend to simply type out
    /// the categories of your node. Use `()` if you don't need categories
    /// at all.
    type CategoryType;

    /// Returns a descriptive name for the node kind, used in the node finder.
    ///
    /// The return type is Cow<str> to allow returning owned or borrowed values
    /// more flexibly. Refer to the documentation for `DataTypeTrait::name` for
    /// more information
    fn node_finder_label(
        &self,
        user_state: &mut Self::UserState,
    ) -> std::borrow::Cow<'_, str>;

    /// Vec of categories to which the node belongs.
    ///
    /// It's often useful to organize similar nodes into categories, which will
    /// then be used by the node finder to show a more manageable UI, especially
    /// if the node template are numerous.
    fn node_finder_categories(
        &self,
        _user_state: &mut Self::UserState,
    ) -> Vec<Self::CategoryType> {
        Vec::default()
    }

    /// Returns a descriptive name for the node kind, used in the graph.
    fn node_graph_label(&self, user_state: &mut Self::UserState) -> String;

    /// Returns the user data for this node kind.
    fn user_data(&self, user_state: &mut Self::UserState) -> Self::NodeData;

    /// This function is run when this node kind gets added to the graph. The
    /// node will be empty by default, and this function can be used to fill its
    /// parameters.
    fn build_node(
        &self,
        graph: &mut Graph<Self::NodeData, Self::DataType, Self::ValueType>,
        user_state: &mut Self::UserState,
        node_id: NodeId,
    );
}

/// The custom user response types when drawing nodes in the graph must
/// implement this trait.
pub trait UserResponseTrait: Clone + std::fmt::Debug {}
