///|
//pub struct Renderer(@lib.SDL_Renderer)

pub struct Renderer {
  base : @lib.SDL_Renderer
  mut r : Byte
  mut g : Byte
  mut b : Byte
  mut a : Byte
}

///|
fn Renderer::inner(self : Self) -> @lib.SDL_Renderer {
  self.base
}

///|
pub fn Renderer::setDefaultColor(self : Self) -> Unit {
  self.r = 0
  self.g = 0
  self.b = 0
  self.a = 255
}

///|
pub suberror SetDrawColorError {
  SetDrawColorError(String)
} derive(Show)

///|
/// Set the draw color for the renderer.
///
/// ## Parameters
///
/// - `color`: The color to set.
/// - `alpha`: The alpha value of the color.
pub fn Renderer::setDrawColor(
  self : Renderer,
  color : Color,
  alpha? : Int = 255,
) -> Unit raise SetDrawColorError {
  let alpha = if alpha < 0 { 0 } else if alpha > 255 { 255 } else { alpha }
  let a = alpha.to_byte()
  let (r, g, b) = color.to_rgb()
  self.r = r
  self.g = g
  self.b = b
  self.a = a
  let res = @lib.sdl_SetRenderDrawColor(self.inner(), r, g, b, a)
  if !res {
    let msg = @lib.sdl_GetError()
    raise SetDrawColorError(msg)
  }
}

///|
fn Renderer::setRGBA(
  self : Self,
  r : Byte,
  g : Byte,
  b : Byte,
  a : Byte,
) -> Unit {
  self.r = r
  self.g = g
  self.b = b
  self.a = a
  let _ = @lib.sdl_SetRenderDrawColor(self.inner(), r, g, b, a)
}

///|
pub fn Renderer::setDrawAlpha(
  self : Renderer,
  alpha : Int,
) -> Unit raise SetDrawColorError {
  let alpha = if alpha < 0 { 0 } else if alpha > 255 { 255 } else { alpha }
  self.a = alpha.to_byte()
  let { r, g, b, a, .. } = self
  let res = @lib.sdl_SetRenderDrawColor(self.inner(), r, g, b, a)
  if !res {
    let msg = @lib.sdl_GetError()
    raise SetDrawColorError(msg)
  }
}

///|
pub suberror ClearRendererError {
  ClearRendererError(String)
} derive(Show)

///|
pub fn Renderer::refreshBackGround(self : Self, color : Color) -> Unit raise {
  let { r, g, b, a, .. } = self
  self.setDrawColor(color)
  let res = @lib.sdl_RenderClear(self.inner())
  if !res {
    let msg = @lib.sdl_GetError()
    raise ClearRendererError(msg)
  }
  self.setRGBA(r, g, b, a)
}

///|
/// Clear the renderer.
pub fn Renderer::clear(self : Renderer) -> Unit raise ClearRendererError {
  let res = @lib.sdl_RenderClear(self.inner())
  if !res {
    let msg = @lib.sdl_GetError()
    raise ClearRendererError(msg)
  }
}

///|
pub suberror RenderPresentError {
  RenderPresentError(String)
} derive(Show)

///|
/// Present the renderer.
pub fn Renderer::present(self : Renderer) -> Unit raise RenderPresentError {
  let res = @lib.sdl_RenderPresent(self.inner())
  if !res {
    let msg = @lib.sdl_GetError()
    raise RenderPresentError(msg)
  }
}

///|
pub suberror RenderRendError {
  RenderRendError(String)
} derive(Show)

