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

///|
fn wgsl_ir_emit_expression_temporary_name_key(
  origin : WgslIrEmitFunctionOrigin,
  handle : Handle,
) -> String {
  match origin {
    UserFunction(function_index) =>
      wgsl_ir_emit_name_key2("fn_expr", function_index, handle.index())
    EntryPointFunction(entry_point_index) =>
      wgsl_ir_emit_name_key2("ep_expr", entry_point_index, handle.index())
  }
}

///|
fn wgsl_ir_emit_local_name_key(
  origin : WgslIrEmitFunctionOrigin,
  handle : Handle,
) -> String {
  match origin {
    UserFunction(function_index) =>
      wgsl_ir_emit_name_key2("fn_local", function_index, handle.index())
    EntryPointFunction(entry_point_index) =>
      wgsl_ir_emit_name_key2("ep_local", entry_point_index, handle.index())
  }
}

///|
fn wgsl_ir_emit_named_expression_name_key(
  origin : WgslIrEmitFunctionOrigin,
  handle : Handle,
) -> String {
  match origin {
    UserFunction(function_index) =>
      wgsl_ir_emit_name_key2("fn_named_expr", function_index, handle.index())
    EntryPointFunction(entry_point_index) =>
      wgsl_ir_emit_name_key2("ep_named_expr", entry_point_index, handle.index())
  }
}

///|
priv struct WgslIrFunctionBodyWriterPlan {
  materialized_expressions : @set.Set[Int]
  temporary_initializer_expressions : @set.Set[Int]
  elided_local_alias_targets : Map[Int, Handle]
  local_variable_declarations : Array[Int]
  has_body_after_hoisted_declarations : Bool
  implicit_expression_slots_before : Array[Int]
  body_emission : BodyEmissionPlanner
}

///|
priv struct WgslIrTemporaryIndexTrace {
  skipped : Int
  base_index : Int
  adjusted_base_index : Int
  implicit_expression_slots_before : Int
  prior_call_projection_argument_slots : Int
  implicit_load_slots : Int
  reused_resource_operand_slots : Int
  projected_temporary_slots : Int
  final_index : Int
}

///|
fn WgslIrFunctionBodyWriterPlan::from_function(
  shader_module : Module,
  function : Function,
  projection_aliases : Bool,
) -> WgslIrFunctionBodyWriterPlan {
  let materialized_expressions = wgsl_ir_function_materialized_expression_indices(
    function,
  )
  let elided_local_alias_targets = wgsl_ir_writer_function_elided_local_alias_targets(
    shader_module, function, materialized_expressions, projection_aliases,
  )
  {
    materialized_expressions,
    temporary_initializer_expressions: wgsl_ir_writer_function_temporary_initializer_expression_indices(
      function,
    ),
    elided_local_alias_targets,
    local_variable_declarations: wgsl_ir_writer_function_body_local_declarations(
      function, elided_local_alias_targets,
    ),
    has_body_after_hoisted_declarations: wgsl_ir_writer_function_body_has_non_hoisted_statement(
      function, elided_local_alias_targets,
    ),
    implicit_expression_slots_before: wgsl_ir_writer_function_implicit_expression_slots_before(
      shader_module, function,
    ),
    body_emission: BodyEmissionPlanner::from_function(
      elided_local_alias_targets,
    ),
  }
}

///|
fn WgslIrFunctionBodyWriterPlan::implicit_expression_slot_offset(
  self : WgslIrFunctionBodyWriterPlan,
  handle : Handle,
) -> Int {
  match self.implicit_expression_slots_before.get(handle.index()) {
    Some(value) => value
    None => 0
  }
}

///|
fn WgslIrFunctionBodyWriterPlan::local_declaration_order(
  self : WgslIrFunctionBodyWriterPlan,
) -> Array[Int] {
  self.local_variable_declarations
}

///|
fn WgslIrFunctionBodyWriterPlan::needs_blank_after_local_declarations(
  self : WgslIrFunctionBodyWriterPlan,
) -> Bool {
  self.local_variable_declarations.length() > 0 &&
  self.has_body_after_hoisted_declarations
}

///|
fn WgslIrFunctionBodyWriterPlan::statement_plan(
  self : WgslIrFunctionBodyWriterPlan,
  function : Function,
  block : Block,
) -> Array[WgslIrStatementWriterPlanItem] {
  self.body_emission.statement_plan(function, block)
}

