///|
/// Debug drawing and utility functions.
///
/// Reference:
/// - Ebiten ebitenutil package (DebugPrint, DrawLine, DrawRect, etc.)

///|
pub fn color_from_hex(hex : Int) -> @gfx.Color {
  let r = ((hex >> 16) & 0xFF).to_double() / 255.0
  let g = ((hex >> 8) & 0xFF).to_double() / 255.0
  let b = (hex & 0xFF).to_double() / 255.0
  @gfx.new_color(r, g, b, 1.0)
}

///|
pub fn color_from_hex_alpha(hex : Int, alpha : Double) -> @gfx.Color {
  let r = ((hex >> 16) & 0xFF).to_double() / 255.0
  let g = ((hex >> 8) & 0xFF).to_double() / 255.0
  let b = (hex & 0xFF).to_double() / 255.0
  @gfx.new_color(r, g, b, alpha)
}

///|
pub fn color_white() -> @gfx.Color {
  @gfx.new_color(1.0, 1.0, 1.0, 1.0)
}

///|
pub fn color_black() -> @gfx.Color {
  @gfx.new_color(0.0, 0.0, 0.0, 1.0)
}

///|
pub fn color_red() -> @gfx.Color {
  @gfx.new_color(1.0, 0.0, 0.0, 1.0)
}

///|
pub fn color_green() -> @gfx.Color {
  @gfx.new_color(0.0, 1.0, 0.0, 1.0)
}

///|
pub fn color_blue() -> @gfx.Color {
  @gfx.new_color(0.0, 0.0, 1.0, 1.0)
}

///|
pub fn color_yellow() -> @gfx.Color {
  @gfx.new_color(1.0, 1.0, 0.0, 1.0)
}

///|
pub fn color_transparent() -> @gfx.Color {
  @gfx.new_color(0.0, 0.0, 0.0, 0.0)
}

///|
pub fn color_to_uniform_dwords(color : @gfx.Color) -> Array[Int] {
  [
    (color.r * 255.0).to_int(),
    (color.g * 255.0).to_int(),
    (color.b * 255.0).to_int(),
    (color.a * 255.0).to_int(),
  ]
}

///|
/// Build vertex data for a line segment as a thin quad.
/// Returns (vertex_data, indices) for a DrawTrianglesCommand.
pub fn line_vertices(
  x0 : Double,
  y0 : Double,
  x1 : Double,
  y1 : Double,
  width : Double,
) -> (Array[Double], Array[Int]) {
  let dx = x1 - x0
  let dy = y1 - y0
  let dir = @vector.Vec2::new(dx, dy).normalize()
  let perp = dir.perpendicular().scale(width / 2.0)
  // Four corners of the line quad
  let vertices = [
    x0 + perp.x,
    y0 + perp.y,
    0.0,
    0.0,
    x1 + perp.x,
    y1 + perp.y,
    1.0,
    0.0,
    x1 - perp.x,
    y1 - perp.y,
    1.0,
    1.0,
    x0 - perp.x,
    y0 - perp.y,
    0.0,
    1.0,
  ]
  let indices = [0, 1, 2, 2, 3, 0]
  (vertices, indices)
}

///|
/// Build vertex data for an axis-aligned rectangle outline as four line quads.
pub fn rect_outline_vertices(
  x : Double,
  y : Double,
  w : Double,
  h : Double,
  line_width : Double,
) -> (Array[Double], Array[Int]) {
  let all_vertices : Array[Double] = []
  let all_indices : Array[Int] = []
  let lines = [
    (x, y, x + w, y), // top
    (x + w, y, x + w, y + h), // right
    (x + w, y + h, x, y + h), // bottom
    (x, y + h, x, y), // left
  ]
  let mut base_index = 0
  for line in lines {
    let (x0, y0, x1, y1) = line
    let (verts, idxs) = line_vertices(x0, y0, x1, y1, line_width)
    for v in verts {
      all_vertices.push(v)
    }
    for idx in idxs {
      all_indices.push(idx + base_index)
    }
    base_index = base_index + 4
  }
  (all_vertices, all_indices)
}

