// Licensing scope: see ../../LICENSE and this directory's LICENSE and NOTICE.
// Potentially incorporated Binaryen expression retains Apache-2.0 terms.
// Original Starshine contributions use the scoped MIT grant in ../../LICENSE.
// Modified for Starshine through 2026: implementation and validation use
// Starshine's representations and APIs; this is not an unchanged upstream file.
// Conservative notice retention does not establish copying or independence.

///|
priv struct SimplifyLocalsValueOrderMinimums {
  mut owner : @ir.HotFunc?
  mut revision : Int32
  states : Array[Int32]
  minimums : Array[Int32]
}

///|
fn SimplifyLocalsValueOrderMinimums::new() -> SimplifyLocalsValueOrderMinimums {
  { owner: None, revision: -1, states: [], minimums: [], }
}

///|
fn SimplifyLocalsValueOrderMinimums::minimum(
  self : SimplifyLocalsValueOrderMinimums,
  func : @ir.HotFunc,
  node : Int32,
  full_queries~ : Array[Int32]? = None,
) -> Int32 {
  let count = @ir.hot_node_count(func)
  let revision = @ir.hot_revision_current(func)
  let current = match self.owner {
    Some(owner) =>
      physical_equal(owner, func) &&
      self.revision == revision &&
      self.states.length() >= count
    None => false
  }
  if !current {
    for index in 0..= count || !@ir.hot_node_is_live(func, node) {
    return 0x7fffffff
  }
  if self.states[node] != 2 {
    match full_queries {
      Some(queries) => queries[0] += 1
      None => ()
    }
  }
  self.collect(func, node)
}

///|
fn SimplifyLocalsValueOrderMinimums::region(
  self : SimplifyLocalsValueOrderMinimums,
  func : @ir.HotFunc,
  region : @ir.HotRegionRef,
) -> Int32 {
  let mut minimum = 0x7fffffff
  for index in 0..<@ir.hot_region_root_count(func, region) {
    minimum = Int32::min(
      minimum,
      self.collect(func, @ir.hot_region_get(func, region, index)),
    )
  }
  minimum
}

///|
fn SimplifyLocalsValueOrderMinimums::collect(
  self : SimplifyLocalsValueOrderMinimums,
  func : @ir.HotFunc,
  node : Int32,
) -> Int32 {
  if node < 0 ||
    node >= self.states.length() ||
    !@ir.hot_node_is_live(func, node) {
    return 0x7fffffff
  }
  if self.states[node] == 2 {
    return self.minimums[node]
  }
  // Valid HOT is a DAG. A temporary cycle is unknown and must keep the safety
  // barrier, rather than making a cyclic dependency appear absent.
  if self.states[node] == 1 {
    return 0
  }
  self.states[node] = 1
  let mut minimum = if @ir.hot_node_result_arity(func, node) > 0 {
    node
  } else {
    0x7fffffff
  }
  for slot in 0..<@ir.hot_child_count(func, node) {
    if !simplify_locals_is_region_child_slot(func, node, slot) {
      minimum = Int32::min(
        minimum,
        self.collect(func, @ir.hot_child_get(func, node, slot)),
      )
    }
  }
  match @ir.hot_node_op(func, node) {
    @ir.Block | @ir.Loop =>
      minimum = Int32::min(
        minimum,
        self.region(func, @ir.hot_region_body_ref(func, node)),
      )
    @ir.If => {
      minimum = Int32::min(
        minimum,
        self.region(func, @ir.hot_region_then_ref(func, node)),
      )
      match @ir.hot_region_else_ref(func, node) {
        Some(region) => minimum = Int32::min(minimum, self.region(func, region))
        None => ()
      }
    }
    @ir.Try => {
      minimum = Int32::min(
        minimum,
        self.region(func, @ir.hot_region_body_ref(func, node)),
      )
      match @ir.hot_region_catch_ref(func, node) {
        Some(region) => minimum = Int32::min(minimum, self.region(func, region))
        None => ()
      }
    }
    @ir.TryTable => {
      minimum = Int32::min(
        minimum,
        self.region(func, @ir.hot_region_body_ref(func, node)),
      )
      minimum = Int32::min(
        minimum,
        self.region(func, @ir.hot_region_catch_list_ref(func, node)),
      )
    }
    _ => ()
  }
  self.minimums[node] = minimum
  self.states[node] = 2
  minimum
}

///|
fn simplify_locals_contains_value_before_with_minimums(
  func : @ir.HotFunc,
  node : Int32,
  before : Int32,
  minimums : SimplifyLocalsValueOrderMinimums?,
  seen_rows~ : Array[Int32]? = None,
) -> Bool {
  match minimums {
    Some(minimums) => minimums.minimum(func, node) < before
    None => {
      match seen_rows {
        Some(rows) => rows[0] += 1
        None => ()
      }
      simplify_locals_node_contains_value_before(
        func,
        node,
        before,
        Array::make(@ir.hot_node_count(func), false),
      )
    }
  }
}

///|
fn simplify_locals_value_order_minimums_for_region(
  sinkables : SimplifyLocalsSinkables,
  func : @ir.HotFunc,
  region : @ir.HotRegionRef,
) -> SimplifyLocalsValueOrderMinimums? {
  if @ir.hot_region_root_count(func, region) < 16 {
    return None
  }
  match sinkables.value_order_minimums {
    Some(minimums) => Some(minimums)
    None => {
      let minimums = SimplifyLocalsValueOrderMinimums::new()
      sinkables.value_order_minimums = Some(minimums)
      Some(minimums)
    }
  }
}