///|
/// UI node tree and style definitions.

///|
pub(all) enum UIDirection {
  Row
  Column
} derive(Debug)

///|
pub impl Show for UIDirection with fn output(self, logger) {
  logger.write_object(Repr(self))
}

///|
pub(all) enum UISizing {
  Fixed(Double)
  Percent(Double)
  Auto
} derive(Debug)

///|
pub impl Show for UISizing with fn output(self, logger) {
  logger.write_object(Repr(self))
}

///|
pub(all) enum UIFlexWrap {
  NoWrap
  Wrap
} derive(Debug)

///|
pub impl Show for UIFlexWrap with fn output(self, logger) {
  logger.write_object(Repr(self))
}

///|
pub(all) enum UIAlignment {
  Start
  End
  FlexStart
  FlexEnd
  Center
  SpaceBetween
  SpaceAround
  SpaceEvenly
  Stretch
  Baseline
} derive(Debug, Eq)

///|
pub impl Show for UIAlignment with fn output(self, logger) {
  logger.write_object(Repr(self))
}

///|
pub(all) struct UINodeStyle {
  direction : UIDirection
  width : UISizing
  height : UISizing
  padding_left : Double
  padding_right : Double
  padding_top : Double
  padding_bottom : Double
  gap : Double
  align : UIAlignment
  justify_content : UIAlignment
  flex_wrap : UIFlexWrap
} derive(Debug)

///|
pub impl Show for UINodeStyle with fn output(self, logger) {
  logger.write_object(Repr(self))
}

///|
pub fn default_node_style() -> UINodeStyle {
  {
    direction: UIDirection::Row,
    width: UISizing::Auto,
    height: UISizing::Auto,
    padding_left: 0.0,
    padding_right: 0.0,
    padding_top: 0.0,
    padding_bottom: 0.0,
    gap: 0.0,
    align: UIAlignment::Start,
    justify_content: UIAlignment::Start,
    flex_wrap: UIFlexWrap::NoWrap,
  }
}

///|
pub struct UINode {
  id : UINodeId
  style : UINodeStyle
  children : Array[UINodeId]
} derive(Debug)

///|
pub impl Show for UINode with fn output(self, logger) {
  logger.write_object(Repr(self))
}

///|
pub struct UITree {
  nodes : Map[Int, UINode]
  mut next_id : Int
}

///|
pub fn UITree::new() -> UITree {
  { nodes: Map([]), next_id: 1, }
}

///|
pub fn UITree::add_node(
  self : UITree,
  style : UINodeStyle,
  children : Array[UINodeId],
) -> UINodeId {
  let id : UINodeId = { value: self.next_id, }
  self.next_id = self.next_id + 1
  let node : UINode = { id, style, children, }
  self.nodes.set(id.value, node)
  id
}

///|
pub fn UITree::get_node(self : UITree, id : UINodeId) -> UINode? {
  self.nodes.get(id.value)
}

///|
pub fn UITree::node_count(self : UITree) -> Int {
  self.nodes.length()
}

///|
fn ui_clamp(value : Double, min_val : Double, max_val : Double) -> Double {
  let clamped = if value > max_val { max_val } else { value }
  if clamped < min_val {
    min_val
  } else {
    clamped
  }
}

///|
fn ui_compute_justify(
  justify : UIAlignment,
  free_space : Double,
  item_count : Int,
) -> (Double, Double) {
  if item_count == 0 {
    return (0.0, 0.0)
  }
  if free_space < 0.0 {
    match justify {
      SpaceBetween | SpaceAround | SpaceEvenly => return (0.0, 0.0)
      _ => ()
    }
  }
  match justify {
    Start | FlexStart => (0.0, 0.0)
    End | FlexEnd => (free_space, 0.0)
    Center => (free_space / 2.0, 0.0)
    SpaceBetween =>
      if item_count == 1 {
        (0.0, 0.0)
      } else {
        (0.0, free_space / (item_count - 1).to_double())
      }
    SpaceAround => {
      let gap = free_space / item_count.to_double()
      (gap / 2.0, gap)
    }
    SpaceEvenly => {
      let gap = free_space / (item_count + 1).to_double()
      (gap, gap)
    }
    Stretch | Baseline => (0.0, 0.0)
  }
}

///|
fn ui_compute_align_offset(align : UIAlignment, free_space : Double) -> Double {
  if free_space <= 0.0 {
    return 0.0
  }
  match align {
    Start | FlexStart => 0.0
    End | FlexEnd => free_space
    Center | SpaceAround | SpaceEvenly => free_space / 2.0
    SpaceBetween | Stretch | Baseline => 0.0
  }
}

///|
pub extend UIDirection with @moonbitlang/core/debug.Debug::{to_repr}

///|
pub extend UIDirection with Show::{to_string, output}

///|
pub extend UISizing with @moonbitlang/core/debug.Debug::{to_repr}

///|
pub extend UISizing with Show::{to_string, output}

///|
pub extend UIFlexWrap with @moonbitlang/core/debug.Debug::{to_repr}

///|
pub extend UIFlexWrap with Show::{to_string, output}

///|
pub extend UIAlignment with @moonbitlang/core/debug.Debug::{to_repr}

///|
pub extend UIAlignment with Eq::{not_equal, equal}

///|
pub extend UIAlignment with Show::{to_string, output}

///|
pub extend UINodeStyle with @moonbitlang/core/debug.Debug::{to_repr}

///|
pub extend UINodeStyle with Show::{to_string, output}

///|
pub extend UINode with @moonbitlang/core/debug.Debug::{to_repr}

///|
pub extend UINode with Show::{to_string, output}