// Copyright 2026 International Digital Economy Academy
//
// 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.

///|
struct TupleHeader {
  data_size : Int
  tuple_index : Int
  peak : Array[Int]
  start : Array[Int]?
  end : Array[Int]?
} derive(Show, ToJson)

///|
fn TupleHeader::has_private_points(self : TupleHeader) -> Bool {
  (self.tuple_index & 0x2000) != 0
}

///|
pub struct TupleVariation {
  peak : Array[Int]
  start : Array[Int]?
  end : Array[Int]?
  indices : Array[Int]?
  deltas_x : Array[Int]
  deltas_y : Array[Int]?
} derive(Show, ToJson)

///|
fn coord_at(coords : Array[Int], index : Int) -> Int {
  if index < 0 || index >= coords.length() { 0 } else { coords[index] }
}

///|
pub fn TupleVariation::scalar(self : TupleVariation, coords : Array[Int]) -> Double {
  let axis_count = self.peak.length()
  let mut scalar = 1.0
  let has_interm = self.start is Some(_) && self.end is Some(_)
  let start_tuple = match self.start {
    None => []
    Some(value) => value
  }
  let end_tuple = match self.end {
    None => []
    Some(value) => value
  }
  for i in 0.. peak || peak > end || (start < 0 && end > 0 && peak != 0) {
        continue
      }
      if v < start || v > end {
        return 0.0
      }
      if v < peak {
        if peak != start {
          scalar = scalar * (v - start).to_double() / (peak - start).to_double()
        }
      } else if peak != end {
        scalar = scalar * (end - v).to_double() / (end - peak).to_double()
      }
    } else {
      let minv = if peak < 0 { peak } else { 0 }
      let maxv = if peak > 0 { peak } else { 0 }
      if v < minv || v > maxv {
        return 0.0
      }
      scalar = scalar * v.to_double() / peak.to_double()
    }
  }
  scalar
}

///|
pub struct TupleVariationData {
  axis_count : Int
  tuple_headers : Array[TupleHeader]
  shared_points : Array[Int]?
  shared_points_size : Int
  data_offset : Int
  data : Bytes
} derive(Show, ToJson)

///|
fn clone_int_array(values : Array[Int]) -> Array[Int] {
  let out : Array[Int] = []
  for v in values {
    out.push(v)
  }
  out
}

///|
fn read_f2dot14_array(
  data : BytesView,
  offset : Int,
  axis_count : Int,
) -> Result[(Array[Int], Int), VarError] {
  let values : Array[Int] = []
  let mut pos = offset
  for _ in 0.. return Err(err)
      Ok(v) => values.push(v)
    }
    pos = pos + 2
  }
  Ok((values, pos))
}