///|
pub fn Renderer::drawPoint(
  self : Self,
  x : Double,
  y : Double,
  color? : Color,
  alpha? : Int,
) -> Unit raise {
  let { r, g, b, a, .. } = self
  match (color, alpha) {
    (Some(c), Some(a)) => self.setDrawColor(c, alpha=a)
    (Some(c), None) => self.setDrawColor(c)
    (None, Some(a)) => self.setDrawAlpha(a)
    (None, None) => ()
  }
  match color {
    Some(c) => self.setDrawColor(c)
    None => ()
  }
  match alpha {
    Some(a) => self.setDrawAlpha(a)
    None => ()
  }
  let res = @lib.sdl_RenderPoint(
    self.inner(),
    Float::from_double(x),
    Float::from_double(y),
  )
  self.setRGBA(r, g, b, a)
  if !res {
    let msg = @lib.sdl_GetError()
    raise RenderRendError(msg)
  }
}

///|
pub fn Renderer::drawPoints(
  self : Self,
  points : Array[(Double, Double)],
  color? : Color,
  alpha? : Int,
) -> Unit raise {
  let { r, g, b, a, .. } = self
  match (color, alpha) {
    (Some(c), Some(a)) => self.setDrawColor(c, alpha=a)
    (Some(c), None) => self.setDrawColor(c)
    (None, Some(a)) => self.setDrawAlpha(a)
    (None, None) => ()
  }
  let len = points.length()
  let pts : FixedArray[Float] = FixedArray::make(len * 2, 0)
  let mut i = 0
  for p in points {
    let (x, y) = p
    pts[i] = Float::from_double(x)
    pts[i + 1] = Float::from_double(y)
    i += 2
  }
  let res = @lib.sdl_RenderPoints(self.inner(), pts, len)
  if !res {
    let msg = @lib.sdl_GetError()
    raise RenderRendError(msg)
  }
  self.setRGBA(r, g, b, a)
}

///|
pub fn Renderer::drawLine(
  self : Self,
  start : (Double, Double),
  end : (Double, Double),
  color? : Color,
  alpha? : Int,
) -> Unit raise {
  let { r, g, b, a, .. } = self
  match (color, alpha) {
    (Some(c), Some(a)) => self.setDrawColor(c, alpha=a)
    (Some(c), None) => self.setDrawColor(c)
    (None, Some(a)) => self.setDrawAlpha(a)
    (None, None) => ()
  }
  let (x1, y1) = start
  let (x2, y2) = end
  let res = @lib.sdl_RenderLine(
    self.inner(),
    Float::from_double(x1),
    Float::from_double(y1),
    Float::from_double(x2),
    Float::from_double(y2),
  )
  self.setRGBA(r, g, b, a)
  if !res {
    let msg = @lib.sdl_GetError()
    raise RenderRendError(msg)
  }
}

///|
pub fn Renderer::drawLines(
  self : Self,
  points : Array[(Double, Double)],
  color? : Color,
  alpha? : Int,
) -> Unit raise {
  let { r, g, b, a, .. } = self
  match (color, alpha) {
    (Some(c), Some(a)) => self.setDrawColor(c, alpha=a)
    (Some(c), None) => self.setDrawColor(c)
    (None, Some(a)) => self.setDrawAlpha(a)
    (None, None) => ()
  }
  let len = points.length()
  let pts : FixedArray[Float] = FixedArray::make(len * 2, 0)
  let mut i = 0
  for p in points {
    let (x, y) = p
    pts[i] = Float::from_double(x)
    pts[i + 1] = Float::from_double(y)
    i += 2
  }
  let res = @lib.sdl_RenderLines(self.inner(), pts, len)
  if !res {
    let msg = @lib.sdl_GetError()
    raise RenderRendError(msg)
  }
  self.setRGBA(r, g, b, a)
}