///|
fn wgsl_ir_writer_function_body_local_declarations(
  function : Function,
  elided_local_alias_targets : Map[Int, Handle],
) -> Array[Int] {
  let order : Array[Int] = []
  let seen : @set.Set[Int] = Set([])
  wgsl_ir_writer_collect_body_local_declarations(
    function.body,
    function,
    elided_local_alias_targets,
    order,
    seen,
  )
  for index in 0.. Unit {
  for statement in block.statements {
    match statement {
      Declare(handle) => {
        let index = handle.index()
        if !seen.contains(index) &&
          wgsl_ir_writer_local_declaration_is_hoistable(
            function, elided_local_alias_targets, index,
          ) {
          seen.add(index)
          order.push(index)
        }
      }
      Block(child) =>
        wgsl_ir_writer_collect_body_local_declarations(
          child, function, elided_local_alias_targets, order, seen,
        )
      If(_, accept, reject) => {
        wgsl_ir_writer_collect_body_local_declarations(
          accept, function, elided_local_alias_targets, order, seen,
        )
        wgsl_ir_writer_collect_body_local_declarations(
          reject, function, elided_local_alias_targets, order, seen,
        )
      }
      Switch(_, cases) =>
        for case in cases {
          wgsl_ir_writer_collect_body_local_declarations(
            case.body,
            function,
            elided_local_alias_targets,
            order,
            seen,
          )
        }
      Loop(body, continuing, _) => {
        wgsl_ir_writer_collect_body_local_declarations(
          body, function, elided_local_alias_targets, order, seen,
        )
        wgsl_ir_writer_collect_body_local_declarations(
          continuing, function, elided_local_alias_targets, order, seen,
        )
      }
      _ => ()
    }
  }
}

///|
fn wgsl_ir_writer_local_declaration_is_hoistable(
  function : Function,
  elided_local_alias_targets : Map[Int, Handle],
  index : Int,
) -> Bool {
  if elided_local_alias_targets.get(index) != None {
    return false
  }
  match function.local_variables.items.get(index) {
    Some(local_var) => local_var.kind == Var && !local_var.generated_temporary
    None => false
  }
}

///|
fn wgsl_ir_writer_function_temporary_initializer_expression_indices(
  function : Function,
) -> @set.Set[Int] {
  let indices : @set.Set[Int] = Set([])
  for local_var in function.local_variables.items {
    if local_var.generated_temporary {
      match local_var.init {
        Some(init) => indices.add(init.index())
        None => ()
      }
    }
  }
  indices
}

///|
fn wgsl_ir_writer_function_body_has_non_hoisted_statement(
  function : Function,
  elided_local_alias_targets : Map[Int, Handle],
) -> Bool {
  for statement in function.body.statements {
    if !wgsl_ir_statement_is_writer_elided_local_alias_declaration(
        elided_local_alias_targets, statement,
      ) &&
      !wgsl_ir_body_statement_is_hoisted_local_var_declaration(
        function, statement,
      ) {
      return true
    }
  }
  false
}

///|
fn wgsl_ir_statement_is_writer_elided_local_alias_declaration(
  elided_local_alias_targets : Map[Int, Handle],
  statement : Statement,
) -> Bool {
  match statement {
    Declare(handle) => elided_local_alias_targets.get(handle.index()) != None
    _ => false
  }
}

///|
fn WgslIrFinalNamePlan::allocate_function_body_names_from_arena(
  self : WgslIrFinalNamePlan,
  shader_module : Module,
  origin : WgslIrEmitFunctionOrigin,
  function : Function,
  function_plan : WgslIrFunctionWriterPlan,
  namer : WgslIrWriterNamer,
  compatibility : WgslNagaCompatibilityView,
  local_spelling_mode : WgslNagaLocalSpellingMode,
) -> Unit {
  let temp_namer = WgslIrWriterNamer::WgslIrWriterNamer()
  self.allocate_function_expression_temporary_names(
    origin, function, function_plan, temp_namer,
  )
  for named in function.named_expressions {
    let key = wgsl_ir_emit_named_expression_name_key(origin, named.handle)
    if self.names.get(key) == None {
      self.names.set(
        key,
        namer.local_call(
          temp_namer,
          compatibility.spelling_for_mode(named.name, local_spelling_mode),
        ),
      )
    }
  }
  for local_index in 0..
        self.expression_alias_name(
          shader_module, origin, function, function_plan, target,
        )
      None =>
        if local_var.generated_temporary {
          match local_var.init {
            Some(init) =>
              self.names.get(
                wgsl_ir_emit_expression_temporary_name_key(origin, init),
              )
            None => None
          }
        } else {
          let raw_name = match local_var.name {
            Some(name) =>
              Some(compatibility.spelling_for_mode(name, local_spelling_mode))
            None => None
          }
          Some(namer.local_call_or(temp_namer, raw_name, "local"))
        }
    }
    match local_name {
      Some(name) => self.names.set(local_key, name)
      None => ()
    }
  }
}