///|
fn slice_shared_tuple(
  shared_tuples : Array[Int],
  index : Int,
  axis_count : Int,
) -> Result[Array[Int], VarError] {
  let start = index * axis_count
  if start < 0 || start + axis_count > shared_tuples.length() {
    return Err(InvalidFormat)
  }
  let out : Array[Int] = []
  for i in 0.. Result[(TupleHeader, Int), VarError] {
  let data_size = read_u16_int(data, offset)
  let tuple_index = read_u16_int(data, offset + 2)
  match (data_size, tuple_index) {
    (Err(err), _) => Err(err)
    (_, Err(err)) => Err(err)
    (Ok(data_size), Ok(tuple_index)) => {
      let mut pos = offset + 4
      let has_peak = (tuple_index & 0x8000) != 0
      let has_intermediate = (tuple_index & 0x4000) != 0
      let peak = if has_peak {
        match read_f2dot14_array(data, pos, axis_count) {
          Err(err) => return Err(err)
          Ok((values, next)) => {
            pos = next
            values
          }
        }
      } else {
        let index = tuple_index & 0x0FFF
        match slice_shared_tuple(shared_tuples, index, axis_count) {
          Err(err) => return Err(err)
          Ok(values) => values
        }
      }
      let start_tuple = if has_intermediate {
        match read_f2dot14_array(data, pos, axis_count) {
          Err(err) => return Err(err)
          Ok((values, next)) => {
            pos = next
            Some(values)
          }
        }
      } else {
        None
      }
      let end_tuple = if has_intermediate {
        match read_f2dot14_array(data, pos, axis_count) {
          Err(err) => return Err(err)
          Ok((values, next)) => {
            pos = next
            Some(values)
          }
        }
      } else {
        None
      }
      Ok((TupleHeader::{ data_size, tuple_index, peak, start: start_tuple, end: end_tuple }, pos))
    }
  }
}

///|
fn decode_point_numbers(
  data : BytesView,
  offset : Int,
  end : Int,
) -> Result[(Array[Int], Int), VarError] {
  if offset < 0 || offset >= end {
    return Err(UnexpectedEof)
  }
  let mut pos = offset
  let mut count = data[pos].to_int()
  pos = pos + 1
  if (count & 0x80) != 0 {
    if pos >= end {
      return Err(UnexpectedEof)
    }
    count = ((count & 0x7F) << 8) | data[pos].to_int()
    pos = pos + 1
  }
  let points : Array[Int] = []
  let mut n = 0
  let mut i = 0
  while i < count {
    if pos >= end {
      return Err(UnexpectedEof)
    }
    let control = data[pos].to_int()
    pos = pos + 1
    let run_count = (control & 0x7F) + 1
    let stop = i + run_count
    if stop > count {
      return Err(InvalidFormat)
    }
    if (control & 0x80) != 0 {
      let needed = run_count * 2
      if pos + needed > end {
        return Err(UnexpectedEof)
      }
      for _ in 0.. return Err(err)
          Ok(value) => {
            n = n + value
            points.push(n)
          }
        }
        pos = pos + 2
      }
    } else {
      if pos + run_count > end {
        return Err(UnexpectedEof)
      }
      for _ in 0.. Int {
  let v = value.to_int()
  if v >= 0x80 { v - 0x100 } else { v }
}

///|
fn decode_values(
  data : BytesView,
  offset : Int,
  end : Int,
  count : Int,
) -> Result[(Array[Int], Int), VarError] {
  if count < 0 {
    return Err(InvalidFormat)
  }
  if count == 0 {
    return Ok(([], offset))
  }
  if offset < 0 || offset > end {
    return Err(UnexpectedEof)
  }
  let mut pos = offset
  let values : Array[Int] = []
  let mut i = 0
  while i < count {
    if pos >= end {
      return Err(UnexpectedEof)
    }
    let control = data[pos].to_int()
    pos = pos + 1
    let run_count = (control & 0x3F) + 1
    let stop = i + run_count
    if stop > count {
      return Err(InvalidFormat)
    }
    let mode = control & 0xC0
    if mode == 0x80 {
      for _ in 0.. end {
        return Err(UnexpectedEof)
      }
      for _ in 0.. return Err(err)
          Ok(v) => values.push(v)
        }
        pos = pos + 2
      }
    } else if mode == 0xC0 {
      let needed = run_count * 4
      if pos + needed > end {
        return Err(UnexpectedEof)
      }
      for _ in 0.. return Err(err)
          Ok(v) => values.push(v)
        }
        pos = pos + 4
      }
    } else {
      if pos + run_count > end {
        return Err(UnexpectedEof)
      }
      for _ in 0.. Result[TupleVariationData, VarError] {
  let tuple_var_count = read_u16_int(data, 0)
  let data_offset = read_u16_int(data, 2)
  match (tuple_var_count, data_offset) {
    (Err(err), _) => Err(err)
    (_, Err(err)) => Err(err)
    (Ok(tuple_var_count), Ok(data_offset)) => {
      let tuple_count = tuple_var_count & 0x0FFF
      let has_shared_points = (tuple_var_count & 0x8000) != 0
      if data_offset < 0 || data_offset > data.length() {
        return Err(UnexpectedEof)
      }
      let mut pos = 4
      let tuple_headers : Array[TupleHeader] = []
      for _ in 0.. return Err(err)
          Ok((header, next_pos)) => {
            tuple_headers.push(header)
            pos = next_pos
          }
        }
      }
      let mut shared_points : Array[Int]? = None
      let mut shared_points_size = 0
      if has_shared_points {
        let decoded = decode_point_numbers(data, data_offset, data.length())
        match decoded {
          Err(err) => return Err(err)
          Ok((points, next_pos)) => {
            shared_points = Some(points)
            shared_points_size = next_pos - data_offset
          }
        }
      }
      Ok(TupleVariationData::{
        axis_count,
        tuple_headers,
        shared_points,
        shared_points_size,
        data_offset,
        data: data.to_bytes(),
      })
    }
  }
}

