///|
/// Parsing for component type section (section 7 in the component binary format).
///
/// For now, this only supports a small subset (primvaltype + basic functype)
/// to unblock end-to-end parsing and testing.

///|
pub(all) enum PrimValType {
  Bool
  S8
  U8
  S16
  U16
  S32
  U32
  S64
  U64
  F32
  F64
  Char
  String
  ErrorContext
} derive(Debug, Eq)

///|
pub(all) enum ValType {
  TypeIdx(Int)
  Prim(PrimValType)
} derive(Debug, Eq)

///|
pub(all) enum ResourceKind {
  /// Resources defined within a component.
  GuestDefined
  /// Resources imported from the host environment.
  HostDefined
} derive(Debug, Eq)

///|
pub(all) struct Param {
  label : String
  ty : ValType
} derive(Debug, Eq)

///|
pub(all) struct FuncType {
  is_async : Bool
  params : Array[Param]
  result : ValType?
} derive(Debug, Eq)

///|
/// A subset of instance type declarations (enough for wasmtime/component-spec).
pub(all) enum InstanceDecl {
  // Embedded core typedef (module type / func type / etc).
  CoreType(Bytes)
  Type(TypeDef)
  // (alias outer ...) inside an instance type declaration.
  // `kind` is the component model external kind discriminator (e.g. Type=0x03).
  AliasOuter(Int, Int, Int)
  // (alias export ...) inside an instance type declaration.
  // `kind` is the component model external kind discriminator.
  AliasInstanceExport(Int, Int, String)
  Export(String, Array[NameAttribute], ExternDesc)
} derive(Debug, Eq)

///|
/// A subset of component type declarations (enough for WIT-encoded components).
pub(all) enum ComponentDecl {
  // Embedded core typedef (module type / func type / etc).
  CoreType(Bytes)
  Type(TypeDef)
  // (alias outer ...) inside a component type declaration.
  AliasOuter(Int, Int, Int)
  // (alias export ...) inside a component type declaration.
  AliasInstanceExport(Int, Int, String)
  Import(String, Array[NameAttribute], ExternDesc)
  Export(String, Array[NameAttribute], ExternDesc)
} derive(Debug, Eq)

///|
pub(all) enum TypeDef {
  DefValType(PrimValType)
  FuncType(FuncType)
  List(ValType)
  FixedList(ValType, Int)
  MapType(ValType, ValType)
  Record(Array[RecordField])
  Variant(Array[VariantCase])
  Tuple(Array[ValType])
  Flags(Array[String])
  Enum(Array[String])
  Option(ValType)
  Result(ValType?, ValType?)
  Own(Int)
  Borrow(Int)
  Stream(ValType?)
  Future(ValType?)
  // Nominal resource type. `id` is filled at instantiation time.
  // `rep` is the core wasm value type (e.g. i32=0x7f).
  // `dtor` is an optional destructor core func index.
  // `kind` tracks whether the resource originates from the component or host.
  ResourceType(Int, Int, Int?, ResourceKind)
  ComponentType(Array[ComponentDecl])
  InstanceTypeEmpty
  InstanceType(Array[InstanceDecl])
} derive(Debug, Eq)

///|
pub impl Show for TypeDef with fn output(self, logger) {
  logger.write_string(@types.compact_show_repr(Repr(self).to_string()))
}

///|
pub(all) struct RecordField {
  label : String
  ty : ValType
} derive(Debug, Eq)

///|
pub(all) struct VariantCase {
  label : String
  ty : ValType?
  // `refines` points at a previously defined case index.
  refines : Int?
} derive(Debug, Eq)

///|

///|
fn Reader::read_label(self : Reader) -> String raise ComponentParseError {
  let len = self.read_leb_u32()
  let bytes = self.read_bytes(len)
  decode_utf8_validated(bytes)
}

///|
fn parse_prim_valtype(op : Int) -> PrimValType? {
  match op {
    0x7f => Some(Bool)
    0x7e => Some(S8)
    0x7d => Some(U8)
    0x7c => Some(S16)
    0x7b => Some(U16)
    0x7a => Some(S32)
    0x79 => Some(U32)
    0x78 => Some(S64)
    0x77 => Some(U64)
    0x76 => Some(F32)
    0x75 => Some(F64)
    0x74 => Some(Char)
    0x73 => Some(String)
    0x64 => Some(ErrorContext)
    _ => None
  }
}