///|
fn WgslIrFinalNamePlan::expression_alias_name(
  self : WgslIrFinalNamePlan,
  shader_module : Module,
  origin : WgslIrEmitFunctionOrigin,
  function : Function,
  function_plan : WgslIrFunctionWriterPlan,
  handle : Handle,
) -> String? {
  match function.expressions.items.get(handle.index()) {
    Some(FunctionArgument(index)) =>
      match origin {
        UserFunction(function_index) =>
          self.names.get(
            wgsl_ir_emit_name_key2("fn_arg", function_index, index),
          )
        EntryPointFunction(entry_point_index) =>
          self.names.get(
            wgsl_ir_emit_name_key2("ep_arg", entry_point_index, index),
          )
      }
    Some(LocalVariable(local_handle)) =>
      match function_plan.elided_local_alias_target(local_handle) {
        Some(target) =>
          self.expression_alias_name(
            shader_module, origin, function, function_plan, target,
          )
        None =>
          self.names.get(wgsl_ir_emit_local_name_key(origin, local_handle))
      }
    Some(Swizzle(_, base, components)) =>
      match
        self.expression_alias_name(
          shader_module, origin, function, function_plan, base,
        ) {
        Some(base_name) =>
          Some("\{base_name}.\{wgsl_ir_writer_swizzle_components(components)}")
        None => None
      }
    Some(Component(base, component)) =>
      match
        self.expression_alias_name(
          shader_module, origin, function, function_plan, base,
        ) {
        Some(base_name) =>
          Some("\{base_name}.\{wgsl_ir_swizzle_component_name(component)}")
        None => None
      }
    Some(AccessIndex(base, index)) =>
      match
        self.expression_alias_name(
          shader_module, origin, function, function_plan, base,
        ) {
        Some(base_name) =>
          match
            wgsl_ir_writer_access_index_alias_suffix(
              shader_module, function, base, index,
            ) {
            Some(suffix) => Some("\{base_name}\{suffix}")
            None => None
          }
        None => None
      }
    _ =>
      self.names.get(wgsl_ir_emit_expression_temporary_name_key(origin, handle))
  }
}

///|
fn wgsl_ir_writer_swizzle_components(
  components : Array[SwizzleComponent],
) -> String {
  let out = StringBuilder::new()
  for component in components {
    out.write_string(wgsl_ir_swizzle_component_name(component))
  }
  out.to_string()
}

///|
fn wgsl_ir_writer_access_index_alias_suffix(
  shader_module : Module,
  function : Function,
  base : Handle,
  index : Int,
) -> String? {
  guard wgsl_ir_writer_alias_expression_is_vector(shader_module, function, base) else {
    return None
  }
  match index {
    0 => Some(".x")
    1 => Some(".y")
    2 => Some(".z")
    3 => Some(".w")
    _ => None
  }
}

///|
fn WgslIrFinalNamePlan::reserve_function_var_local_names_from_arena(
  self : WgslIrFinalNamePlan,
  origin : WgslIrEmitFunctionOrigin,
  function : Function,
  function_plan : WgslIrFunctionWriterPlan,
  namer : WgslIrWriterNamer,
  compatibility : WgslNagaCompatibilityView,
  local_spelling_mode : WgslNagaLocalSpellingMode,
) -> Unit {
  let temp_namer = WgslIrWriterNamer::WgslIrWriterNamer()
  for local_index in function_plan.local_declaration_order() {
    let local_handle = Handle::Handle(local_index)
    let local_var = function.local_variables.items[local_index]
    if local_var.kind == Var && !local_var.generated_temporary {
      let key = wgsl_ir_emit_local_name_key(origin, local_handle)
      if self.names.get(key) == None {
        let raw_name = match local_var.name {
          Some(name) =>
            Some(compatibility.spelling_for_mode(name, local_spelling_mode))
          None => None
        }
        self.names.set(key, namer.local_call_or(temp_namer, raw_name, "local"))
      }
    }
  }
}

///|
fn WgslIrFinalNamePlan::allocate_function_expression_temporary_names(
  self : WgslIrFinalNamePlan,
  origin : WgslIrEmitFunctionOrigin,
  function : Function,
  function_plan : WgslIrFunctionWriterPlan,
  temp_namer : WgslIrWriterNamer,
) -> Unit {
  for expr_index in 0.. Map[Int, Handle] {
  let targets : Map[Int, Handle] = Map([])
  let mut changed = true
  while changed {
    changed = false
    for local_index in 0.. {
            targets.set(local_index, target)
            changed = true
          }
          None => ()
        }
      }
    }
  }
  targets
}