///|
pub fn Renderer::drawRect(
  self : Self,
  anchor : (Double, Double),
  width : Double,
  height : Double,
  color? : Color,
  alpha? : Int,
  fill? : Bool = false,
) -> Unit raise {
  let { r, g, b, a, .. } = self
  match (color, alpha) {
    (Some(c), Some(a)) => self.setDrawColor(c, alpha=a)
    (Some(c), None) => self.setDrawColor(c)
    (None, Some(a)) => self.setDrawAlpha(a)
    (None, None) => ()
  }
  let rect = @lib.SDL_FRect::{
    x: Float::from_double(anchor.0),
    y: Float::from_double(anchor.1),
    w: Float::from_double(width),
    h: Float::from_double(height),
  }
  let res = if fill {
    @lib.sdl_RenderFillRect(self.inner(), rect)
  } else {
    @lib.sdl_RenderRect(self.inner(), rect)
  }
  if !res {
    let msg = @lib.sdl_GetError()
    raise RenderRendError(msg)
  }
  self.setRGBA(r, g, b, a)
}

///|
pub fn Renderer::drawRects(
  self : Self,
  rects : Array[((Double, Double), Double, Double)],
  color? : Color,
  alpha? : Int,
  fill? : Bool = false,
) -> Unit raise {
  let { r, g, b, a, .. } = self
  match (color, alpha) {
    (Some(c), Some(a)) => self.setDrawColor(c, alpha=a)
    (Some(c), None) => self.setDrawColor(c)
    (None, Some(a)) => self.setDrawAlpha(a)
    (None, None) => ()
  }
  let len = rects.length()
  let sdl_rects : FixedArray[Float] = FixedArray::make(len * 4, 0)
  let mut i = 0
  for r in rects {
    let ((x, y), w, h) = r
    sdl_rects[i] = Float::from_double(x)
    sdl_rects[i + 1] = Float::from_double(y)
    sdl_rects[i + 2] = Float::from_double(w)
    sdl_rects[i + 3] = Float::from_double(h)
    i += 4
  }
  let res = if fill {
    @lib.sdl_RenderFillRects(self.inner(), sdl_rects, len)
  } else {
    @lib.sdl_RenderRects(self.inner(), sdl_rects, len)
  }
  if !res {
    let msg = @lib.sdl_GetError()
    raise RenderRendError(msg)
  }
  self.setRGBA(r, g, b, a)
}

///|
pub suberror DrawGeometryError {
  DrawGeometryError(String)
} derive(Show)

///|
pub fn Renderer::drawGeometry(
  self : Self,
  vertexes : Array[(Double, Double)],
  color_alpha? : Array[(Color, Byte)] = [],
  texture_coords? : Array[(Double, Double)] = [],
  indices? : Array[Int] = [],
) -> Unit raise {
  if vertexes.length() < 3 {
    raise DrawGeometryError(
      "At least 3 vertexes are required to draw a geometry.",
    )
  }
  if not(color_alpha.length() is (0 | 1)) &&
    color_alpha.length() != vertexes.length() {
    raise DrawGeometryError(
      "color_alpha length must be 0, 1, or equal to vertexes length.",
    )
  }
  if not(texture_coords.length() is (0 | 1)) &&
    texture_coords.length() != vertexes.length() {
    raise DrawGeometryError(
      "texture_coords length must be 0, 1, or equal to vertexes length.",
    )
  }
  if indices.length() != 0 && indices.length() != vertexes.length() {
    raise DrawGeometryError(
      "indices length must be 0, or equal to vertexes length.",
    )
  }
  if vertexes.length() % 3 != 0 {
    raise DrawGeometryError("vertexes length must be a multiple of 3.")
  }

  let len_of_vertexes = vertexes.length()
  let sdl_vertexes_data : FixedArray[Float] = FixedArray::make(
    len_of_vertexes * 8,
    0.0,
  )
  let mut idx = 0
  for i, v in vertexes {
    let (x, y) = v
    sdl_vertexes_data[idx] = Float::from_double(x)
    sdl_vertexes_data[idx + 1] = Float::from_double(y)
    let (r, g, b, a) = match color_alpha {
      [] => {
        let { r, g, b, a, .. } = self
        (
          Float::from_byte(r),
          Float::from_byte(g),
          Float::from_byte(b),
          Float::from_byte(a),
        )
      }
      [c] => {
        let (color, a) = c
        let (r, g, b) = color.to_rgb()
        (
          Float::from_byte(r),
          Float::from_byte(g),
          Float::from_byte(b),
          Float::from_byte(a),
        )
      }
      _ => {
        let (color, a) = color_alpha[i]
        let (r, g, b) = color.to_rgb()
        (
          Float::from_byte(r),
          Float::from_byte(g),
          Float::from_byte(b),
          Float::from_byte(a),
        )
      }
    }
    sdl_vertexes_data[idx + 2] = r
    sdl_vertexes_data[idx + 3] = g
    sdl_vertexes_data[idx + 4] = b
    sdl_vertexes_data[idx + 5] = a
    let (tx, ty) = match texture_coords {
      [] => (0.0, 0.0)
      [c] => c
      _ => texture_coords[i]
    }
    sdl_vertexes_data[idx + 6] = Float::from_double(tx)
    sdl_vertexes_data[idx + 7] = Float::from_double(ty)
    idx += 8
  }

  let indices_data : FixedArray[Int] = if indices.length() == 0 {
    @lib.nullptr()
  } else {
    FixedArray::from_array(indices)
  }
  let indices_len = indices.length()
  let texture : @lib.SDL_Texture = @lib.nullptr() // No texture used

  let res = @lib.sdl_RenderGeometry(
    self.inner(),
    texture,
    sdl_vertexes_data,
    len_of_vertexes,
    indices_data,
    indices_len,
  )

  if !res {
    let msg = @lib.sdl_GetError()
    raise DrawGeometryError(msg)
  }
}

