///|
pub(all) struct ImageRegion2D {
  position : @math.Vec2
  size : @math.Vec2
} derive(Eq, Debug)

///|
pub(all) struct ImageDrawCommand2D {
  image : @asset_types.ImageHandle
  destination : @math.Rect
  source : ImageRegion2D?
  transform : @math.Transform
  uv_transform : @math.Transform
  repeat : @math.RepeatMode
  alpha_mode : @render.AlphaMode2D
  blend_mode : @render.BlendMode2D
  color : @render.Color
}

///|
pub(all) struct FontWeight(UInt) derive(Eq, Debug, Hash)

///|
pub fn FontWeight::new(value : UInt) -> FontWeight {
  FontWeight(value).clamp()
}

///|
pub fn FontWeight::thin() -> FontWeight {
  FontWeight(100U)
}

///|
pub fn FontWeight::extra_light() -> FontWeight {
  FontWeight(200U)
}

///|
pub fn FontWeight::light() -> FontWeight {
  FontWeight(300U)
}

///|
pub fn FontWeight::normal() -> FontWeight {
  FontWeight(400U)
}

///|
pub fn FontWeight::medium() -> FontWeight {
  FontWeight(500U)
}

///|
pub fn FontWeight::semi_bold() -> FontWeight {
  FontWeight(600U)
}

///|
pub fn FontWeight::bold() -> FontWeight {
  FontWeight(700U)
}

///|
pub fn FontWeight::extra_bold() -> FontWeight {
  FontWeight(800U)
}

///|
pub fn FontWeight::black() -> FontWeight {
  FontWeight(900U)
}

///|
pub fn FontWeight::extra_black() -> FontWeight {
  FontWeight(950U)
}

///|
pub fn FontWeight::clamp(self : FontWeight) -> FontWeight {
  match self {
    FontWeight(0U) => FontWeight::normal()
    FontWeight(value) if value > 1000U => FontWeight(1000U)
    _ => self
  }
}

///|
pub fn FontWeight::value(self : FontWeight) -> UInt {
  match self.clamp() {
    FontWeight(value) => value
  }
}

///|
pub(all) struct TextStyle2D {
  font : @asset_types.FontHandle?
  family : String
  size : Double
  weight : FontWeight
  color : @render.Color
  line_height : Double
  letter_spacing : Double
  align : @math.HAlign
  baseline : @math.VAlign
}

///|
pub(all) struct TextSection2D {
  text : String
  style : TextStyle2D
  underline : Bool
}

///|
pub(all) struct TextDrawCommand2D {
  text : String
  sections : Array[TextSection2D]
  position : @math.Vec2
  transform : @math.Transform
  blend_mode : @render.BlendMode2D
  style : TextStyle2D
  max_width : Double?
  max_height : Double?
  wrap : Bool
  ellipsis : Bool
}

///|
pub(all) struct RectDrawCommand2D {
  rect : @math.Rect
  transform : @math.Transform
  alpha_mode : @render.AlphaMode2D
  blend_mode : @render.BlendMode2D
  fill_color : @render.Color
  stroke_color : @render.Color?
}

///|
pub(all) struct CircleDrawCommand2D {
  center : @math.Vec2
  radius : Double
  transform : @math.Transform
  alpha_mode : @render.AlphaMode2D
  blend_mode : @render.BlendMode2D
  fill_color : @render.Color
  stroke_color : @render.Color?
}

///|
/// Backend-independent topology for untextured 2D geometry.
pub(all) enum GeometryTopology2D {
  TriangleList
  LineList
} derive(Eq, Debug)

///|
/// A backend-independent, non-indexed 2D geometry draw command.
///
/// Triangle vertices are grouped in threes and line vertices in pairs. The
/// local vertices are transformed first, then translated by `origin`.
pub(all) struct GeometryDrawCommand2D {
  vertices : Array[@math.Vec2]
  topology : GeometryTopology2D
  origin : @math.Vec2
  transform : @math.Transform
  alpha_mode : @render.AlphaMode2D
  blend_mode : @render.BlendMode2D
  color : @render.Color
}