///|
fn wgsl_ir_writer_local_alias_target_expression(
  function : Function,
  shader_module : Module,
  materialized_expressions : @set.Set[Int],
  known_targets : Map[Int, Handle],
  projection_aliases : Bool,
  local_handle : Handle,
) -> Handle? {
  guard function.local_variables.items.get(local_handle.index())
    is Some(local_var) else {
    return None
  }
  guard local_var.kind == Let else { return None }
  guard !local_var.generated_temporary else { return None }
  guard local_var.init is Some(init) else { return None }
  wgsl_ir_writer_expression_alias_target(
    function, shader_module, materialized_expressions, known_targets, projection_aliases,
    local_handle, init,
  )
}

///|
fn wgsl_ir_writer_expression_alias_target(
  function : Function,
  shader_module : Module,
  materialized_expressions : @set.Set[Int],
  known_targets : Map[Int, Handle],
  projection_aliases : Bool,
  local_handle : Handle,
  expression_handle : Handle,
) -> Handle? {
  if materialized_expressions.contains(expression_handle.index()) {
    return Some(expression_handle)
  }
  match function.expressions.items.get(expression_handle.index()) {
    Some(Swizzle(_, base, _)) if projection_aliases =>
      if wgsl_ir_writer_local_alias_is_forwarded_by_local_declaration(
          function, local_handle,
        ) &&
        wgsl_ir_writer_expression_projection_base_has_alias_target(
          function, materialized_expressions, known_targets, base,
        ) {
        Some(expression_handle)
      } else {
        None
      }
    Some(Component(base, _)) if projection_aliases =>
      if wgsl_ir_writer_local_alias_is_forwarded_by_local_declaration(
          function, local_handle,
        ) &&
        wgsl_ir_writer_expression_projection_base_has_alias_target(
          function, materialized_expressions, known_targets, base,
        ) {
        Some(expression_handle)
      } else {
        None
      }
    Some(AccessIndex(base, index)) if projection_aliases =>
      if wgsl_ir_writer_local_alias_is_forwarded_by_local_declaration(
          function, local_handle,
        ) &&
        wgsl_ir_writer_expression_projection_base_has_alias_target(
          function, materialized_expressions, known_targets, base,
        ) &&
        wgsl_ir_writer_access_index_alias_suffix(
          shader_module, function, base, index,
        ) !=
        None {
        Some(expression_handle)
      } else {
        None
      }
    Some(FunctionArgument(_)) => Some(expression_handle)
    Some(LocalVariable(local_handle)) =>
      match known_targets.get(local_handle.index()) {
        Some(target) =>
          if projection_aliases &&
            wgsl_ir_writer_expression_is_projection(function, target) {
            None
          } else {
            Some(target)
          }
        None =>
          match function.local_variables.items.get(local_handle.index()) {
            Some(local_var) =>
              if local_var.generated_temporary {
                match local_var.init {
                  Some(init) =>
                    if materialized_expressions.contains(init.index()) {
                      Some(init)
                    } else {
                      None
                    }
                  None => None
                }
              } else {
                None
              }
            None => None
          }
      }
    _ => None
  }
}

///|
fn wgsl_ir_writer_expression_is_projection(
  function : Function,
  handle : Handle,
) -> Bool {
  match function.expressions.items.get(handle.index()) {
    Some(Swizzle(_, _, _)) | Some(Component(_, _)) | Some(AccessIndex(_, _)) =>
      true
    _ => false
  }
}

///|
fn wgsl_ir_writer_local_alias_is_forwarded_by_local_declaration(
  function : Function,
  local_handle : Handle,
) -> Bool {
  let mut reference_count = 0
  for expression in function.expressions.items {
    if expression == LocalVariable(local_handle) {
      reference_count += 1
    }
  }
  if reference_count != 1 {
    return false
  }
  for local_var in function.local_variables.items {
    match local_var.init {
      Some(init) =>
        if wgsl_ir_expression_graph_contains_local_variable(
            function, init, local_handle,
          ) {
          return true
        }
      None => ()
    }
  }
  false
}

///|
fn wgsl_ir_expression_graph_contains_local_variable(
  function : Function,
  root : Handle,
  local_handle : Handle,
) -> Bool {
  match function.expressions.items.get(root.index()) {
    Some(LocalVariable(handle)) => handle == local_handle
    Some(Load(inner))
    | Some(AddressOf(_, inner))
    | Some(Splat(_, inner))
    | Some(Swizzle(_, inner, _))
    | Some(Component(inner, _))
    | Some(AccessIndex(inner, _)) =>
      wgsl_ir_expression_graph_contains_local_variable(
        function, inner, local_handle,
      )
    Some(Access(base, index)) =>
      wgsl_ir_expression_graph_contains_local_variable(
        function, base, local_handle,
      ) ||
      wgsl_ir_expression_graph_contains_local_variable(
        function, index, local_handle,
      )
    Some(Unary(_, inner)) =>
      wgsl_ir_expression_graph_contains_local_variable(
        function, inner, local_handle,
      )
    Some(Binary(_, left, right)) =>
      wgsl_ir_expression_graph_contains_local_variable(
        function, left, local_handle,
      ) ||
      wgsl_ir_expression_graph_contains_local_variable(
        function, right, local_handle,
      )
    _ => false
  }
}