///|
fn parse_valtype(reader : Reader) -> ValType raise ComponentParseError {
  let v = reader.read_sleb_i32()
  if v >= 0 {
    TypeIdx(v)
  } else {
    match parse_prim_valtype_from_sleb(v) {
      Some(p) => Prim(p)
      None => raise InvalidTypeSection
    }
  }
}

///|
fn parse_paramlist(reader : Reader) -> Array[Param] raise ComponentParseError {
  let n = reader.read_leb_u32()
  let params : Array[Param] = []
  for _ in 0.. ValType? raise ComponentParseError {
  let tag = reader.read_u8()
  if tag == 0x00 {
    Some(parse_valtype(reader))
  } else if tag == 0x01 {
    let must_be_zero = reader.read_u8()
    if must_be_zero != 0x00 {
      raise InvalidTypeSection
    }
    None
  } else {
    raise InvalidTypeSection
  }
}

///|
fn parse_functype(
  reader : Reader,
  op : Int,
) -> FuncType raise ComponentParseError {
  let is_async = op == 0x43
  let params = parse_paramlist(reader)
  let result = parse_resultlist(reader)
  { is_async, params, result, }
}

///|
fn parse_nameattributes_string(
  reader : Reader,
) -> (String, Array[NameAttribute]) raise ComponentParseError {
  let tag = reader.read_u8()
  if tag == 0x00 {
    let len = reader.read_leb_u32()
    let bytes = reader.read_bytes(len)
    (decode_utf8_validated(bytes), [])
  } else if tag == 0x01 {
    let len = reader.read_leb_u32()
    let bytes = reader.read_bytes(len)
    (decode_utf8_validated(bytes), [])
  } else if tag == 0x02 {
    let len = reader.read_leb_u32()
    let bytes = reader.read_bytes(len)
    (decode_utf8_validated(bytes), parse_attributes(reader))
  } else {
    raise InvalidTypeSection
  }
}

///|
fn parse_typebound_for_instancedecl(
  reader : Reader,
) -> TypeBound raise ComponentParseError {
  let tag = reader.read_u8()
  if tag == 0x00 {
    Eq(reader.read_typeidx())
  } else if tag == 0x01 {
    SubResource
  } else {
    raise InvalidTypeSection
  }
}

///|
fn parse_valuebound_for_instancedecl(
  reader : Reader,
) -> ValueBound raise ComponentParseError {
  let tag = reader.read_u8()
  if tag == 0x00 {
    Eq(reader.read_leb_u32())
  } else if tag == 0x01 {
    Type(parse_valtype(reader))
  } else {
    raise InvalidTypeSection
  }
}

///|
fn parse_externdesc_for_instancedecl(
  reader : Reader,
) -> ExternDesc raise ComponentParseError {
  let tag = reader.read_u8()
  if tag == 0x00 {
    let must_be_core_module = reader.read_u8()
    if must_be_core_module != 0x11 {
      raise InvalidTypeSection
    }
    CoreModuleType(reader.read_leb_u32())
  } else if tag == 0x01 {
    FuncType(reader.read_typeidx())
  } else if tag == 0x02 {
    Value(parse_valuebound_for_instancedecl(reader))
  } else if tag == 0x03 {
    Type(parse_typebound_for_instancedecl(reader))
  } else if tag == 0x04 {
    ComponentType(reader.read_typeidx())
  } else if tag == 0x05 {
    InstanceType(reader.read_typeidx())
  } else {
    raise InvalidTypeSection
  }
}

///|
fn parse_alias_kind(reader : Reader) -> Int raise ComponentParseError {
  // Alias kinds use the same encoding as Sort in the import/export sections:
  // 0x00  for core items, or a single-byte tag for component items.
  let tag = reader.read_u8()
  if tag == 0x00 {
    let core_sort = reader.read_u8()
    // Encode core sorts in a separate range to avoid collisions with 0x01..0x05.
    0x100 + core_sort
  } else {
    tag
  }
}

///|
fn skip_limits(reader : Reader) -> Unit raise ComponentParseError {
  let flags = reader.read_u8()
  reader.read_leb_u32() |> ignore
  if (flags & 0x01) != 0 {
    reader.read_leb_u32() |> ignore
  }
}

///|
fn skip_core_functype_inline(reader : Reader) -> Unit raise ComponentParseError {
  let form = reader.read_u8()
  if form != 0x60 {
    raise InvalidTypeSection
  }
  skip_core_functype_body(reader)
}

///|
fn skip_core_functype_body(reader : Reader) -> Unit raise ComponentParseError {
  let nparams = reader.read_leb_u32()
  for _i in 0.. ignore
  }
  let nresults = reader.read_leb_u32()
  for _i in 0.. ignore
  }
}

