///|
pub enum DrawCmd {
  Rect(@math.Rect, @color.Color, Double)
  RectStroke(@math.Rect, @color.Color, Double, Double)
  Circle(@math.Vec2, Double, @color.Color)
  CircleStroke(@math.Vec2, Double, @color.Color, Double)
  Line(@math.Vec2, @math.Vec2, @color.Color, Double)
  Text(@math.Vec2, String, Double, @color.Color, String, Int)
  Clip(@math.Rect)
  ResetClip
  LinearGradient(
    @math.Rect,
    @math.Vec2,
    @math.Vec2,
    @color.Color,
    @color.Color,
    Double
  )
  BezierCurve(
    @math.Vec2,
    @math.Vec2,
    @math.Vec2,
    @math.Vec2,
    @color.Color,
    Double
  )
} derive(Eq, Debug)

///|
pub struct DrawList {
  commands : Array[DrawCmd]
} derive(Eq, Debug)

///|
pub fn DrawList::new() -> DrawList {
  { commands: [], }
}

///|
pub fn DrawList::clear(self : DrawList) -> Unit {
  self.commands.clear()
}

///|
pub fn DrawList::len(self : DrawList) -> Int {
  self.commands.length()
}

///|
pub fn DrawList::commands(self : DrawList) -> Array[DrawCmd] {
  self.commands
}

///|
pub fn DrawList::is_empty(self : DrawList) -> Bool {
  self.commands.is_empty()
}

///|
pub fn DrawList::push(self : DrawList, cmd : DrawCmd) -> Unit {
  self.commands.push(cmd)
}

///|
pub fn DrawList::add_rect(
  self : DrawList,
  rect : @math.Rect,
  color : @color.Color,
  corner_radius : Double,
) -> Unit {
  self.commands.push(DrawCmd::Rect(rect, color, corner_radius))
}

///|
pub fn DrawList::add_rect_stroke(
  self : DrawList,
  rect : @math.Rect,
  color : @color.Color,
  stroke_width : Double,
  corner_radius : Double,
) -> Unit {
  self.commands.push(
    DrawCmd::RectStroke(rect, color, stroke_width, corner_radius),
  )
}

///|
pub fn DrawList::add_circle(
  self : DrawList,
  center : @math.Vec2,
  radius : Double,
  color : @color.Color,
) -> Unit {
  self.commands.push(DrawCmd::Circle(center, radius, color))
}

///|
pub fn DrawList::add_circle_stroke(
  self : DrawList,
  center : @math.Vec2,
  radius : Double,
  color : @color.Color,
  stroke_width : Double,
) -> Unit {
  self.commands.push(DrawCmd::CircleStroke(center, radius, color, stroke_width))
}

///|
pub fn DrawList::add_line(
  self : DrawList,
  start : @math.Vec2,
  end : @math.Vec2,
  color : @color.Color,
  stroke_width : Double,
) -> Unit {
  self.commands.push(DrawCmd::Line(start, end, color, stroke_width))
}

///|
pub fn DrawList::add_text(
  self : DrawList,
  pos : @math.Vec2,
  text : String,
  font_size : Double,
  color : @color.Color,
  font_family? : String = "",
  font_weight? : Int = 500,
) -> Unit {
  self.commands.push(
    DrawCmd::Text(pos, text, font_size, color, font_family, font_weight),
  )
}

///|
pub fn DrawList::add_linear_gradient(
  self : DrawList,
  rect : @math.Rect,
  start_pt : @math.Vec2,
  end_pt : @math.Vec2,
  start_color : @color.Color,
  end_color : @color.Color,
  corner_radius : Double,
) -> Unit {
  self.commands.push(
    DrawCmd::LinearGradient(
      rect, start_pt, end_pt, start_color, end_color, corner_radius,
    ),
  )
}

///|
pub fn DrawList::add_bezier_curve(
  self : DrawList,
  p0 : @math.Vec2,
  p1 : @math.Vec2,
  p2 : @math.Vec2,
  p3 : @math.Vec2,
  color : @color.Color,
  stroke_width : Double,
) -> Unit {
  self.commands.push(DrawCmd::BezierCurve(p0, p1, p2, p3, color, stroke_width))
}

///|
pub fn DrawList::push_clip(self : DrawList, rect : @math.Rect) -> Unit {
  self.commands.push(DrawCmd::Clip(rect))
}

///|
pub fn DrawList::pop_clip(self : DrawList) -> Unit {
  self.commands.push(DrawCmd::ResetClip)
}

///|
/// Append every command of `other` into this list, preserving order.
/// Used to composite the deferred foreground layer onto the main layer.
pub fn DrawList::append(self : DrawList, other : DrawList) -> Unit {
  let count = other.commands.length()
  for i = 0; i < count; i = i + 1 {
    self.commands.push(other.commands[i])
  }
}

///|
pub fn DrawList::get_command(self : DrawList, index : Int) -> DrawCmd? {
  if index >= 0 && index < self.commands.length() {
    Some(self.commands[index])
  } else {
    None
  }
}