//! This module contains traits and structs related to behaviour.

use crate::named::Named;
use crate::uuidd::UUIDd;

/// A [`Behaviour`] is [`Named`] and [`UUIDd`].
pub trait Behaviour: Named + UUIDd {}

/// This is an implementation of [`Behaviour`].
pub struct BasicBehaviour {
    /// The name of the [`BasicBehaviour`].
    name: String,
    /// The UUID of the [`BasicBehaviour`].
    uuid: uuid::Uuid,
}

impl BasicBehaviour {
    /// Create a new [`BasicBehaviour`].
    ///
    /// This is an implementation of [`Behaviour`].
    /// It takes just a `name` as an argument.
    /// The `uuid` is randomly generated.
    ///
    /// ```rust
    /// use belief_spread::behaviour::BasicBehaviour;
    /// let x: BasicBehaviour = BasicBehaviour::new("name".to_string());
    /// ```
    pub fn new(name: String) -> Self {
        BasicBehaviour::new_with_uuid(name, uuid::Uuid::new_v4())
    }

    /// Create a new [`BasicBehaviour`].
    ///
    /// This is an implementation of [`Behaviour`].
    /// It takes just a `name` and `uuid` as an argument.
    ///
    /// ```rust
    /// use belief_spread::behaviour::BasicBehaviour;
    /// let x: BasicBehaviour = BasicBehaviour::new_with_uuid(
    ///     "name".to_string(),
    ///     uuid::Uuid::new_v4()
    /// );
    /// ```
    pub fn new_with_uuid(name: String, uuid: uuid::Uuid) -> Self {
        Self { name, uuid }
    }
}

impl Behaviour for BasicBehaviour {}

impl Named for BasicBehaviour {
    fn get_name(&self) -> &str {
        self.name.as_str()
    }

    fn set_name(&mut self, name: String) {
        self.name = name;
    }
}

impl UUIDd for BasicBehaviour {
    fn get_uuid(&self) -> &uuid::Uuid {
        &self.uuid
    }

    fn set_uuid(&mut self, new_uuid: uuid::Uuid) {
        self.uuid = new_uuid
    }
}

#[cfg(test)]
mod tests {
    use crate::{named::Named, uuidd::UUIDd};

    use super::BasicBehaviour;

    #[test]
    fn new_assigns_name_then_get_name() {
        let x = BasicBehaviour::new("name".to_string());
        assert_eq!(x.get_name(), "name");
    }

    #[test]
    fn new_assigns_name_then_set_name_then_get_name() {
        let mut x = BasicBehaviour::new("name".to_string());
        assert_eq!(x.get_name(), "name");
        x.set_name("name2".to_string());
        assert_eq!(x.get_name(), "name2")
    }

    #[test]
    fn new_with_uuid_then_get_uuid() {
        let u = uuid::Uuid::new_v4();
        let x = BasicBehaviour::new_with_uuid("x".to_string(), u);
        assert_eq!(x.get_uuid(), &u);
        assert_eq!(x.get_name(), "x");
    }

    #[test]
    fn new_with_uuid_then_set_uuid_then_get_uuid() {
        let u1 = uuid::Uuid::new_v4();
        let mut x = BasicBehaviour::new_with_uuid("x".to_string(), u1);
        assert_eq!(x.get_uuid(), &u1);
        assert_eq!(x.get_name(), "x");
        let u2 = uuid::Uuid::new_v4();
        x.set_uuid(u2);
        assert_eq!(x.get_uuid(), &u2);
        assert_eq!(x.get_name(), "x");
    }

    #[test]
    fn new_assigns_random_uuid() {
        let x1 = BasicBehaviour::new("x1".to_string());
        let x2 = BasicBehaviour::new("x2".to_string());
        assert_eq!(x1.get_name(), "x1");
        assert_eq!(x2.get_name(), "x2");
        assert_ne!(x1.get_uuid(), x2.get_uuid());
    }
}