//pub fn Renderer::drawTriangle(
//  self: Self,
//  v1: (Double, Double),
//  v2: (Double, Double),
//  v3: (Double, Double),
//  color?: Color, alpha?: Int,
//  fill~: Bool = false
//) -> Unit raise {
//  if !fill {
//    self.drawLines([v1, v2, v3, v1], color?=color)
//    return
//  }
//  let vertexes: Array[(Double, Double)] = Array::new()
//  vertexes..push(v1) .. push(v2) .. push(v3)
//  let color = match color {
//    Some(c) => c
//    None => Color::RGB(self.r, self.g, self.b)
//  }
//  let alpha = match alpha {
//    Some(a) => if a < 0 { 0 } else if a > 255 { 255 } else { a }
//    None => self.a.to_int()
//  }
//  let alpha = alpha.to_byte()
//  let color_alpha = [(color, alpha)]
//  self.drawGeometry(
//    vertexes,
//    color_alpha=color_alpha
//  )
//}

//// draw a circle, use drawGeometry
//pub fn Renderer::drawCircle(
//  self: Self,
//  center: (Double, Double),
//  radius: Double,
//  color?: Color, alpha?: Int, fill~: Bool = false
//) -> Unit raise {
//  let segments = match radius.to_int() {
//    _..<10 => 8 // For small circles, use fewer segments
//    10..<20 => 16 // For medium circles, use more segments
//    20..=50 => 32 // For larger circles, use even more segments
//    50..<100 => 64 // For very large circles, use the maximum segments
//    100..<200 => 128 // For extremely large circles, use even more segments
//    200..<500 => 256 // For huge circles, use a very high number of segments
//    500..<_ => 512
//  }
//  // if not fill, use draw lines
//  if !fill {
//    let angle_step = (2.0 * @math.PI) / segments.to_double()
//    let vertexes: Array[(Double, Double)] = Array::new()
//    for i in 0.. c
//    None => Color::RGB(self.r, self.g, self.b)
//  }
//  let alpha = match alpha {
//    Some(a) => if a < 0 { 0 } else if a > 255 { 255 } else { a }
//    None => self.a.to_int()
//  }
//  let alpha = alpha.to_byte()
//  let color_alpha = [(color, alpha)]
//
//  self.drawGeometry(vertexes, color_alpha=color_alpha)
//}