// Copyright 2026 Ling71671
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
/// Scoped drawing abstraction that isolates UI widgets from raw global DrawList command queues
/// and enforces local coordinate translation and active scissor clip boundaries.
pub struct Painter {
  draw_list : @draw.DrawList
  clip_rect : @math.Rect?
  offset : @math.Vec2
} derive(Eq, Debug)

///|
pub fn Painter::new(
  draw_list : @draw.DrawList,
  clip_rect? : @math.Rect? = None,
  offset? : @math.Vec2 = @math.Vec2::zero(),
) -> Painter {
  { draw_list, clip_rect, offset, }
}

///|
/// Returns the active drawing coordinate offset.
pub fn Painter::offset(self : Painter) -> @math.Vec2 {
  self.offset
}

///|
/// Returns the current active clip rectangle if one is active.
pub fn Painter::clip_rect(self : Painter) -> @math.Rect? {
  self.clip_rect
}

///|
/// Create a child painter with an additional local translation offset and optional intersected clip rectangle.
pub fn Painter::sub_painter(
  self : Painter,
  local_offset : @math.Vec2,
  sub_clip? : @math.Rect? = None,
) -> Painter {
  let combined_offset = self.offset.add(local_offset)
  let effective_clip = match (self.clip_rect, sub_clip) {
    (Some(parent), Some(child)) => Some(parent.intersect(child))
    (Some(parent), None) => Some(parent)
    (None, Some(child)) => Some(child)
    (None, None) => None
  }
  Painter::new(self.draw_list, clip_rect=effective_clip, offset=combined_offset)
}

///|
/// Render a filled rectangle with optional corner rounding, shifted by the painter's offset.
pub fn Painter::add_rect(
  self : Painter,
  rect : @math.Rect,
  color : @color.Color,
  corner_radius : Double,
) -> Unit {
  let shifted_rect = @math.Rect::new(
    rect.x + self.offset.x,
    rect.y + self.offset.y,
    rect.w,
    rect.h,
  )
  self.draw_list.add_rect(shifted_rect, color, corner_radius)
}

///|
/// Render a stroked rectangle with line width and corner rounding, shifted by the painter's offset.
pub fn Painter::add_rect_stroke(
  self : Painter,
  rect : @math.Rect,
  color : @color.Color,
  stroke_width : Double,
  corner_radius : Double,
) -> Unit {
  let shifted_rect = @math.Rect::new(
    rect.x + self.offset.x,
    rect.y + self.offset.y,
    rect.w,
    rect.h,
  )
  self.draw_list.add_rect_stroke(
    shifted_rect, color, stroke_width, corner_radius,
  )
}

///|
/// Render a filled circle shifted by the painter's offset.
pub fn Painter::add_circle(
  self : Painter,
  center : @math.Vec2,
  radius : Double,
  color : @color.Color,
) -> Unit {
  let shifted_center = @math.Vec2::new(
    center.x + self.offset.x,
    center.y + self.offset.y,
  )
  self.draw_list.add_circle(shifted_center, radius, color)
}

///|
/// Render a stroked circle with specified line width, shifted by the painter's offset.
pub fn Painter::add_circle_stroke(
  self : Painter,
  center : @math.Vec2,
  radius : Double,
  color : @color.Color,
  stroke_width : Double,
) -> Unit {
  let shifted_center = @math.Vec2::new(
    center.x + self.offset.x,
    center.y + self.offset.y,
  )
  self.draw_list.add_circle_stroke(shifted_center, radius, color, stroke_width)
}

///|
/// Render a line segment connecting two endpoints, shifted by the painter's offset.
pub fn Painter::add_line(
  self : Painter,
  start : @math.Vec2,
  end : @math.Vec2,
  color : @color.Color,
  stroke_width : Double,
) -> Unit {
  let shifted_start = @math.Vec2::new(
    start.x + self.offset.x,
    start.y + self.offset.y,
  )
  let shifted_end = @math.Vec2::new(
    end.x + self.offset.x,
    end.y + self.offset.y,
  )
  self.draw_list.add_line(shifted_start, shifted_end, color, stroke_width)
}

///|
/// Render text at the specified local position, shifted by the painter's offset.
pub fn Painter::add_text(
  self : Painter,
  pos : @math.Vec2,
  text : String,
  font_size : Double,
  color : @color.Color,
  font_family? : String = "",
  font_weight? : Int = 500,
) -> Unit {
  let shifted_pos = @math.Vec2::new(
    pos.x + self.offset.x,
    pos.y + self.offset.y,
  )
  self.draw_list.add_text(
    shifted_pos,
    text,
    font_size,
    color,
    font_family~,
    font_weight~,
  )
}

///|
/// Render a linear color gradient inside a rectangle, shifted by the painter's offset.
pub fn Painter::add_linear_gradient(
  self : Painter,
  rect : @math.Rect,
  start_pt : @math.Vec2,
  end_pt : @math.Vec2,
  start_color : @color.Color,
  end_color : @color.Color,
  corner_radius : Double,
) -> Unit {
  let shifted_rect = @math.Rect::new(
    rect.x + self.offset.x,
    rect.y + self.offset.y,
    rect.w,
    rect.h,
  )
  let shifted_start = @math.Vec2::new(
    start_pt.x + self.offset.x,
    start_pt.y + self.offset.y,
  )
  let shifted_end = @math.Vec2::new(
    end_pt.x + self.offset.x,
    end_pt.y + self.offset.y,
  )
  self.draw_list.add_linear_gradient(
    shifted_rect, shifted_start, shifted_end, start_color, end_color, corner_radius,
  )
}

///|
/// Render a cubic bezier curve, shifted by the painter's offset.
pub fn Painter::add_bezier_curve(
  self : Painter,
  p0 : @math.Vec2,
  p1 : @math.Vec2,
  p2 : @math.Vec2,
  p3 : @math.Vec2,
  color : @color.Color,
  stroke_width : Double,
) -> Unit {
  let off = self.offset
  let shifted_p0 = @math.Vec2::new(p0.x + off.x, p0.y + off.y)
  let shifted_p1 = @math.Vec2::new(p1.x + off.x, p1.y + off.y)
  let shifted_p2 = @math.Vec2::new(p2.x + off.x, p2.y + off.y)
  let shifted_p3 = @math.Vec2::new(p3.x + off.x, p3.y + off.y)
  self.draw_list.add_bezier_curve(
    shifted_p0, shifted_p1, shifted_p2, shifted_p3, color, stroke_width,
  )
}

///|
/// Push a scissor clipping region to the underlying draw list.
pub fn Painter::push_clip(self : Painter, rect : @math.Rect) -> Unit {
  let shifted_rect = @math.Rect::new(
    rect.x + self.offset.x,
    rect.y + self.offset.y,
    rect.w,
    rect.h,
  )
  self.draw_list.push_clip(shifted_rect)
}

///|
/// Pop the active scissor clipping region from the underlying draw list.
pub fn Painter::pop_clip(self : Painter) -> Unit {
  self.draw_list.pop_clip()
}

///|
/// Append another draw list to this painter's target list.
pub fn Painter::append(self : Painter, other : @draw.DrawList) -> Unit {
  self.draw_list.append(other)
}