///|
pub(all) struct GradientStop2D {
  offset : Double
  color : @render.Color
} derive(Eq, Debug)

///|
pub(all) struct GradientRectDrawCommand2D {
  rect : @math.Rect
  transform : @math.Transform
  alpha_mode : @render.AlphaMode2D
  blend_mode : @render.BlendMode2D
  radii : RoundedCornerRadii2D
  output_scale : Double
  start : @math.Vec2
  end : @math.Vec2
  stops : Array[GradientStop2D]
}

///|
pub(all) struct RoundedCornerRadii2D {
  top_left : Double
  top_right : Double
  bottom_right : Double
  bottom_left : Double
} derive(Eq, Debug, Hash)

///|
pub(all) struct EdgeWidths2D {
  top : Double
  right : Double
  bottom : Double
  left : Double
} derive(Eq, Debug, Hash)

///|
pub(all) struct RoundedOutline2D {
  width : Double
  offset : Double
  color : @render.Color
} derive(Eq, Debug, Hash)

///|
pub(all) struct RoundedShadowLayer2D {
  offset : @math.Vec2
  spread : Double
  blur : Double
  color : @render.Color
} derive(Eq, Debug, Hash)

///|
pub(all) struct RoundedChromeDrawCommand2D {
  rect : @math.Rect
  radii : RoundedCornerRadii2D
  fill_color : @render.Color?
  border_widths : EdgeWidths2D
  border_color : @render.Color?
  outline : RoundedOutline2D?
  shadows : Array[RoundedShadowLayer2D]
  output_scale : Double
  alpha_mode : @render.AlphaMode2D
  blend_mode : @render.BlendMode2D
} derive(Eq, Debug, Hash)

///|
pub(all) struct ColoredVertex2D {
  position : @math.Vec2
  color : @render.Color
} derive(Eq, Debug)

///|
pub(all) struct ColoredTriangle2D {
  a : ColoredVertex2D
  b : ColoredVertex2D
  c : ColoredVertex2D
} derive(Eq, Debug)

///|
pub(all) struct ColoredGeometryDrawCommand2D {
  triangles : Array[ColoredTriangle2D]
  alpha_mode : @render.AlphaMode2D
  blend_mode : @render.BlendMode2D
}

///|
pub(all) struct ClipRectCommand2D {
  rect : @math.Rect
}

///|
pub(all) enum DrawCommand2D {
  Image(ImageDrawCommand2D)
  ImageMaterial(ImageMaterialDrawCommand2D)
  Text(TextDrawCommand2D)
  Rect(RectDrawCommand2D)
  Circle(CircleDrawCommand2D)
  Geometry(GeometryDrawCommand2D)
  GradientRect(GradientRectDrawCommand2D)
  RoundedChrome(RoundedChromeDrawCommand2D)
  ColoredGeometry(ColoredGeometryDrawCommand2D)
  PushClip(ClipRectCommand2D)
  PopClip
}

///|
pub(all) enum RenderPassLoadOp2D {
  Load
  Clear
} derive(Eq, Debug)

///|
pub(all) enum RenderPassTarget2D {
  Screen
  Offscreen(String)
} derive(Eq, Debug)

///|
pub(all) struct RenderPass2D {
  camera_entity_id : UInt?
  order : Int
  viewport : @math.Rect
  load_op : RenderPassLoadOp2D
  clear_color : @render.Color
  target : RenderPassTarget2D
  mut world_commands : Array[DrawCommand2D]
  mut ui_commands : Array[DrawCommand2D]
  mut world_batches : Array[Array[DrawCommand2D]]
  mut ui_batches : Array[Array[DrawCommand2D]]
}

///|
pub(all) struct RenderFrame2D {
  mut passes : Array[RenderPass2D]
  mut world_commands : Array[DrawCommand2D]
  mut ui_commands : Array[DrawCommand2D]
}

///|
pub fn empty_render_frame2d() -> RenderFrame2D {
  { passes: [], world_commands: [], ui_commands: [] }
}