// Copyright Two Neutron Stars Incorporated and contributors
// SPDX-License-Identifier: BlueOak-1.0.0

//! Built-in implementations of `Bluegum` and `BluegumWithState` for primitive
//! types and common standard library types.

use crate::{
  Bluegum,
  BluegumWithState,
  Builder,
};

/// Implementation for unit type
impl Bluegum for () {
  fn node(&self, b: &mut crate::Builder) {
    b.name("Unit");
  }
}

/// Implementation for Vec<T> where T implements Bluegum
impl<T> Bluegum for Vec<T>
where
  T: Bluegum,
{
  fn node(&self, b: &mut crate::Builder) {
    b.name("Vec").add_nodes("items", self);
  }
}

/// Implementation for 2-tuples where both types implement Bluegum
/// TODO: Extend to tuples up to 12 elements (gold-wqj)
impl<T1, T2> Bluegum for (T1, T2)
where
  T1: Bluegum,
  T2: Bluegum,
{
  fn node(&self, b: &mut crate::Builder) {
    b.name("(T1,T2)")
      .add_node("T1", &self.0)
      .add_node("T2", &self.1);
  }
}

/// Stateful implementation for unit type
impl<State> BluegumWithState<State> for () {
  fn node_with_state(&self, b: &mut crate::Builder, _state: &State) {
    b.name("Unit");
  }
}

/// Stateful implementation for Vec<T> where T implements BluegumWithState
impl<State, T> BluegumWithState<State> for Vec<T>
where
  T: BluegumWithState<State>,
{
  fn node_with_state(&self, b: &mut crate::Builder, state: &State) {
    b.add_builders(
      self
        .iter()
        .map(|node| Builder::render_with_state(node, state))
        .collect::<Vec<Builder>>(),
    );
  }
}

/// Stateful implementation for 2-tuples where both types implement
/// BluegumWithState TODO: Extend to tuples up to 12 elements (gold-d68)
impl<State, T1, T2> BluegumWithState<State> for (T1, T2)
where
  T1: BluegumWithState<State>,
  T2: BluegumWithState<State>,
{
  fn node_with_state(&self, b: &mut crate::Builder, state: &State) {
    b.name("(T1,T2)")
      .add_node("T1", &self.0)
      .add_node("T2", &self.1);
  }
}

/// A simple leaf node helper for creating nodes with just fields (no child
/// nodes)
pub struct Leaf<'a> {
  name:   String,
  fields: Vec<(&'a str, String)>,
}

impl<'a> Leaf<'a> {
  /// Create a new Leaf node with the given name and fields
  pub fn new(name: String, fields: &[(&'a str, String)]) -> Self {
    Self {
      name,
      fields: fields.into(),
    }
  }
}

impl Bluegum for Leaf<'_> {
  fn node(&self, b: &mut crate::Builder) {
    b.name(&self.name);

    for (name, value) in &self.fields {
      b.field(name, value);
    }
  }
}

/// Macro to implement Bluegum and BluegumWithState for primitive types
///
/// This macro generates implementations that display the type name and
/// show the debug representation as an alternate value.
macro_rules! impl_bluegum_for_primitive {
  ($($ty:ty),* $(,)?) => {
    $(
      impl $crate::Bluegum for $ty {
        fn node(&self, b: &mut $crate::Builder) {
          b.name(stringify!($ty)).alt(&format!("{:?}", self));
        }
      }

      impl<State> $crate::BluegumWithState<State> for $ty {
        fn node_with_state(&self, b: &mut $crate::Builder, _state: &State) {
          b.name(stringify!($ty)).alt(&format!("{:?}", self));
        }
      }
    )*
  };
}

// Implement Bluegum for common primitive types
impl_bluegum_for_primitive! {
  u8, u16, u32, u64, u128, usize,
  i8, i16, i32, i64, i128, isize,
  f32, f64,
  bool,
  char,
  String,
}