///|
/// Build vertex data for a filled axis-aligned rectangle.
pub fn rect_fill_vertices(
  x : Double,
  y : Double,
  w : Double,
  h : Double,
) -> (Array[Double], Array[Int]) {
  let vertices = [
    x,
    y,
    0.0,
    0.0,
    x + w,
    y,
    1.0,
    0.0,
    x + w,
    y + h,
    1.0,
    1.0,
    x,
    y + h,
    0.0,
    1.0,
  ]
  let indices = [0, 1, 2, 2, 3, 0]
  (vertices, indices)
}

///|
/// Build a draw command for a filled rectangle using a solid color shader.
pub fn new_rect_fill_command(
  dst : @gfx.ImageHandle,
  shader : @gfx.ShaderHandle,
  x : Double,
  y : Double,
  w : Double,
  h : Double,
  color : @gfx.Color,
  pipeline_id : Int,
) -> @gfx.DrawTrianglesCommand {
  let (vertices, indices) = rect_fill_vertices(x, y, w, h)
  let uniform = color_to_uniform_dwords(color)
  @gfx.new_draw_triangles_command(
    dst,
    shader,
    [@gfx.new_dst_region(x.to_int(), y.to_int(), w.to_int(), h.to_int(), 6)],
    0,
    pipeline_id,
    0,
    @gfx.BlendMode::from_int(1),
    vertices,
    indices,
    [],
    uniform,
  )
}

///|
/// Build a debug overlay showing current input state.
/// Returns draw commands for:
/// - Cursor crosshair at the current position
/// - Small indicator squares for each pressed key
/// - Indicator for pressed mouse buttons
pub fn build_input_debug_overlay(
  dst : @gfx.ImageHandle,
  shader : @gfx.ShaderHandle,
  snapshot : @core.InputSnapshot,
  key_state : @inpututil.KeyInputState,
  mouse_state : @inpututil.MouseButtonInputState,
  pipeline_id : Int,
) -> Array[@gfx.DrawTrianglesCommand] {
  let commands : Array[@gfx.DrawTrianglesCommand] = []
  // Cursor crosshair (2 lines)
  let cx = snapshot.cursor_x
  let cy = snapshot.cursor_y
  let crosshair_size = 10.0
  let crosshair_color = color_from_hex_alpha(0x00FF00, 0.8)
  commands.push(
    new_line_command(
      dst,
      shader,
      cx - crosshair_size,
      cy,
      cx + crosshair_size,
      cy,
      1.0,
      crosshair_color,
      pipeline_id,
    ),
  )
  commands.push(
    new_line_command(
      dst,
      shader,
      cx,
      cy - crosshair_size,
      cx,
      cy + crosshair_size,
      1.0,
      crosshair_color,
      pipeline_id,
    ),
  )
  // Pressed keys: small 8x8 squares in a row at top-left
  let pressed_keys = @inpututil.append_pressed_keys(key_state, [])
  let key_color = color_from_hex_alpha(0xFFFF00, 0.7)
  let just_pressed_color = color_from_hex_alpha(0xFF0000, 0.9)
  let just_pressed_keys = @inpututil.append_just_pressed_keys(key_state, [])
  for i, _key in pressed_keys {
    let kx = 4.0 + i.to_double() * 10.0
    let ky = 4.0
    let is_just = contains_int_arr(just_pressed_keys, pressed_keys[i])
    let c = if is_just { just_pressed_color } else { key_color }
    commands.push(
      new_rect_fill_command(dst, shader, kx, ky, 8.0, 8.0, c, pipeline_id),
    )
  }
  // Mouse buttons: small 12x12 squares below keys
  let pressed_btns = @inpututil.append_pressed_mouse_buttons(mouse_state, [])
  let btn_color = color_from_hex_alpha(0x00AAFF, 0.8)
  for i, _btn in pressed_btns {
    let bx = 4.0 + i.to_double() * 14.0
    let by = 16.0
    commands.push(
      new_rect_fill_command(
        dst, shader, bx, by, 12.0, 12.0, btn_color, pipeline_id,
      ),
    )
  }
  commands
}

///|
fn contains_int_arr(arr : Array[Int], value : Int) -> Bool {
  for item in arr {
    if item == value {
      return true
    }
  }
  false
}

