// 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.

///|
priv struct OrderEdge {
  target : Int
  width : Int
}

///|
pub struct OverflowRecord {
  parent : Int
  child : Int
  offset : Int
  min : Int
  max : Int
  width : Int
  is_signed : Bool
} derive(Eq, Show)

let inf_distance : Int64 = Int64::from_int(1) << 60
let space_stride : Int64 = Int64::from_int(1) << 32

fn edge_weight(width : Int, child_size : Int) -> Int64 {
  let size = Int64::from_int(child_size)
  if width <= 0 {
    return size
  }
  let limit = Int64::from_int(1) << (width * 8)
  size + limit
}

fn collect_order_edges(
  objects : ArrayView[SerializeObject],
) -> Result[(Array[Array[OrderEdge]], Array[Int]), SerializeError] {
  let count = objects.length()
  let edges : Array[Array[OrderEdge]] = []
  let indegree : Array[Int] = Array::make(count, 0)
  for _ in 0..= count {
        return Err(SerializeError::InvalidLink)
      }
      edges[i].push(OrderEdge::{ target, width: 0 })
      indegree[target] = indegree[target] + 1
    }
    for link in obj.real_links {
      if link.objidx < 0 || link.objidx >= count {
        return Err(SerializeError::InvalidLink)
      }
      if link.width == 0 {
        edges[i].push(OrderEdge::{ target: link.objidx, width: 0 })
        indegree[link.objidx] = indegree[link.objidx] + 1
        continue
      }
      if link.is_signed {
        continue
      }
      if link.whence == SerializeWhence::Absolute {
        continue
      }
      edges[i].push(OrderEdge::{ target: link.objidx, width: link.width })
      indegree[link.objidx] = indegree[link.objidx] + 1
    }
  }
  Ok((edges, indegree))
}

