///|
/// Generic 2D triangle payload decoder.
///
/// This package is intentionally engine-agnostic so it can be extracted
/// into a standalone utility later.

///|
pub struct TrianglePayload {
  has_payload : Bool
  ax : Double
  ay : Double
  bx : Double
  by : Double
  cx : Double
  cy : Double
  au : Double
  av : Double
  bu : Double
  bv : Double
  cu : Double
  cv : Double
  uniform_r : Double
  uniform_g : Double
  uniform_b : Double
  uniform_a : Double
  texture_seed : Int
} derive(Debug)

///|
pub impl Show for TrianglePayload with output(self, logger) {
  logger.write_object(to_repr(self))
}

///|
pub fn clamp_unit(value : Double) -> Double {
  if value < 0.0 {
    0.0
  } else if value > 1.0 {
    1.0
  } else {
    value
  }
}

///|
fn decode_uniform_channel(
  uniforms : Array[Int],
  index : Int,
  fallback : Double,
) -> Double {
  if index >= 0 && index < uniforms.length() {
    let value = uniforms[index]
    if value >= 0 && value <= 255 {
      value.to_double() / 255.0
    } else {
      fallback
    }
  } else {
    fallback
  }
}

///|
fn resolve_texture_seed(src_image_ids : Array[Int]) -> Int {
  if src_image_ids.length() > 0 {
    let seed = src_image_ids[0]
    if seed < 0 {
      0
    } else {
      seed
    }
  } else {
    0
  }
}

///|
fn resolve_index_or_default(
  indices : Array[Int],
  order : Int,
  vertex_count : Int,
) -> Int {
  if vertex_count <= 0 {
    0
  } else if order >= 0 && order < indices.length() {
    let index = indices[order]
    if index >= 0 && index < vertex_count {
      index
    } else if order < vertex_count {
      order
    } else {
      0
    }
  } else if order < vertex_count {
    order
  } else {
    0
  }
}

///|
pub fn default_triangle_payload() -> TrianglePayload {
  {
    has_payload: false,
    ax: 0.0,
    ay: 0.5,
    bx: -0.5,
    by: -0.5,
    cx: 0.5,
    cy: -0.5,
    au: 0.0,
    av: 0.0,
    bu: 1.0,
    bv: 0.0,
    cu: 1.0,
    cv: 1.0,
    uniform_r: 1.0,
    uniform_g: 1.0,
    uniform_b: 1.0,
    uniform_a: 1.0,
    texture_seed: 0,
  }
}

///|
pub fn decode_triangle_payload(
  vertex_data : Array[Double],
  indices : Array[Int],
  uniform_dwords : Array[Int],
  src_image_ids : Array[Int],
) -> TrianglePayload {
  let payload_len = vertex_data.length()
  if payload_len < 6 {
    default_triangle_payload()
  } else {
    let mut ax = 0.0
    let mut ay = 0.5
    let mut bx = -0.5
    let mut by = -0.5
    let mut cx = 0.5
    let mut cy = -0.5
    let mut au = 0.0
    let mut av = 0.0
    let mut bu = 1.0
    let mut bv = 0.0
    let mut cu = 1.0
    let mut cv = 1.0

    if payload_len >= 12 {
      let vertex_count = payload_len / 4
      let i0 = resolve_index_or_default(indices, 0, vertex_count)
      let i1 = resolve_index_or_default(indices, 1, vertex_count)
      let i2 = resolve_index_or_default(indices, 2, vertex_count)

      let i0_base = i0 * 4
      let i1_base = i1 * 4
      let i2_base = i2 * 4
      ax = vertex_data[i0_base]
      ay = vertex_data[i0_base + 1]
      bx = vertex_data[i1_base]
      by = vertex_data[i1_base + 1]
      cx = vertex_data[i2_base]
      cy = vertex_data[i2_base + 1]
      au = vertex_data[i0_base + 2]
      av = vertex_data[i0_base + 3]
      bu = vertex_data[i1_base + 2]
      bv = vertex_data[i1_base + 3]
      cu = vertex_data[i2_base + 2]
      cv = vertex_data[i2_base + 3]
    } else {
      let vertex_count = payload_len / 2
      let i0 = resolve_index_or_default(indices, 0, vertex_count)
      let i1 = resolve_index_or_default(indices, 1, vertex_count)
      let i2 = resolve_index_or_default(indices, 2, vertex_count)
      let i0_base = i0 * 2
      let i1_base = i1 * 2
      let i2_base = i2 * 2
      ax = vertex_data[i0_base]
      ay = vertex_data[i0_base + 1]
      bx = vertex_data[i1_base]
      by = vertex_data[i1_base + 1]
      cx = vertex_data[i2_base]
      cy = vertex_data[i2_base + 1]
    }

    {
      has_payload: true,
      ax,
      ay,
      bx,
      by,
      cx,
      cy,
      au,
      av,
      bu,
      bv,
      cu,
      cv,
      uniform_r: clamp_unit(decode_uniform_channel(uniform_dwords, 0, 1.0)),
      uniform_g: clamp_unit(decode_uniform_channel(uniform_dwords, 1, 1.0)),
      uniform_b: clamp_unit(decode_uniform_channel(uniform_dwords, 2, 1.0)),
      uniform_a: clamp_unit(decode_uniform_channel(uniform_dwords, 3, 1.0)),
      texture_seed: resolve_texture_seed(src_image_ids),
    }
  }
}