///|
/// Build a draw command for a line segment.
pub fn new_line_command(
  dst : @gfx.ImageHandle,
  shader : @gfx.ShaderHandle,
  x0 : Double,
  y0 : Double,
  x1 : Double,
  y1 : Double,
  width : Double,
  color : @gfx.Color,
  pipeline_id : Int,
) -> @gfx.DrawTrianglesCommand {
  let (vertices, indices) = line_vertices(x0, y0, x1, y1, width)
  let uniform = color_to_uniform_dwords(color)
  let min_x = if x0 < x1 { x0 } else { x1 }
  let min_y = if y0 < y1 { y0 } else { y1 }
  let max_x = if x0 > x1 { x0 } else { x1 }
  let max_y = if y0 > y1 { y0 } else { y1 }
  @gfx.new_draw_triangles_command(
    dst,
    shader,
    [
      @gfx.new_dst_region(
        min_x.to_int(),
        min_y.to_int(),
        (max_x - min_x + width).to_int(),
        (max_y - min_y + width).to_int(),
        6,
      ),
    ],
    0,
    pipeline_id,
    0,
    @gfx.BlendMode::from_int(1),
    vertices,
    indices,
    [],
    uniform,
  )
}

///|
/// Build vertex data for a filled rectangle in NDC (pixel coords → clip space).
pub fn ndc_rect_fill_vertices(
  px : Double,
  py : Double,
  pw : Double,
  ph : Double,
  screen_w : Double,
  screen_h : Double,
) -> (Array[Double], Array[Int]) {
  let x0 = px / screen_w * 2.0 - 1.0
  let y0 = 1.0 - py / screen_h * 2.0
  let x1 = (px + pw) / screen_w * 2.0 - 1.0
  let y1 = 1.0 - (py + ph) / screen_h * 2.0
  let vertices = [
    x0, y0, 0.0, 0.0, x1, y0, 1.0, 0.0, x1, y1, 1.0, 1.0, x0, y1, 0.0, 1.0,
  ]
  let indices = [0, 1, 2, 2, 3, 0]
  (vertices, indices)
}

///|
/// Build a draw command for a filled rectangle in NDC coordinates.
pub fn new_ndc_rect_fill_command(
  dst : @gfx.ImageHandle,
  shader : @gfx.ShaderHandle,
  px : Double,
  py : Double,
  pw : Double,
  ph : Double,
  screen_w : Double,
  screen_h : Double,
  color : @gfx.Color,
  pipeline_id : Int,
) -> @gfx.DrawTrianglesCommand {
  let (vertices, indices) = ndc_rect_fill_vertices(
    px, py, pw, ph, screen_w, screen_h,
  )
  let uniform = color_to_uniform_dwords(color)
  @gfx.new_draw_triangles_command(
    dst,
    shader,
    [@gfx.new_dst_region(0, 0, screen_w.to_int(), screen_h.to_int(), 6)],
    0,
    pipeline_id,
    0,
    @gfx.BlendMode::from_int(1),
    vertices,
    indices,
    [],
    uniform,
  )
}

// ============================================================
// Dot-matrix glyph rendering (3x5 grid)
// ============================================================