///|
fn skip_core_extern_type(
  reader : Reader,
  kind : Int,
) -> Unit raise ComponentParseError {
  match kind {
    0x00 => reader.read_leb_u32() |> ignore // func type index
    0x01 => {
      reader.read_u8() |> ignore // elem/ref type
      skip_limits(reader)
    }
    0x02 => skip_limits(reader)
    0x03 => {
      reader.read_u8() |> ignore // valtype
      reader.read_u8() |> ignore // mut
    }
    0x04 => reader.read_leb_u32() |> ignore // tag type index
    _ => raise InvalidTypeSection
  }
}

///|
fn skip_core_moduletype(reader : Reader) -> Unit raise ComponentParseError {
  let ndecls = reader.read_leb_u32()
  for _i in 0.. {
        reader.read_label() |> ignore // module
        reader.read_label() |> ignore // name
        let kind = reader.read_u8()
        skip_core_extern_type(reader, kind)
      }
      0x01 => skip_core_functype_inline(reader)
      0x02 => {
        // alias decl: sort + target + indices
        reader.read_u8() |> ignore
        let target = reader.read_u8()
        if target == 0x01 {
          reader.read_leb_u32() |> ignore
          reader.read_leb_u32() |> ignore
        } else {
          raise InvalidTypeSection
        }
      }
      0x03 => {
        reader.read_label() |> ignore // name
        let kind = reader.read_u8()
        skip_core_extern_type(reader, kind)
      }
      _ => raise InvalidTypeSection
    }
  }
}

///|
fn skip_core_typedef(reader : Reader) -> Unit raise ComponentParseError {
  let op = reader.read_u8()
  if op == 0x50 {
    skip_core_moduletype(reader)
  } else if op == 0x60 {
    skip_core_functype_body(reader)
  } else if op == 0x00 {
    if reader.read_u8() != 0x50 {
      raise InvalidTypeSection
    }
    let supertype_count = reader.read_leb_u32()
    for _ in 0.. ignore
    }
    if reader.read_u8() != 0x60 {
      raise InvalidTypeSection
    }
    skip_core_functype_body(reader)
  } else {
    raise InvalidTypeSection
  }
}

