///|
pub suberror WgslIrValidationError {
  InvalidTypeHandle(Int32)
  InvalidConstantHandle(Int32)
  InvalidOverrideHandle(Int32)
  InvalidGlobalVariableHandle(Int32)
  InvalidGlobalExpressionHandle(Int32)
  InvalidFunctionHandle(Int32)
  InvalidLocalVariableHandle(Int32)
  InvalidFunctionExpressionHandle(Int32)
  InvalidHandleRange(Int32, Int32)
  InvalidEntryPoint(String)
  MissingExpressionType(Int32, String)
  InvalidConditionType(String, Int32, String, String)
  InvalidStoreTarget(Int32)
  InvalidReturnValue(String)
  InvalidFunctionArgumentCount(String, Int32, Int32)
  InvalidFunctionArgumentType(String, Int32)
  InvalidFunctionResult(String)
  InvalidType(String)
  InvalidGlobalVariable(String)
  InvalidExpression(String, Int32)
  InvalidStatement(String)
}

///|
pub fn WgslIrValidationError::message(self : WgslIrValidationError) -> String {
  match self {
    InvalidTypeHandle(index) => "invalid IR type handle: \{index}"
    InvalidConstantHandle(index) => "invalid IR constant handle: \{index}"
    InvalidOverrideHandle(index) => "invalid IR override handle: \{index}"
    InvalidGlobalVariableHandle(index) =>
      "invalid IR global variable handle: \{index}"
    InvalidGlobalExpressionHandle(index) =>
      "invalid IR global expression handle: \{index}"
    InvalidFunctionHandle(index) => "invalid IR function handle: \{index}"
    InvalidLocalVariableHandle(index) =>
      "invalid IR local variable handle: \{index}"
    InvalidFunctionExpressionHandle(index) =>
      "invalid IR function expression handle: \{index}"
    InvalidHandleRange(start, end) =>
      "invalid IR handle range: \{start}..\{end}"
    InvalidEntryPoint(name) => "invalid IR entry point: \{name}"
    MissingExpressionType(index, expression) =>
      "missing IR expression type for handle: \{index} (\{expression})"
    InvalidConditionType(function_name, index, expression, ty) =>
      "invalid IR condition expression type in \{function_name} for handle: \{index} (\{expression}) with type \{ty}"
    InvalidStoreTarget(index) =>
      "invalid IR store target expression type for handle: \{index}"
    InvalidReturnValue(name) => "invalid IR return value for function: \{name}"
    InvalidFunctionArgumentCount(name, expected, actual) =>
      "invalid IR function call argument count for \{name}: expected \{expected}, got \{actual}"
    InvalidFunctionArgumentType(name, index) =>
      "invalid IR function call argument type for \{name} at argument \{index}"
    InvalidFunctionResult(name) => "invalid IR function call result for \{name}"
    InvalidType(message) => "invalid IR type: \{message}"
    InvalidGlobalVariable(message) => "invalid IR global variable: \{message}"
    InvalidExpression(message, index) =>
      "invalid IR expression for handle \{index}: \{message}"
    InvalidStatement(message) => "invalid IR statement: \{message}"
  }
}

///|
pub fn validate_wgsl_ir_module(
  shader_module : Module,
) -> Unit raise WgslIrValidationError {
  let validator = WgslIrValidator::{ shader_module, filter: None, }
  validator.validate_module()
}

///|
fn validate_wgsl_ir_module_with_filter(
  shader_module : Module,
  filter : WgslIrReachability,
) -> Unit raise WgslIrValidationError {
  let validator = WgslIrValidator::{ shader_module, filter: Some(filter), }
  validator.validate_module()
}

///|
priv struct WgslIrValidator {
  shader_module : Module
  filter : WgslIrReachability?
}

///|
priv struct WgslIrValidationLayout {
  align : Int32
  size : Int32
}

///|
fn WgslIrValidator::validate_module(
  self : WgslIrValidator,
) -> Unit raise WgslIrValidationError {
  for index in 0.. self.validate_global_expression_handle(init)
        None => ()
      }
    }
  }
  for index in 0.. Unit raise WgslIrValidationError {
  if entry_point.name == "" {
    raise InvalidEntryPoint(entry_point.name)
  }
  self.validate_function(entry_point.function)
}

///|
fn WgslIrValidator::should_validate_type(
  self : WgslIrValidator,
  index : Int32,
) -> Bool {
  match self.filter {
    Some(filter) => filter.contains_type(index)
    None => true
  }
}

///|
fn WgslIrValidator::should_validate_constant(
  self : WgslIrValidator,
  index : Int32,
) -> Bool {
  match self.filter {
    Some(filter) => filter.contains_constant(index)
    None => true
  }
}

///|
fn WgslIrValidator::should_validate_override(
  self : WgslIrValidator,
  index : Int32,
) -> Bool {
  match self.filter {
    Some(filter) => filter.contains_override(index)
    None => true
  }
}

///|
fn WgslIrValidator::should_validate_global_variable(
  self : WgslIrValidator,
  index : Int32,
) -> Bool {
  match self.filter {
    Some(filter) => filter.contains_global_variable(index)
    None => true
  }
}

///|
fn WgslIrValidator::should_validate_global_expression(
  self : WgslIrValidator,
  index : Int32,
) -> Bool {
  ignore(index)
  match self.filter {
    Some(_) => false
    None => true
  }
}

///|
fn WgslIrValidator::should_validate_function(
  self : WgslIrValidator,
  index : Int32,
) -> Bool {
  match self.filter {
    Some(filter) => filter.contains_function(index)
    None => true
  }
}

///|
fn WgslIrValidator::should_validate_entry_point(
  self : WgslIrValidator,
  index : Int32,
) -> Bool {
  match self.filter {
    Some(filter) => filter.contains_entry_point(index)
    None => true
  }
}