///|
fn wgsl_ir_writer_expression_projection_base_has_alias_target(
  function : Function,
  materialized_expressions : @set.Set[Int],
  known_targets : Map[Int, Handle],
  base : Handle,
) -> Bool {
  if materialized_expressions.contains(base.index()) {
    return true
  }
  match function.expressions.items.get(base.index()) {
    Some(Swizzle(_, inner, _)) =>
      wgsl_ir_writer_expression_projection_base_has_alias_target(
        function, materialized_expressions, known_targets, inner,
      )
    Some(Component(inner, _)) =>
      wgsl_ir_writer_expression_projection_base_has_alias_target(
        function, materialized_expressions, known_targets, inner,
      )
    Some(AccessIndex(inner, _)) =>
      wgsl_ir_writer_expression_projection_base_has_alias_target(
        function, materialized_expressions, known_targets, inner,
      )
    _ => false
  }
}

///|
fn wgsl_ir_writer_alias_expression_is_vector(
  shader_module : Module,
  function : Function,
  handle : Handle,
) -> Bool {
  match function.expressions.items.get(handle.index()) {
    Some(FunctionArgument(index)) =>
      match function.arguments.get(index) {
        Some(argument) =>
          wgsl_ir_writer_type_handle_is_vector(shader_module, argument.ty)
        None => false
      }
    Some(LocalVariable(local_handle)) =>
      match function.local_variables.items.get(local_handle.index()) {
        Some(local_var) =>
          wgsl_ir_writer_type_handle_is_vector(shader_module, local_var.ty)
        None => false
      }
    Some(Load(pointer)) =>
      match
        wgsl_ir_writer_expression_type_handle(shader_module, function, pointer) {
        Some(ty) =>
          wgsl_ir_writer_type_handle_is_vector(
            shader_module,
            wgsl_ir_writer_deref_pointer_type_handle(shader_module, ty),
          )
        None => false
      }
    Some(ImageLoad(image, _, _, _, _)) =>
      wgsl_ir_writer_image_load_result_is_vector(shader_module, function, image)
    Some(Swizzle(size, _, _)) => size.width() > 1
    _ => false
  }
}

///|
fn wgsl_ir_writer_expression_type_handle(
  shader_module : Module,
  function : Function,
  handle : Handle,
) -> Handle? {
  match function.expressions.items.get(handle.index()) {
    Some(FunctionArgument(index)) =>
      match function.arguments.get(index) {
        Some(argument) => Some(argument.ty)
        None => None
      }
    Some(LocalVariable(local_handle)) =>
      match function.local_variables.items.get(local_handle.index()) {
        Some(local_var) => Some(local_var.ty)
        None => None
      }
    Some(GlobalVariable(global_handle)) =>
      match shader_module.global_variables.items.get(global_handle.index()) {
        Some(global) => Some(global.ty)
        None => None
      }
    Some(Load(pointer)) =>
      match
        wgsl_ir_writer_expression_type_handle(shader_module, function, pointer) {
        Some(ty) =>
          Some(wgsl_ir_writer_deref_pointer_type_handle(shader_module, ty))
        None => None
      }
    _ => None
  }
}

///|
fn wgsl_ir_writer_type_handle_is_vector(
  shader_module : Module,
  ty : Handle,
) -> Bool {
  match shader_module.types.items.get(ty.index()) {
    Some({ inner: Vector(_, _), .. }) => true
    _ => false
  }
}

///|
fn wgsl_ir_writer_deref_pointer_type_handle(
  shader_module : Module,
  ty : Handle,
) -> Handle {
  match shader_module.types.items.get(ty.index()) {
    Some({ inner: Pointer(inner, _) | BindingArray(inner, _), .. }) => inner
    _ => ty
  }
}

///|
fn wgsl_ir_writer_image_load_result_is_vector(
  shader_module : Module,
  function : Function,
  image : Handle,
) -> Bool {
  match wgsl_ir_writer_expression_type_handle(shader_module, function, image) {
    Some(image_ty) =>
      match shader_module.types.items.get(image_ty.index()) {
        Some({ inner: Image(_, _, _), .. }) => true
        _ => false
      }
    None => false
  }
}

///|
fn wgsl_ir_function_materialized_expression_indices(
  function : Function,
) -> @set.Set[Int] {
  let indices : @set.Set[Int] = Set([])
  for local_var in function.local_variables.items {
    if local_var.generated_temporary {
      match local_var.init {
        Some(init) => indices.add(init.index())
        None => ()
      }
    }
  }
  wgsl_ir_collect_statement_materialized_expression_indices(
    function.body,
    indices,
  )
  for expr_index in 0.. indices.add(expr_index)
      _ => ()
    }
  }
  indices
}