///|
pub fn TupleVariationData::decode(
  self : TupleVariationData,
  point_count : Int,
  is_gvar : Bool,
) -> Result[Array[TupleVariation], VarError] {
  if point_count < 0 {
    return Err(InvalidFormat)
  }
  let data = self.data[:]
  let mut pos = self.data_offset + self.shared_points_size
  if pos < 0 || pos > data.length() {
    return Err(UnexpectedEof)
  }
  let tuples : Array[TupleVariation] = []
  for header in self.tuple_headers {
    let end = pos + header.data_size
    if end < pos || end > data.length() {
      return Err(UnexpectedEof)
    }
    let mut points : Array[Int] = []
    if header.has_private_points() {
      let decoded = decode_point_numbers(data, pos, end)
      match decoded {
        Err(err) => return Err(err)
        Ok((values, next_pos)) => {
          points = values
          pos = next_pos
        }
      }
    } else {
      points = match self.shared_points {
        None => []
        Some(values) => clone_int_array(values)
      }
    }
    let apply_to_all = points.length() == 0
    let num_deltas = if apply_to_all { point_count } else { points.length() }
    let decoded_x = decode_values(data, pos, end, num_deltas)
    let deltas_x = match decoded_x {
      Err(err) => return Err(err)
      Ok((values, next_pos)) => {
        pos = next_pos
        values
      }
    }
    let mut deltas_y : Array[Int]? = None
    if is_gvar {
      let decoded_y = decode_values(data, pos, end, num_deltas)
      match decoded_y {
        Err(err) => return Err(err)
        Ok((values, next_pos)) => {
          pos = next_pos
          deltas_y = Some(values)
        }
      }
    }
    if pos > end {
      return Err(UnexpectedEof)
    }
    if pos < end {
      pos = end
    }
    let indices = if apply_to_all { None } else { Some(points) }
    tuples.push(TupleVariation::{
      peak: header.peak,
      start: header.start,
      end: header.end,
      indices,
      deltas_x,
      deltas_y,
    })
  }
  Ok(tuples)
}

///|
pub fn TupleVariationData::apply(
  self : TupleVariationData,
  coords : Array[Int],
  point_count : Int,
  is_gvar : Bool,
) -> Result[(Array[Double], Array[Double]?), VarError] {
  let tuples = match self.decode(point_count, is_gvar) {
    Err(err) => return Err(err)
    Ok(values) => values
  }
  let deltas_x : Array[Double] = []
  let deltas_y : Array[Double] = []
  for _ in 0.. ()
          Some(values) => {
            for i in 0..= point_count {
          continue
        }
        deltas_x[idx] = deltas_x[idx] + tuple.deltas_x[i].to_double() * scalar
        if is_gvar {
          match tuple.deltas_y {
            None => ()
            Some(deltas) => {
              deltas_y[idx] = deltas_y[idx] + deltas[i].to_double() * scalar
            }
          }
        }
      }
    }
  }
  let out_y = if is_gvar { Some(deltas_y) } else { None }
  Ok((deltas_x, out_y))
}