///|
fn bytes_sub(bytes : Bytes, start : Int, end : Int) -> Bytes {
  let out : Array[Byte] = []
  for i in start.. InstanceDecl raise ComponentParseError {
  let tag = reader.read_u8()
  if tag == 0x00 {
    let start = reader.pos
    skip_core_typedef(reader)
    let end = reader.pos
    CoreType(bytes_sub(reader.data, start, end))
  } else if tag == 0x01 {
    Type(parse_typedef(reader))
  } else if tag == 0x02 {
    // aliasdecl ::= 0x02 kind: target: ...
    let kind = parse_alias_kind(reader)
    let target = reader.read_u8()
    if target == 0x00 {
      // InstanceExport { instance_index, name }
      let instance_idx = reader.read_leb_u32()
      // Alias targets use the plain `name` string encoding (length + bytes),
      // not the export-name string encoding used by import/export decls.
      let name = reader.read_label()
      AliasInstanceExport(kind, instance_idx, name)
    } else if target == 0x02 {
      // Outer { count, index }
      let count = reader.read_leb_u32()
      let idx = reader.read_leb_u32()
      AliasOuter(kind, count, idx)
    } else {
      raise InvalidTypeSection
    }
  } else if tag == 0x04 {
    let (name, attributes) = parse_nameattributes_string(reader)
    let desc = parse_externdesc_for_instancedecl(reader)
    Export(name, attributes, desc)
  } else {
    raise UnsupportedTypeOpcode(tag)
  }
}

///|
fn parse_componentdecl(
  reader : Reader,
) -> ComponentDecl raise ComponentParseError {
  let tag = reader.read_u8()
  if tag == 0x00 {
    let start = reader.pos
    skip_core_typedef(reader)
    let end = reader.pos
    CoreType(bytes_sub(reader.data, start, end))
  } else if tag == 0x01 {
    Type(parse_typedef(reader))
  } else if tag == 0x02 {
    // aliasdecl ::= 0x02 kind: target: ...
    let kind = parse_alias_kind(reader)
    let target = reader.read_u8()
    if target == 0x00 {
      // InstanceExport { instance_index, name }
      let instance_idx = reader.read_leb_u32()
      let name = reader.read_label()
      AliasInstanceExport(kind, instance_idx, name)
    } else if target == 0x02 {
      // Outer { count, index }
      let count = reader.read_leb_u32()
      let idx = reader.read_leb_u32()
      AliasOuter(kind, count, idx)
    } else {
      raise InvalidTypeSection
    }
  } else if tag == 0x03 {
    let (name, attributes) = parse_nameattributes_string(reader)
    let desc = parse_externdesc_for_instancedecl(reader)
    Import(name, attributes, desc)
  } else if tag == 0x04 {
    let (name, attributes) = parse_nameattributes_string(reader)
    let desc = parse_externdesc_for_instancedecl(reader)
    Export(name, attributes, desc)
  } else {
    raise UnsupportedTypeOpcode(tag)
  }
}

///|
fn parse_typedef(reader : Reader) -> TypeDef raise ComponentParseError {
  let op = reader.read_u8()
  match op {
    0x40 | 0x43 => FuncType(parse_functype(reader, op))
    0x65 => {
      // futuretype ::= 0x65 t?:
      let tag = reader.read_u8()
      if tag == 0x00 {
        Future(None)
      } else if tag == 0x01 {
        Future(Some(parse_valtype(reader)))
      } else {
        raise InvalidTypeSection
      }
    }
    0x66 => {
      // streamtype ::= 0x66 t?:
      let tag = reader.read_u8()
      if tag == 0x00 {
        Stream(None)
      } else if tag == 0x01 {
        Stream(Some(parse_valtype(reader)))
      } else {
        raise InvalidTypeSection
      }
    }
    0x68 => Borrow(reader.read_typeidx())
    0x69 => Own(reader.read_typeidx())
    0x6a => {
      let has_ok = reader.read_u8()
      let ok = if has_ok == 0x00 {
        None
      } else if has_ok == 0x01 {
        Some(parse_valtype(reader))
      } else {
        raise InvalidTypeSection
      }
      let has_err = reader.read_u8()
      let err = if has_err == 0x00 {
        None
      } else if has_err == 0x01 {
        Some(parse_valtype(reader))
      } else {
        raise InvalidTypeSection
      }
      Result(ok, err)
    }
    0x6b => Option(parse_valtype(reader))
    0x6d => {
      let n = reader.read_leb_u32()
      let cases : Array[String] = []
      for _ in 0.. {
      let n = reader.read_leb_u32()
      let cases : Array[String] = []
      for _ in 0.. {
      let n = reader.read_leb_u32()
      let tys : Array[ValType] = []
      for _ in 0.. List(parse_valtype(reader))
    0x67 => {
      let ty = parse_valtype(reader)
      let len = reader.read_leb_u32()
      FixedList(ty, len)
    }
    0x63 => MapType(parse_valtype(reader), parse_valtype(reader))
    0x71 => {
      let n = reader.read_leb_u32()
      let cases : Array[VariantCase] = []
      for _ in 0.. {
      let n = reader.read_leb_u32()
      let fields : Array[RecordField] = []
      for _ in 0.. {
      // resourcetype ::= 0x3f rep: dtor:
      let rep = reader.read_u8()
      let has_dtor = reader.read_u8()
      let dtor = if has_dtor == 0x00 {
        None
      } else if has_dtor == 0x01 {
        Some(reader.read_leb_u32())
      } else {
        raise InvalidTypeSection
      }
      ResourceType(-1, rep, dtor, GuestDefined)
    }
    0x41 => {
      // componenttype ::= 0x41 cd*:vec()
      let n = reader.read_leb_u32()
      let decls : Array[ComponentDecl] = []
      for _ in 0.. {
      // instancetype ::= 0x42 id*:vec()
      let n = reader.read_leb_u32()
      if n == 0 {
        InstanceTypeEmpty
      } else {
        let decls : Array[InstanceDecl] = []
        for _ in 0..
      match parse_prim_valtype(op) {
        Some(p) => DefValType(p)
        None => raise UnsupportedTypeOpcode(op)
      }
  }
}

///|
pub fn parse_type_section(
  payload : Bytes,
) -> Array[TypeDef] raise ComponentParseError {
  let reader = Reader::Reader(payload)
  let n = reader.read_leb_u32()
  let types : Array[TypeDef] = []
  for _ in 0..