// 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 WgslIrLowerer::payload_variable_from_attributes(
  self : WgslIrLowerer,
  attributes : Array[@ast.WgslAttribute],
) -> Handle? raise WgslIrLowerError {
  match self.global_variable_from_identifier_attribute(attributes, "payload") {
    Some(handle) => Some(handle)
    None => None
  }
}

///|
fn WgslIrLowerer::mesh_stage_info_from_attributes(
  self : WgslIrLowerer,
  stage : ShaderStage,
  attributes : Array[@ast.WgslAttribute],
) -> MeshStageInfo? raise WgslIrLowerError {
  guard stage == Mesh else { return None }
  guard self.global_variable_from_identifier_attribute(attributes, "mesh")
    is Some(output_variable) else {
    raise UnsupportedExpression(
      "mesh entry point requires @mesh output variable",
    )
  }
  Some(self.mesh_stage_info_from_output_variable(output_variable))
}

///|
fn WgslIrLowerer::global_variable_from_identifier_attribute(
  self : WgslIrLowerer,
  attributes : Array[@ast.WgslAttribute],
  attribute_name : String,
) -> Handle? raise WgslIrLowerError {
  guard wgsl_ir_attribute_named(attributes, attribute_name) is Some(attribute) else {
    return None
  }
  guard wgsl_ir_attribute_identifier_value(attribute) is Some(name) else {
    raise UnsupportedExpression(
      "@\{attribute_name} expects a global variable identifier",
    )
  }
  match self.global_names.get(name) {
    Some(handle) => Some(handle)
    None => raise UnknownIdentifier(name)
  }
}

///|
fn WgslIrLowerer::mesh_stage_info_from_output_variable(
  self : WgslIrLowerer,
  output_variable : Handle,
) -> MeshStageInfo raise WgslIrLowerError {
  guard self.shader_module.global_variables.items.get(output_variable.index())
    is Some(global) else {
    raise UnsupportedExpression("invalid mesh output variable")
  }
  guard self.shader_module.types.get(global.ty) is Some(output_type) else {
    raise UnsupportedExpression("invalid mesh output variable type")
  }
  guard output_type.inner is Struct(members, _) else {
    raise UnsupportedExpression("mesh output variable must use a struct type")
  }
  guard wgsl_ir_struct_member_with_builtin(members, Vertices)
    is Some(vertex_member) else {
    raise UnsupportedExpression(
      "mesh output struct requires @builtin(vertices)",
    )
  }
  guard wgsl_ir_struct_member_with_builtin(members, Primitives)
    is Some(primitive_member) else {
    raise UnsupportedExpression(
      "mesh output struct requires @builtin(primitives)",
    )
  }
  let (vertex_output_type, max_vertices, max_vertices_override) = self.mesh_output_array_info(
    vertex_member.ty,
    "vertices",
  )
  let (primitive_output_type, max_primitives, max_primitives_override) = self.mesh_output_array_info(
    primitive_member.ty,
    "primitives",
  )
  {
    topology: self.mesh_primitive_topology(primitive_output_type),
    max_vertices,
    max_vertices_override,
    max_primitives,
    max_primitives_override,
    vertex_output_type,
    primitive_output_type,
    output_variable,
  }
}

///|
fn WgslIrLowerer::mesh_output_array_info(
  self : WgslIrLowerer,
  ty : Handle,
  member_name : String,
) -> (Handle, Int32, Handle?) raise WgslIrLowerError {
  guard self.shader_module.types.get(ty) is Some(array_type) else {
    raise UnsupportedExpression("invalid mesh output \{member_name} type")
  }
  guard array_type.inner is Array(element_type, size, _) else {
    raise UnsupportedExpression("mesh output \{member_name} must be an array")
  }
  let (count, override_handle) = match size {
    Constant(value) => (value, (None : Handle?))
    Pending(handle) => (1, Some(handle))
    Dynamic =>
      raise UnsupportedExpression(
        "mesh output \{member_name} array size must be fixed",
      )
  }
  (element_type, count, override_handle)
}

///|
fn WgslIrLowerer::mesh_primitive_topology(
  self : WgslIrLowerer,
  primitive_output_type : Handle,
) -> MeshOutputTopology raise WgslIrLowerError {
  guard self.shader_module.types.get(primitive_output_type)
    is Some(primitive_type) else {
    raise UnsupportedExpression("invalid mesh primitive output type")
  }
  guard primitive_type.inner is Struct(members, _) else {
    raise UnsupportedExpression("mesh primitive output type must be a struct")
  }
  for struct_member in members {
    match struct_member.binding {
      Some(BuiltIn(TriangleIndices)) => return Triangles
      Some(BuiltIn(LineIndices)) => return Lines
      Some(BuiltIn(PointIndex)) => return Points
      _ => ()
    }
  }
  raise UnsupportedExpression(
    "mesh primitive output type requires point, line, or triangle indices",
  )
}

///|
fn wgsl_ir_struct_member_with_builtin(
  members : Array[StructMember],
  builtin : BuiltIn,
) -> StructMember? {
  for struct_member in members {
    match struct_member.binding {
      Some(BuiltIn(value)) if value == builtin => return Some(struct_member)
      _ => ()
    }
  }
  None
}