///|
fn wgsl_ir_collect_statement_materialized_expression_indices(
  block : Block,
  indices : @set.Set[Int],
) -> Unit {
  for statement in block.statements {
    match statement {
      Block(child) =>
        wgsl_ir_collect_statement_materialized_expression_indices(
          child, indices,
        )
      If(_, accept, reject) => {
        wgsl_ir_collect_statement_materialized_expression_indices(
          accept, indices,
        )
        wgsl_ir_collect_statement_materialized_expression_indices(
          reject, indices,
        )
      }
      Switch(_, cases) =>
        for case in cases {
          wgsl_ir_collect_statement_materialized_expression_indices(
            case.body,
            indices,
          )
        }
      Loop(body, continuing, _) => {
        wgsl_ir_collect_statement_materialized_expression_indices(body, indices)
        wgsl_ir_collect_statement_materialized_expression_indices(
          continuing, indices,
        )
      }
      Call(_, _, Some(result)) | Atomic(_, _, _, _, Some(result)) =>
        indices.add(result.index())
      _ => ()
    }
  }
}

///|
fn wgsl_ir_emit_expression_temporary_index(
  function : Function,
  function_plan : WgslIrFunctionWriterPlan,
  handle : Handle,
) -> Int {
  wgsl_ir_emit_expression_temporary_index_trace(function, function_plan, handle).final_index
}

///|
fn wgsl_ir_emit_expression_temporary_index_trace(
  function : Function,
  function_plan : WgslIrFunctionWriterPlan,
  handle : Handle,
) -> WgslIrTemporaryIndexTrace {
  let mut skipped = 0
  for index in 0.. 0 {
    reused_resource_operand_slots
  } else {
    0
  }
  let final_index = adjusted_base_index +
    implicit_expression_slots_before +
    prior_call_projection_argument_slots +
    implicit_load_slots +
    reused_resource_operand_slots +
    projected_temporary_slots
  {
    skipped,
    base_index,
    adjusted_base_index,
    implicit_expression_slots_before,
    prior_call_projection_argument_slots,
    implicit_load_slots,
    reused_resource_operand_slots,
    projected_temporary_slots,
    final_index,
  }
}

///|
fn wgsl_naga_function_expression_is_synthetic_local_reference(
  function : Function,
  handle : Handle,
) -> Bool {
  match function.expressions.items.get(handle.index()) {
    Some(Load(inner)) =>
      wgsl_naga_function_expression_is_synthetic_local_reference(
        function, inner,
      )
    Some(LocalVariable(local_handle)) =>
      match function.local_variables.items.get(local_handle.index()) {
        Some(local_var) =>
          if local_var.kind != Let {
            false
          } else if local_var.generated_temporary {
            true
          } else {
            true
          }
        None => false
      }
    _ => false
  }
}

///|
fn wgsl_ir_emit_atomic_value_operand_slot_adjustment(
  function : Function,
  handle : Handle,
) -> Int {
  guard function.expressions.items.get(handle.index()) is Some(Load(_)) else {
    return 0
  }
  guard wgsl_ir_function_materialized_expression_indices(function).contains(
    handle.index(),
  ) else {
    return 0
  }
  for statement in function.body.statements {
    if wgsl_ir_statement_uses_handle_as_atomic_value(
        function, statement, handle,
      ) {
      return 1
    }
  }
  0
}

///|
fn wgsl_ir_statement_uses_handle_as_atomic_value(
  function : Function,
  statement : Statement,
  handle : Handle,
) -> Bool {
  match statement {
    Atomic(_, _, value, _, result) =>
      value == handle && result == Some(Handle(handle.index() + 1))
    Block(block) =>
      wgsl_ir_block_uses_handle_as_atomic_value(function, block, handle)
    If(_, accept, reject) =>
      wgsl_ir_block_uses_handle_as_atomic_value(function, accept, handle) ||
      wgsl_ir_block_uses_handle_as_atomic_value(function, reject, handle)
    Switch(_, cases) => {
      for case in cases {
        if wgsl_ir_block_uses_handle_as_atomic_value(
            function,
            case.body,
            handle,
          ) {
          return true
        }
      }
      false
    }
    Loop(body, continuing, _) =>
      wgsl_ir_block_uses_handle_as_atomic_value(function, body, handle) ||
      wgsl_ir_block_uses_handle_as_atomic_value(function, continuing, handle)
    _ => false
  }
}

///|
fn wgsl_ir_block_uses_handle_as_atomic_value(
  function : Function,
  block : Block,
  handle : Handle,
) -> Bool {
  for statement in block.statements {
    if wgsl_ir_statement_uses_handle_as_atomic_value(
        function, statement, handle,
      ) {
      return true
    }
  }
  false
}

