///| Runtime-feature detection is intentionally conservative: a reported

///| feature is definitely required, while an absent report means only that the

///|
/// currently decoded metadata did not require it.
pub enum WasmFeature {
  MultiMemory
  Memory64
  Threads
  BulkMemory
  ReferenceTypes
  ExceptionHandling
  Simd
}

///|
pub fn WasmFeature::label(self : WasmFeature) -> String {
  match self {
    MultiMemory => "multi-memory"
    Memory64 => "memory64"
    Threads => "threads"
    BulkMemory => "bulk-memory"
    ReferenceTypes => "reference-types"
    ExceptionHandling => "exception-handling"
    Simd => "simd"
  }
}

///|
fn contains_feature(values : Array[WasmFeature], target : WasmFeature) -> Bool {
  for value in values {
    if value.label() == target.label() {
      return true
    }
  }
  false
}

///|
fn add_feature(values : Array[WasmFeature], target : WasmFeature) -> Unit {
  if !contains_feature(values, target) {
    values.push(target)
  }
}

///|
pub fn detect_features(decoded : DecodedModule) -> Array[WasmFeature] {
  let features : Array[WasmFeature] = []
  if decoded.memories.length() + imported_count(decoded, MemoryExternal) > 1 {
    add_feature(features, MultiMemory)
  }
  for memory in decoded.memories {
    if memory.limits.memory64 {
      add_feature(features, Memory64)
    }
    if memory.limits.shared {
      add_feature(features, Threads)
    }
  }
  for table in decoded.tables {
    match table.element_type {
      ExternRef => add_feature(features, ReferenceTypes)
      _ => ()
    }
  }
  if decoded.binary().has_section(DataCount) {
    add_feature(features, BulkMemory)
  }
  if decoded.binary().has_section(Tag) {
    add_feature(features, ExceptionHandling)
  }
  for body in decoded.code_bodies {
    match scan_code_body(decoded.binary(), body) {
      Ok(stats) => {
        if stats.bulk_memory_instruction_count > 0 {
          add_feature(features, BulkMemory)
        }
        if stats.simd_instruction_count > 0 {
          add_feature(features, Simd)
        }
      }
      Err(_) => ()
    }
  }
  features
}

///|
pub fn render_features(features : Array[WasmFeature]) -> String {
  if features.length() == 0 {
    return "required features: MVP\n"
  }
  let mut text = "required features:\n"
  for feature in features {
    text = text + "  - " + feature.label() + "\n"
  }
  text
}