///|
pub fn glyph_pattern(ch : Int) -> Array[Int] {
  match ch {
    // Digit values (for draw_number)
    0 | 48 => [1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1]
    1 | 49 => [0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1]
    2 | 50 => [1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1]
    3 | 51 => [1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1]
    4 | 52 => [1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1]
    5 | 53 => [1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1]
    6 | 54 => [1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1]
    7 | 55 => [1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]
    8 | 56 => [1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1]
    9 | 57 => [1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1]
    // A
    65 => [0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1]
    // B
    66 => [1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0]
    // C
    67 => [1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1]
    // D
    68 => [1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0]
    // E
    69 => [1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1]
    // F
    70 => [1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0]
    // G
    71 => [1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1]
    // H
    72 => [1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1]
    // I
    73 => [1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1]
    // J
    74 => [0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1]
    // K
    75 => [1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1]
    // L
    76 => [1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1]
    // M
    77 => [1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1]
    // N
    78 => [1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1]
    // O
    79 => [1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1]
    // P
    80 => [1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0]
    // Q
    81 => [1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1]
    // R
    82 => [1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1]
    // S
    83 => [1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1]
    // T
    84 => [1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0]
    // U
    85 => [1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1]
    // V
    86 => [1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0]
    // W
    87 => [1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1]
    // X
    88 => [1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1]
    // Y
    89 => [1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0]
    // Z
    90 => [1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1]
    // colon ':'
    58 => [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
    // space ' '
    32 => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    // '>'
    62 => [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0]
    // '+'
    43 => [0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0]
    // '/'
    47 => [0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0]
    // '-'
    45 => [0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0]
    // '%'
    37 => [1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1]
    _ => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
  }
}

///|
pub fn draw_dot_text(
  cmds : Array[@gfx.DrawTrianglesCommand],
  dst : @gfx.ImageHandle,
  shader : @gfx.ShaderHandle,
  chars : Array[Int],
  cx : Double,
  cy : Double,
  sw : Double,
  sh : Double,
  color : @gfx.Color,
  scale : Double,
) -> Unit {
  let px_size = 3.0 * scale
  let char_w = 3.0 * px_size
  let gap = px_size
  let total_w = chars.length().to_double() * char_w +
    (chars.length() - 1).to_double() * gap
  let char_h = 5.0 * px_size
  let start_x = cx - total_w / 2.0
  let start_y = cy - char_h / 2.0
  for i = 0; i < chars.length(); i = i + 1 {
    let pattern = glyph_pattern(chars[i])
    let ox = start_x + i.to_double() * (char_w + gap)
    for row = 0; row < 5; row = row + 1 {
      for col = 0; col < 3; col = col + 1 {
        if pattern[row * 3 + col] == 1 {
          let x = ox + col.to_double() * px_size
          let y = start_y + row.to_double() * px_size
          cmds.push(
            new_ndc_rect_fill_command(
              dst, shader, x, y, px_size, px_size, sw, sh, color, 0,
            ),
          )
        }
      }
    }
  }
}

///|
pub fn draw_number(
  cmds : Array[@gfx.DrawTrianglesCommand],
  dst : @gfx.ImageHandle,
  shader : @gfx.ShaderHandle,
  number : Int,
  ox : Double,
  oy : Double,
  sw : Double,
  sh : Double,
  color : @gfx.Color,
  scale : Double,
) -> Unit {
  let digits : Array[Int] = []
  if number == 0 {
    digits.push(0)
  } else {
    let mut n = number
    while n > 0 {
      digits.push(n % 10)
      n = n / 10
    }
    // Reverse
    let len = digits.length()
    for i = 0; i < len / 2; i = i + 1 {
      let tmp = digits[i]
      digits[i] = digits[len - 1 - i]
      digits[len - 1 - i] = tmp
    }
  }
  draw_dot_text(cmds, dst, shader, digits, ox, oy, sw, sh, color, scale)
}

// ============================================================
// Render commands pipeline
// ============================================================

///|
pub fn[T : @gfx.GraphicsDriver] render_commands(
  graphics : T,
  cmds : Array[@gfx.DrawTrianglesCommand],
  clear_color? : @gfx.Color,
  clear_enabled? : Bool,
) -> Unit raise {
  let color = match clear_color {
    Some(c) => c
    None => @gfx.new_color(0.0, 0.0, 0.0, 1.0)
  }
  let clear = match clear_enabled {
    Some(c) => c
    None => true
  }
  let pass = @gfx.new_render_pass_desc(color, clear)
  graphics.begin(pass)
  for cmd in cmds {
    graphics.draw_triangles(cmd)
  }
  graphics.end(true)
}

///|
/// Build a simple draw command with common defaults.
pub fn new_simple_draw_command(
  dst : @gfx.ImageHandle,
  shader : @gfx.ShaderHandle,
  vertices : Array[Double],
  indices : Array[Int],
  color : @gfx.Color,
) -> @gfx.DrawTrianglesCommand {
  let uniform = color_to_uniform_dwords(color)
  @gfx.new_draw_triangles_command(
    dst,
    shader,
    [@gfx.new_dst_region(0, 0, 0, 0, indices.length())],
    0,
    0,
    0,
    @gfx.BlendMode::from_int(1),
    vertices,
    indices,
    [],
    uniform,
  )
}