///|
fn validation_function_contract(
  func_type : @types.FuncType,
) -> @wasm_milkir.WasmFunctionContract {
  {
    params: func_type.params.map(type_from_wasm),
    results: func_type.results.map(type_from_wasm),
  }
}

///|
fn validation_value_defaultable(value_type : @types.ValueType) -> Bool {
  match value_type {
    I32 | I64 | F32 | F64 | V128 => true
    _ => value_type.is_nullable()
  }
}

///|
fn validation_field_contract(
  field : @types.FieldType,
) -> @wasm_milkir.WasmFieldContract {
  match field.storage_type {
    Val(value_type) =>
      {
        storage: Value(type_from_wasm(value_type)),
        mutable_: field.mutable,
        defaultable: validation_value_defaultable(value_type),
      }
    Packed(I8) =>
      { storage: Packed8, mutable_: field.mutable, defaultable: true }
    Packed(I16) =>
      { storage: Packed16, mutable_: field.mutable, defaultable: true }
  }
}

///|
fn validation_defined_type_contract(
  subtype : @types.SubType,
) -> @wasm_milkir.WasmDefinedTypeContract {
  match subtype.composite {
    Func(func_type) => Function(validation_function_contract(func_type))
    Struct(struct_type) =>
      Struct(struct_type.fields.map(validation_field_contract))
    Array(array_type) => Array(validation_field_contract(array_type.element))
  }
}

///|
fn TranslationContext::linked_function_contracts(
  self : TranslationContext,
) -> Map[Int, @wasm_milkir.WasmFunctionContract] {
  let contracts : Map[Int, @wasm_milkir.WasmFunctionContract] = Map([])
  for import_index, type_index in self.import_func_type_indices {
    let linked_index = if import_index < self.import_remap.length() {
      self.import_remap[import_index]
    } else {
      import_index
    }
    if self.module_types.get(type_index) is Some(subtype) &&
      subtype.composite is Func(func_type) {
      contracts[linked_index] = validation_function_contract(func_type)
    }
  }
  for local_index, type_index in self.func_type_indices {
    let module_function_index = self.num_imports + local_index
    let linked_index = self.func_base + module_function_index
    if self.module_types.get(type_index) is Some(subtype) &&
      subtype.composite is Func(func_type) {
      contracts[linked_index] = validation_function_contract(func_type)
    }
  }
  contracts
}

///|
/// Build the module/linker context required by contextual Wasm MilkIR checks.
pub fn TranslationContext::wasm_validation_context(
  self : TranslationContext,
) -> @wasm_milkir.WasmValidationContext {
  let function_contracts = self.linked_function_contracts()
  @wasm_milkir.WasmValidationContext::new({
    function_contract: fn(index) { function_contracts.get(index) },
    defined_type_contract: fn(index) {
      self.module_types.get(index).map(validation_defined_type_contract)
    },
    table_contract: fn(index) {
      self.tables
      .get(index)
      .map(fn(table) {
        {
          index_type: if table.type_.is_table64 {
            I64
          } else {
            I32
          },
          element_type: type_from_wasm(table.type_.elem_type),
        }
      })
    },
    tag_contract: fn(index) {
      guard self.tags.get(index) is Some(tag) else { None }
      guard self.module_types.get(tag.type_idx) is Some(subtype) else { None }
      match subtype.composite {
        Func(func_type) => Some(validation_function_contract(func_type))
        _ => None
      }
    },
    memory_exists: fn(index) { index >= 0 && index < self.memory_count },
    data_segment_exists: fn(index) { index >= 0 && index < self.data_count },
    element_segment_type: fn(index) {
      self.element_types.get(index).map(type_from_wasm)
    },
  })
}