///|
fn wgsl_ir_writer_function_implicit_expression_slots_before(
  shader_module : Module,
  function : Function,
) -> Array[Int] {
  let offsets : Array[Int] = []
  let mut offset = 0
  for index in 0..
        offset += wgsl_ir_writer_compose_implicit_expression_slots(
          shader_module, ty, components,
        )
      _ => ()
    }
  }
  offsets
}

///|
fn wgsl_ir_writer_compose_implicit_expression_slots(
  shader_module : Module,
  ty : Handle,
  components : Array[Handle],
) -> Int {
  match shader_module.types.items.get(ty.index()) {
    Some(type_) =>
      match type_.inner {
        Matrix(columns, rows, _) =>
          if components.length() == columns.width() * rows.width() {
            columns.width()
          } else {
            0
          }
        _ => 0
      }
    None => 0
  }
}

///|
fn wgsl_ir_function_expression_is_projection_graph_node(
  function : Function,
  handle : Handle,
) -> Bool {
  match function.expressions.items.get(handle.index()) {
    Some(Load(_))
    | Some(Access(_, _))
    | Some(AccessIndex(_, _))
    | Some(Component(_, _))
    | Some(Swizzle(_, _, _)) => true
    _ => false
  }
}

///|
fn wgsl_ir_emit_implicit_load_slot_offset(
  function : Function,
  function_plan : WgslIrFunctionWriterPlan,
  limit : Handle,
) -> Int {
  let mut offset = 0
  for index in 0..<=limit.index() {
    let handle = Handle::Handle(index)
    if function_plan.contains_materialized_expression(handle) &&
      wgsl_ir_emit_expression_has_implicit_load_slot(function, handle) {
      offset += 1
    }
  }
  offset
}

///|
fn wgsl_ir_emit_expression_has_implicit_load_slot(
  function : Function,
  handle : Handle,
) -> Bool {
  match function.expressions.items.get(handle.index()) {
    Some(AccessIndex(base, _)) | Some(Component(base, _)) =>
      wgsl_ir_emit_expression_is_dynamic_pointer_access(function, base)
    _ => false
  }
}

///|
fn wgsl_ir_emit_expression_is_dynamic_pointer_access(
  function : Function,
  handle : Handle,
) -> Bool {
  match function.expressions.items.get(handle.index()) {
    Some(Access(_, _)) => true
    Some(AccessIndex(base, _)) | Some(Component(base, _)) =>
      wgsl_ir_emit_expression_is_dynamic_pointer_access(function, base)
    _ => false
  }
}

///|
fn wgsl_ir_emit_reused_resource_operand_slot_offset(
  function : Function,
  limit : Handle,
) -> Int {
  let seen : Map[Int, Int] = Map([])
  let mut offset = 0
  for index in 0..<=limit.index() {
    match function.expressions.items.get(index) {
      Some(ImageSample(image, sampler, _, _, _, _, _, _, _)) => {
        let sample_depth = wgsl_ir_function_expression_statement_depth(
          function,
          Handle(index),
        )
        offset += wgsl_ir_emit_reused_resource_operand_slot(
          function, seen, sample_depth, image,
        )
        offset += wgsl_ir_emit_reused_resource_operand_slot(
          function, seen, sample_depth, sampler,
        )
      }
      _ => ()
    }
  }
  offset
}

///|
fn wgsl_ir_emit_reused_resource_operand_slot(
  function : Function,
  seen : Map[Int, Int],
  sample_depth : Int,
  handle : Handle,
) -> Int {
  guard wgsl_ir_emit_expression_is_global_resource(function, handle) else {
    return 0
  }
  let index = handle.index()
  match seen.get(index) {
    Some(first_depth) => if first_depth == sample_depth { 0 } else { 1 }
    None => {
      seen.set(index, sample_depth)
      0
    }
  }
}

///|
fn wgsl_ir_function_expression_statement_depth(
  function : Function,
  handle : Handle,
) -> Int {
  match wgsl_ir_block_expression_statement_depth(function.body, handle, 0) {
    Some(depth) => depth
    None => -1
  }
}

///|
fn wgsl_ir_block_expression_statement_depth(
  block : Block,
  handle : Handle,
  depth : Int,
) -> Int? {
  for statement in block.statements {
    match wgsl_ir_statement_expression_depth(statement, handle, depth) {
      Some(found) => return Some(found)
      None => ()
    }
  }
  None
}