fn assign_spaces(edges : Array[Array[OrderEdge]]) -> Array[Int] {
  let count = edges.length()
  let spaces : Array[Int] = Array::make(count, 0)
  let reachable : Array[Bool] = Array::make(count, false)
  let queue : Array[Int] = []
  for i in 0..= 4 {
        queue.push(edge.target)
      }
    }
  }
  let mut index = 0
  while index < queue.length() {
    let node = queue[index]
    index = index + 1
    if reachable[node] {
      continue
    }
    reachable[node] = true
    for edge in edges[node] {
      if !reachable[edge.target] {
        queue.push(edge.target)
      }
    }
  }
  if queue.length() == 0 {
    return spaces
  }
  let undirected : Array[Array[Int]] = []
  for _ in 0.. Array[Int64] {
  let count = objects.length()
  let dist : Array[Int64] = Array::make(count, inf_distance)
  let visited : Array[Bool] = Array::make(count, false)
  for i in 0.. Result[Array[Int], SerializeError] {
  let count = edges.length()
  let indeg = indegree.copy()
  let ready : Array[Int] = []
  for i in 0.. 0 {
    let mut best_idx = 0
    let mut best_node = ready[0]
    for i in 1.. Result[Array[Int], SerializeError] {
  let count = objects.length()
  if order.length() != count {
    return Err(SerializeError::InvalidLink)
  }
  let starts : Array[Int] = Array::make(count, 0)
  let seen : Array[Bool] = Array::make(count, false)
  let mut offset = 0
  for idx in order {
    if idx < 0 || idx >= count {
      return Err(SerializeError::InvalidLink)
    }
    if seen[idx] {
      return Err(SerializeError::InvalidLink)
    }
    seen[idx] = true
    starts[idx] = offset
    offset = offset + objects[idx].data.length()
  }
  Ok(starts)
}

fn collect_overflows(
  objects : ArrayView[SerializeObject],
  order : ArrayView[Int],
) -> Result[Array[OverflowRecord], SerializeError] {
  let count = objects.length()
  let starts = match compute_starts(objects, order) {
    Ok(value) => value
    Err(err) => return Err(err)
  }
  let overflows : Array[OverflowRecord] = []
  for i in 0..= count {
        return Err(SerializeError::InvalidLink)
      }
      if link.position < 0 || link.position + link.width > obj.data.length() {
        return Err(SerializeError::OutOfBounds)
      }
      let target_start = starts[link.objidx]
      let origin = match link.whence {
        SerializeWhence::Head => starts[i]
        SerializeWhence::Tail => starts[i] + obj.data.length()
        SerializeWhence::Absolute => 0
      }
      let offset = target_start + link.bias - origin
      if link.is_signed {
        let limits = match signed_limits(link.width) {
          None => return Err(SerializeError::InvalidWidth)
          Some((min, max)) => (min, max)
        }
        if offset < limits.0 || offset > limits.1 {
          overflows.push(OverflowRecord::{
            parent: i,
            child: link.objidx,
            offset,
            min: limits.0,
            max: limits.1,
            width: link.width,
            is_signed: true,
          })
        }
      } else {
        let max_value = match max_value_for_width(link.width) {
          None => return Err(SerializeError::InvalidWidth)
          Some(value) => value
        }
        let max_int = max_value.reinterpret_as_int()
        if offset < 0 || offset > max_int {
          overflows.push(OverflowRecord::{
            parent: i,
            child: link.objidx,
            offset,
            min: 0,
            max: max_int,
            width: link.width,
            is_signed: false,
          })
        }
      }
    }
  }
  Ok(overflows)
}

fn priority_delta(width : Int, child_size : Int) -> Int64 {
  if width <= 0 {
    return Int64::from_int(1)
  }
  edge_weight(width, child_size) + Int64::from_int(1)
}

///|
pub fn repack_object_order(
  objects : ArrayView[SerializeObject],
  max_iterations? : Int = 32,
) -> Result[Array[Int], SerializeError] {
  let count = objects.length()
  if count == 0 {
    return Ok([])
  }
  let (edges, indegree) = match collect_order_edges(objects) {
    Ok(value) => value
    Err(err) => return Err(err)
  }
  let spaces = assign_spaces(edges)
  let priorities : Array[Int64] = Array::make(count, Int64::from_int(0))
  let mut iter = 0
  while iter < max_iterations {
    let base_dist = compute_shortest_dist(objects, edges, indegree)
    let scores : Array[Int64] = Array::make(count, Int64::from_int(0))
    for i in 0.. value
      Err(err) => return Err(err)
    }
    let overflows = match collect_overflows(objects, order[:]) {
      Ok(value) => value
      Err(err) => return Err(err)
    }
    if overflows.length() == 0 {
      return Ok(order)
    }
    for overflow in overflows {
      let child_size = objects[overflow.child].data.length()
      let delta = priority_delta(overflow.width, child_size)
      priorities[overflow.child] = priorities[overflow.child] - delta
    }
    iter = iter + 1
  }
  Err(SerializeError::OffsetOverflow)
}

///|
pub fn serialize_objects_repacked(
  objects : ArrayView[SerializeObject],
  max_iterations? : Int = 32,
) -> Result[Bytes, SerializeError] {
  let order = match repack_object_order(objects, max_iterations=max_iterations) {
    Ok(value) => value
    Err(err) => return Err(err)
  }
  serialize_objects_in_order(objects, order[:])
}

///|
pub fn serialize_objects_repacked_with_order(
  objects : ArrayView[SerializeObject],
  max_iterations? : Int = 32,
) -> Result[(Bytes, Array[Int]), SerializeError] {
  let order = match repack_object_order(objects, max_iterations=max_iterations) {
    Ok(value) => value
    Err(err) => return Err(err)
  }
  let bytes = match serialize_objects_in_order(objects, order[:]) {
    Ok(value) => value
    Err(err) => return Err(err)
  }
  Ok((bytes, order))
}