///|
fn wgsl_ir_statement_expression_depth(
  statement : Statement,
  handle : Handle,
  depth : Int,
) -> Int? {
  match statement {
    Emit(range) =>
      if range.start.index() <= handle.index() &&
        handle.index() <= range.end.index() {
        Some(depth)
      } else {
        None
      }
    Store(target, value) =>
      if target == handle || value == handle {
        Some(depth)
      } else {
        None
      }
    Phony(value) | Return(Some(value)) | ConstAssert(value) =>
      if value == handle {
        Some(depth)
      } else {
        None
      }
    If(condition, accept, reject) =>
      if condition == handle {
        Some(depth)
      } else {
        match
          wgsl_ir_block_expression_statement_depth(accept, handle, depth + 1) {
          Some(found) => Some(found)
          None =>
            wgsl_ir_block_expression_statement_depth(reject, handle, depth + 1)
        }
      }
    Loop(body, continuing, break_if) =>
      match break_if {
        Some(value) if value == handle => Some(depth)
        _ =>
          match
            wgsl_ir_block_expression_statement_depth(body, handle, depth + 1) {
            Some(found) => Some(found)
            None =>
              wgsl_ir_block_expression_statement_depth(
                continuing,
                handle,
                depth + 1,
              )
          }
      }
    Block(child) =>
      wgsl_ir_block_expression_statement_depth(child, handle, depth + 1)
    Switch(selector, cases) =>
      if selector == handle {
        Some(depth)
      } else {
        for case in cases {
          match
            wgsl_ir_block_expression_statement_depth(
              case.body,
              handle,
              depth + 1,
            ) {
            Some(found) => return Some(found)
            None => ()
          }
        }
        None
      }
    ImageStore(image, coordinate, array_index, value) =>
      if image == handle ||
        coordinate == handle ||
        value == handle ||
        array_index == Some(handle) {
        Some(depth)
      } else {
        None
      }
    ImageAtomic(image, coordinate, array_index, _, value) =>
      if image == handle ||
        coordinate == handle ||
        value == handle ||
        array_index == Some(handle) {
        Some(depth)
      } else {
        None
      }
    Atomic(pointer, _, value, compare, result) =>
      if pointer == handle ||
        value == handle ||
        compare == Some(handle) ||
        result == Some(handle) {
        Some(depth)
      } else {
        None
      }
    WorkGroupUniformLoad(pointer, result) =>
      if pointer == handle || result == handle {
        Some(depth)
      } else {
        None
      }
    Call(callee, arguments, result) =>
      if callee == handle ||
        arguments.any(fn(argument) { argument == handle }) ||
        result == Some(handle) {
        Some(depth)
      } else {
        None
      }
    RayQuery(query, ray_function) =>
      if query == handle ||
        wgsl_ir_ray_query_statement_function_contains_handle(
          ray_function, handle,
        ) {
        Some(depth)
      } else {
        None
      }
    RayPipelineFunction(ray_function) =>
      if wgsl_ir_ray_pipeline_statement_function_contains_handle(
          ray_function, handle,
        ) {
        Some(depth)
      } else {
        None
      }
    SubgroupBallot(value, result) =>
      if value == handle || result == Some(handle) {
        Some(depth)
      } else {
        None
      }
    SubgroupGather(_, value, result) =>
      if value == handle || result == handle {
        Some(depth)
      } else {
        None
      }
    SubgroupCollectiveOperation(_, _, value, result) =>
      if value == handle || result == handle {
        Some(depth)
      } else {
        None
      }
    CooperativeStore(pointer, data) =>
      if pointer == handle ||
        wgsl_ir_cooperative_store_data_contains_handle(data, handle) {
        Some(depth)
      } else {
        None
      }
    ControlBarrier(_)
    | MemoryBarrier(_)
    | Declare(_)
    | Break
    | Continue
    | Kill
    | Return(None)
    | ImplicitReturn => None
  }
}

///|
fn wgsl_ir_ray_query_statement_function_contains_handle(
  ray_function : RayQueryFunction,
  handle : Handle,
) -> Bool {
  match ray_function {
    Initialize(ray, flags) => ray == handle || flags == handle
    Proceed(value) | GenerateIntersection(value) => value == handle
    ConfirmIntersection | Terminate => false
  }
}

///|
fn wgsl_ir_ray_pipeline_statement_function_contains_handle(
  ray_function : RayPipelineFunction,
  handle : Handle,
) -> Bool {
  match ray_function {
    TraceRay(a, b, c) => a == handle || b == handle || c == handle
  }
}

///|
fn wgsl_ir_cooperative_store_data_contains_handle(
  data : CooperativeData,
  handle : Handle,
) -> Bool {
  data.pointer == handle || data.stride == handle
}

///|
fn wgsl_ir_emit_expression_is_global_resource(
  function : Function,
  handle : Handle,
) -> Bool {
  match function.expressions.items.get(handle.index()) {
    Some(GlobalVariable(_)) => true
    _ => false
  }
}

///|