use crate::component::*;
use crate::{ExportKind, Module, NameMap, RawSection, ValType};
use alloc::format;
use alloc::vec::Vec;
use core::mem;

/// Convenience type to build a component incrementally and automatically keep
/// track of index spaces.
///
/// This type is intended to be a wrapper around the [`Component`] encoding type
/// which is useful for building it up incrementally over time. This type will
/// automatically collect definitions into sections and reports the index of all
/// items added by keeping track of indices internally.
#[derive(Debug, Default)]
pub struct ComponentBuilder {
    /// The binary component that's being built.
    component: Component,

    /// The last section which was appended to during encoding. This type is
    /// generated by the `section_accessors` macro below.
    ///
    /// When something is encoded this is used if it matches the kind of item
    /// being encoded, otherwise it's "flushed" to the output component and a
    /// new section is started.
    last_section: LastSection,

    // Core index spaces
    core_modules: Namespace,
    core_funcs: Namespace,
    core_types: Namespace,
    core_memories: Namespace,
    core_tables: Namespace,
    core_instances: Namespace,
    core_tags: Namespace,
    core_globals: Namespace,

    // Component index spaces
    funcs: Namespace,
    instances: Namespace,
    types: Namespace,
    components: Namespace,
    values: Namespace,
}

impl ComponentBuilder {
    /// Returns the current number of core modules.
    pub fn core_module_count(&self) -> u32 {
        self.core_modules.count
    }

    /// Returns the current number of core funcs.
    pub fn core_func_count(&self) -> u32 {
        self.core_funcs.count
    }

    /// Returns the current number of core types.
    pub fn core_type_count(&self) -> u32 {
        self.core_types.count
    }

    /// Returns the current number of core memories.
    pub fn core_memory_count(&self) -> u32 {
        self.core_memories.count
    }

    /// Returns the current number of core tables.
    pub fn core_table_count(&self) -> u32 {
        self.core_tables.count
    }

    /// Returns the current number of core instances.
    pub fn core_instance_count(&self) -> u32 {
        self.core_instances.count
    }

    /// Returns the current number of core tags.
    pub fn core_tag_count(&self) -> u32 {
        self.core_tags.count
    }

    /// Returns the current number of core globals.
    pub fn core_global_count(&self) -> u32 {
        self.core_globals.count
    }

    /// Returns the current number of component funcs.
    pub fn func_count(&self) -> u32 {
        self.funcs.count
    }

    /// Returns the current number of component instances.
    pub fn instance_count(&self) -> u32 {
        self.instances.count
    }

    /// Returns the current number of component values.
    pub fn value_count(&self) -> u32 {
        self.values.count
    }

    /// Returns the current number of components.
    pub fn component_count(&self) -> u32 {
        self.components.count
    }

    /// Returns the current number of component types.
    pub fn type_count(&self) -> u32 {
        self.types.count
    }

    /// Add all debug names registered to the component.
    pub fn append_names(&mut self) {
        let mut names = ComponentNameSection::new();
        if !self.core_funcs.names.is_empty() {
            names.core_funcs(&self.core_funcs.names);
        }
        if !self.core_tables.names.is_empty() {
            names.core_tables(&self.core_tables.names);
        }
        if !self.core_memories.names.is_empty() {
            names.core_memories(&self.core_memories.names);
        }
        if !self.core_globals.names.is_empty() {
            names.core_globals(&self.core_globals.names);
        }
        if !self.core_tags.names.is_empty() {
            names.core_tags(&self.core_tags.names);
        }
        if !self.core_types.names.is_empty() {
            names.core_types(&self.core_types.names);
        }
        if !self.core_modules.names.is_empty() {
            names.core_modules(&self.core_modules.names);
        }
        if !self.core_instances.names.is_empty() {
            names.core_instances(&self.core_instances.names);
        }
        if !self.instances.names.is_empty() {
            names.instances(&self.instances.names);
        }
        if !self.funcs.names.is_empty() {
            names.funcs(&self.funcs.names);
        }
        if !self.types.names.is_empty() {
            names.types(&self.types.names);
        }
        if !self.components.names.is_empty() {
            names.components(&self.components.names);
        }
        if !self.instances.names.is_empty() {
            names.instances(&self.instances.names);
        }
        if !names.is_empty() {
            self.custom_section(&names.as_custom());
        }
    }

    /// Completes this component and returns the binary encoding of the entire
    /// component.
    pub fn finish(mut self) -> Vec<u8> {
        self.flush();
        self.component.finish()
    }

    /// Encodes a core wasm `Module` into this component, returning its index.
    pub fn core_module(&mut self, debug_name: Option<&str>, module: &Module) -> u32 {
        self.flush();
        self.component.section(&ModuleSection(module));
        self.core_modules.add(debug_name)
    }

    /// Encodes a core wasm `module` into this component, returning its index.
    pub fn core_module_raw(&mut self, debug_name: Option<&str>, module: &[u8]) -> u32 {
        self.flush();
        self.component.section(&RawSection {
            id: ComponentSectionId::CoreModule.into(),
            data: module,
        });
        self.core_modules.add(debug_name)
    }

    /// Instantiates a core wasm module at `module_index` with the `args`
    /// provided.
    ///
    /// Returns the index of the core wasm instance created.
    pub fn core_instantiate<'a, A>(
        &mut self,
        debug_name: Option<&str>,
        module_index: u32,
        args: A,
    ) -> u32
    where
        A: IntoIterator<Item = (&'a str, ModuleArg)>,
        A::IntoIter: ExactSizeIterator,
    {
        self.instances().instantiate(module_index, args);
        self.core_instances.add(debug_name)
    }

    /// Creates a new core wasm instance from the `exports` provided.
    ///
    /// Returns the index of the core wasm instance created.
    pub fn core_instantiate_exports<'a, E>(&mut self, debug_name: Option<&str>, exports: E) -> u32
    where
        E: IntoIterator<Item = (&'a str, ExportKind, u32)>,
        E::IntoIter: ExactSizeIterator,
    {
        self.instances().export_items(exports);
        self.core_instances.add(debug_name)
    }

    /// Creates a new aliased item where the core `instance` specified has its
    /// export `name` aliased out with the `kind` specified.
    ///
    /// Returns the index of the item created.
    pub fn core_alias_export(
        &mut self,
        debug_name: Option<&str>,
        instance: u32,
        name: &str,
        kind: ExportKind,
    ) -> u32 {
        self.alias(
            debug_name,
            Alias::CoreInstanceExport {
                instance,
                kind,
                name,
            },
        )
    }

    /// Adds a new alias to this component
    pub fn alias(&mut self, debug_name: Option<&str>, alias: Alias<'_>) -> u32 {
        self.aliases().alias(alias);
        match alias {
            Alias::InstanceExport { kind, .. } => self.inc_kind(debug_name, kind),
            Alias::CoreInstanceExport { kind, .. } => self.inc_core_kind(debug_name, kind),
            Alias::Outer {
                kind: ComponentOuterAliasKind::Type,
                ..
            } => self.types.add(debug_name),
            Alias::Outer {
                kind: ComponentOuterAliasKind::CoreModule,
                ..
            } => self.core_modules.add(debug_name),
            Alias::Outer {
                kind: ComponentOuterAliasKind::Component,
                ..
            } => self.components.add(debug_name),
            Alias::Outer {
                kind: ComponentOuterAliasKind::CoreType,
                ..
            } => self.core_types.add(debug_name),
        }
    }

    /// Creates an alias to a previous component instance's exported item.
    ///
    /// The `instance` provided is the instance to access and the `name` is the
    /// item to access.
    ///
    /// Returns the index of the new item defined.
    pub fn alias_export(&mut self, instance: u32, name: &str, kind: ComponentExportKind) -> u32 {
        self.alias(
            Some(name),
            Alias::InstanceExport {
                instance,
                kind,
                name,
            },
        )
    }

    fn inc_kind(&mut self, debug_name: Option<&str>, kind: ComponentExportKind) -> u32 {
        match kind {
            ComponentExportKind::Func => self.funcs.add(debug_name),
            ComponentExportKind::Module => self.core_modules.add(debug_name),
            ComponentExportKind::Type => self.types.add(debug_name),
            ComponentExportKind::Component => self.components.add(debug_name),
            ComponentExportKind::Instance => self.instances.add(debug_name),
            ComponentExportKind::Value => self.values.add(debug_name),
        }
    }

    fn inc_core_kind(&mut self, debug_name: Option<&str>, kind: ExportKind) -> u32 {
        match kind {
            ExportKind::Func => self.core_funcs.add(debug_name),
            ExportKind::Table => self.core_tables.add(debug_name),
            ExportKind::Memory => self.core_memories.add(debug_name),
            ExportKind::Global => self.core_globals.add(debug_name),
            ExportKind::Tag => self.core_tags.add(debug_name),
        }
    }

    /// Lowers the `func_index` component function into a core wasm function
    /// using the `options` provided.
    ///
    /// Returns the index of the core wasm function created.
    pub fn lower_func<O>(&mut self, debug_name: Option<&str>, func_index: u32, options: O) -> u32
    where
        O: IntoIterator<Item = CanonicalOption>,
        O::IntoIter: ExactSizeIterator,
    {
        self.canonical_functions().lower(func_index, options);
        self.core_funcs.add(debug_name)
    }

    /// Lifts the core wasm `core_func_index` function with the component
    /// function type `type_index` and `options`.
    ///
    /// Returns the index of the component function created.
    pub fn lift_func<O>(
        &mut self,
        debug_name: Option<&str>,
        core_func_index: u32,
        type_index: u32,
        options: O,
    ) -> u32
    where
        O: IntoIterator<Item = CanonicalOption>,
        O::IntoIter: ExactSizeIterator,
    {
        self.canonical_functions()
            .lift(core_func_index, type_index, options);
        self.funcs.add(debug_name)
    }

    /// Imports a new item into this component with the `name` and `ty` specified.
    pub fn import(&mut self, name: &str, ty: ComponentTypeRef) -> u32 {
        let ret = match &ty {
            ComponentTypeRef::Instance(_) => self.instances.add(Some(name)),
            ComponentTypeRef::Func(_) => self.funcs.add(Some(name)),
            ComponentTypeRef::Type(..) => self.types.add(Some(name)),
            ComponentTypeRef::Component(_) => self.components.add(Some(name)),
            ComponentTypeRef::Module(_) => self.core_modules.add(Some(name)),
            ComponentTypeRef::Value(_) => self.values.add(Some(name)),
        };
        self.imports().import(name, ty);
        ret
    }

    /// Exports a new item from this component with the `name` and `kind`
    /// specified.
    ///
    /// The `idx` is the item to export and the `ty` is an optional type to
    /// ascribe to the export.
    pub fn export(
        &mut self,
        name: &str,
        kind: ComponentExportKind,
        idx: u32,
        ty: Option<ComponentTypeRef>,
    ) -> u32 {
        self.exports().export(name, kind, idx, ty);
        self.inc_kind(Some(name), kind)
    }

    /// Creates a new encoder for the next core type in this component.
    pub fn core_type(&mut self, debug_name: Option<&str>) -> (u32, ComponentCoreTypeEncoder<'_>) {
        (self.core_types.add(debug_name), self.core_types().ty())
    }

    /// Creates a new encoder for the next type in this component.
    pub fn ty(&mut self, debug_name: Option<&str>) -> (u32, ComponentTypeEncoder<'_>) {
        (self.types.add(debug_name), self.types().ty())
    }

    /// Creates a new instance type within this component.
    pub fn type_instance(&mut self, debug_name: Option<&str>, ty: &InstanceType) -> u32 {
        self.types().instance(ty);
        self.types.add(debug_name)
    }

    /// Creates a new component type within this component.
    pub fn type_component(&mut self, debug_name: Option<&str>, ty: &ComponentType) -> u32 {
        self.types().component(ty);
        self.types.add(debug_name)
    }

    /// Creates a new defined component type within this component.
    pub fn type_defined(
        &mut self,
        debug_name: Option<&str>,
    ) -> (u32, ComponentDefinedTypeEncoder<'_>) {
        (self.types.add(debug_name), self.types().defined_type())
    }

    /// Creates a new component function type within this component.
    pub fn type_function(
        &mut self,
        debug_name: Option<&str>,
    ) -> (u32, ComponentFuncTypeEncoder<'_>) {
        (self.types.add(debug_name), self.types().function())
    }

    /// Declares a new resource type within this component.
    pub fn type_resource(
        &mut self,
        debug_name: Option<&str>,
        rep: ValType,
        dtor: Option<u32>,
    ) -> u32 {
        self.types().resource(rep, dtor);
        self.types.add(debug_name)
    }

    /// Defines a new subcomponent of this component.
    pub fn component(&mut self, debug_name: Option<&str>, mut builder: ComponentBuilder) -> u32 {
        builder.flush();
        self.flush();
        self.component
            .section(&NestedComponentSection(&builder.component));
        self.components.add(debug_name)
    }

    /// Defines a new subcomponent of this component.
    pub fn component_raw(&mut self, debug_name: Option<&str>, data: &[u8]) -> u32 {
        let raw_section = RawSection {
            id: ComponentSectionId::Component.into(),
            data,
        };
        self.flush();
        self.component.section(&raw_section);
        self.components.add(debug_name)
    }

    /// Instantiates the `component_index` specified with the `args` specified.
    pub fn instantiate<A, S>(
        &mut self,
        debug_name: Option<&str>,
        component_index: u32,
        args: A,
    ) -> u32
    where
        A: IntoIterator<Item = (S, ComponentExportKind, u32)>,
        A::IntoIter: ExactSizeIterator,
        S: AsRef<str>,
    {
        self.component_instances()
            .instantiate(component_index, args);
        self.instances.add(debug_name)
    }

    /// Declares a new `resource.drop` intrinsic.
    pub fn resource_drop(&mut self, ty: u32) -> u32 {
        self.canonical_functions().resource_drop(ty);
        self.core_funcs.add(Some("resource.drop"))
    }

    /// Declares a new `resource.drop` intrinsic.
    pub fn resource_drop_async(&mut self, ty: u32) -> u32 {
        self.canonical_functions().resource_drop_async(ty);
        self.core_funcs.add(Some("resource.drop async"))
    }

    /// Declares a new `resource.new` intrinsic.
    pub fn resource_new(&mut self, ty: u32) -> u32 {
        self.canonical_functions().resource_new(ty);
        self.core_funcs.add(Some("resource.new"))
    }

    /// Declares a new `resource.rep` intrinsic.
    pub fn resource_rep(&mut self, ty: u32) -> u32 {
        self.canonical_functions().resource_rep(ty);
        self.core_funcs.add(Some("resource.rep"))
    }

    /// Declares a new `thread.spawn_ref` intrinsic.
    pub fn thread_spawn_ref(&mut self, ty: u32) -> u32 {
        self.canonical_functions().thread_spawn_ref(ty);
        self.core_funcs.add(Some("thread.spawn-ref"))
    }

    /// Declares a new `thread.available_parallelism` intrinsic.
    pub fn thread_available_parallelism(&mut self) -> u32 {
        self.canonical_functions().thread_available_parallelism();
        self.core_funcs.add(Some("thread.available-parallelism"))
    }

    /// Declares a new `backpressure.inc` intrinsic.
    pub fn backpressure_inc(&mut self) -> u32 {
        self.canonical_functions().backpressure_inc();
        self.core_funcs.add(Some("backpressure.inc"))
    }

    /// Declares a new `backpressure.dec` intrinsic.
    pub fn backpressure_dec(&mut self) -> u32 {
        self.canonical_functions().backpressure_dec();
        self.core_funcs.add(Some("backpressure.dec"))
    }

    /// Declares a new `task.return` intrinsic.
    pub fn task_return<O>(&mut self, ty: Option<ComponentValType>, options: O) -> u32
    where
        O: IntoIterator<Item = CanonicalOption>,
        O::IntoIter: ExactSizeIterator,
    {
        self.canonical_functions().task_return(ty, options);
        self.core_funcs.add(Some("task.return"))
    }

    /// Declares a new `task.cancel` intrinsic.
    pub fn task_cancel(&mut self) -> u32 {
        self.canonical_functions().task_cancel();
        self.core_funcs.add(Some("task.cancel"))
    }

    /// Declares a new `context.get` intrinsic.
    pub fn context_get(&mut self, ty: ValType, i: u32) -> u32 {
        self.canonical_functions().context_get(ty, i);
        self.core_funcs.add(Some(&format!("context.get {i}")))
    }

    /// Declares a new `context.set` intrinsic.
    pub fn context_set(&mut self, ty: ValType, i: u32) -> u32 {
        self.canonical_functions().context_set(ty, i);
        self.core_funcs.add(Some(&format!("context.set {i}")))
    }

    /// Declares a new `thread.yield` intrinsic.
    pub fn thread_yield(&mut self, cancellable: bool) -> u32 {
        self.canonical_functions().thread_yield(cancellable);
        self.core_funcs.add(Some("thread.yield"))
    }

    /// Declares a new `subtask.drop` intrinsic.
    pub fn subtask_drop(&mut self) -> u32 {
        self.canonical_functions().subtask_drop();
        self.core_funcs.add(Some("subtask.drop"))
    }

    /// Declares a new `subtask.cancel` intrinsic.
    pub fn subtask_cancel(&mut self, async_: bool) -> u32 {
        self.canonical_functions().subtask_cancel(async_);
        self.core_funcs.add(Some("subtask.cancel"))
    }

    /// Declares a new `stream.new` intrinsic.
    pub fn stream_new(&mut self, ty: u32) -> u32 {
        self.canonical_functions().stream_new(ty);
        self.core_funcs.add(Some("stream.new"))
    }

    /// Declares a new `stream.read` intrinsic.
    pub fn stream_read<O>(&mut self, ty: u32, options: O) -> u32
    where
        O: IntoIterator<Item = CanonicalOption>,
        O::IntoIter: ExactSizeIterator,
    {
        self.canonical_functions().stream_read(ty, options);
        self.core_funcs.add(Some("stream.read"))
    }

    /// Declares a new `stream.write` intrinsic.
    pub fn stream_write<O>(&mut self, ty: u32, options: O) -> u32
    where
        O: IntoIterator<Item = CanonicalOption>,
        O::IntoIter: ExactSizeIterator,
    {
        self.canonical_functions().stream_write(ty, options);
        self.core_funcs.add(Some("stream.write"))
    }

    /// Declares a new `stream.cancel-read` intrinsic.
    pub fn stream_cancel_read(&mut self, ty: u32, async_: bool) -> u32 {
        self.canonical_functions().stream_cancel_read(ty, async_);
        self.core_funcs.add(Some("stream.cancel-read"))
    }

    /// Declares a new `stream.cancel-write` intrinsic.
    pub fn stream_cancel_write(&mut self, ty: u32, async_: bool) -> u32 {
        self.canonical_functions().stream_cancel_write(ty, async_);
        self.core_funcs.add(Some("stream.cancel-write"))
    }

    /// Declares a new `stream.drop-readable` intrinsic.
    pub fn stream_drop_readable(&mut self, ty: u32) -> u32 {
        self.canonical_functions().stream_drop_readable(ty);
        self.core_funcs.add(Some("stream.drop-readable"))
    }

    /// Declares a new `stream.drop-writable` intrinsic.
    pub fn stream_drop_writable(&mut self, ty: u32) -> u32 {
        self.canonical_functions().stream_drop_writable(ty);
        self.core_funcs.add(Some("stream.drop-writable"))
    }

    /// Declares a new `future.new` intrinsic.
    pub fn future_new(&mut self, ty: u32) -> u32 {
        self.canonical_functions().future_new(ty);
        self.core_funcs.add(Some("future.new"))
    }

    /// Declares a new `future.read` intrinsic.
    pub fn future_read<O>(&mut self, ty: u32, options: O) -> u32
    where
        O: IntoIterator<Item = CanonicalOption>,
        O::IntoIter: ExactSizeIterator,
    {
        self.canonical_functions().future_read(ty, options);
        self.core_funcs.add(Some("future.read"))
    }

    /// Declares a new `future.write` intrinsic.
    pub fn future_write<O>(&mut self, ty: u32, options: O) -> u32
    where
        O: IntoIterator<Item = CanonicalOption>,
        O::IntoIter: ExactSizeIterator,
    {
        self.canonical_functions().future_write(ty, options);
        self.core_funcs.add(Some("future.write"))
    }

    /// Declares a new `future.cancel-read` intrinsic.
    pub fn future_cancel_read(&mut self, ty: u32, async_: bool) -> u32 {
        self.canonical_functions().future_cancel_read(ty, async_);
        self.core_funcs.add(Some("future.cancel-read"))
    }

    /// Declares a new `future.cancel-write` intrinsic.
    pub fn future_cancel_write(&mut self, ty: u32, async_: bool) -> u32 {
        self.canonical_functions().future_cancel_write(ty, async_);
        self.core_funcs.add(Some("future.cancel-write"))
    }

    /// Declares a new `future.drop-readable` intrinsic.
    pub fn future_drop_readable(&mut self, ty: u32) -> u32 {
        self.canonical_functions().future_drop_readable(ty);
        self.core_funcs.add(Some("future.drop-readable"))
    }

    /// Declares a new `future.drop-writable` intrinsic.
    pub fn future_drop_writable(&mut self, ty: u32) -> u32 {
        self.canonical_functions().future_drop_writable(ty);
        self.core_funcs.add(Some("future.drop-writable"))
    }

    /// Declares a new `error-context.new` intrinsic.
    pub fn error_context_new<O>(&mut self, options: O) -> u32
    where
        O: IntoIterator<Item = CanonicalOption>,
        O::IntoIter: ExactSizeIterator,
    {
        self.canonical_functions().error_context_new(options);
        self.core_funcs.add(Some("error-context.new"))
    }

    /// Declares a new `error-context.debug-message` intrinsic.
    pub fn error_context_debug_message<O>(&mut self, options: O) -> u32
    where
        O: IntoIterator<Item = CanonicalOption>,
        O::IntoIter: ExactSizeIterator,
    {
        self.canonical_functions()
            .error_context_debug_message(options);
        self.core_funcs.add(Some("error-context.debug-message"))
    }

    /// Declares a new `error-context.drop` intrinsic.
    pub fn error_context_drop(&mut self) -> u32 {
        self.canonical_functions().error_context_drop();
        self.core_funcs.add(Some("error-context.drop"))
    }

    /// Declares a new `waitable-set.new` intrinsic.
    pub fn waitable_set_new(&mut self) -> u32 {
        self.canonical_functions().waitable_set_new();
        self.core_funcs.add(Some("waitable-set.new"))
    }

    /// Declares a new `waitable-set.wait` intrinsic.
    pub fn waitable_set_wait(&mut self, cancellable: bool, memory: u32) -> u32 {
        self.canonical_functions()
            .waitable_set_wait(cancellable, memory);
        self.core_funcs.add(Some("waitable-set.wait"))
    }

    /// Declares a new `waitable-set.poll` intrinsic.
    pub fn waitable_set_poll(&mut self, cancellable: bool, memory: u32) -> u32 {
        self.canonical_functions()
            .waitable_set_poll(cancellable, memory);
        self.core_funcs.add(Some("waitable-set.poll"))
    }

    /// Declares a new `waitable-set.drop` intrinsic.
    pub fn waitable_set_drop(&mut self) -> u32 {
        self.canonical_functions().waitable_set_drop();
        self.core_funcs.add(Some("waitable-set.drop"))
    }

    /// Declares a new `waitable.join` intrinsic.
    pub fn waitable_join(&mut self) -> u32 {
        self.canonical_functions().waitable_join();
        self.core_funcs.add(Some("waitable.join"))
    }

    /// Declares a new `thread.index` intrinsic.
    pub fn thread_index(&mut self) -> u32 {
        self.canonical_functions().thread_index();
        self.core_funcs.add(Some("thread.index"))
    }

    /// Declares a new `thread.new-indirect` intrinsic.
    pub fn thread_new_indirect(&mut self, func_ty_idx: u32, table_index: u32) -> u32 {
        self.canonical_functions()
            .thread_new_indirect(func_ty_idx, table_index);
        self.core_funcs.add(Some("thread.new-indirect"))
    }

    /// Declares a new `thread.suspend-to-suspended` intrinsic.
    pub fn thread_suspend_to_suspended(&mut self, cancellable: bool) -> u32 {
        self.canonical_functions()
            .thread_suspend_to_suspended(cancellable);
        self.core_funcs.add(Some("thread.suspend-to-suspended"))
    }

    /// Declares a new `thread.suspend-to` intrinsic.
    pub fn thread_suspend_to(&mut self, cancellable: bool) -> u32 {
        self.canonical_functions().thread_suspend_to(cancellable);
        self.core_funcs.add(Some("thread.suspend-to"))
    }

    /// Declares a new `thread.suspend` intrinsic.
    pub fn thread_suspend(&mut self, cancellable: bool) -> u32 {
        self.canonical_functions().thread_suspend(cancellable);
        self.core_funcs.add(Some("thread.suspend"))
    }

    /// Declares a new `thread.unsuspend` intrinsic.
    pub fn thread_unsuspend(&mut self) -> u32 {
        self.canonical_functions().thread_unsuspend();
        self.core_funcs.add(Some("thread.unsuspend"))
    }

    /// Declares a new `thread.yield-to-suspended` intrinsic.
    pub fn thread_yield_to_suspended(&mut self, cancellable: bool) -> u32 {
        self.canonical_functions()
            .thread_yield_to_suspended(cancellable);
        self.core_funcs.add(Some("thread.yield-to-suspended"))
    }

    /// Adds a new custom section to this component.
    pub fn custom_section(&mut self, section: &CustomSection<'_>) {
        self.flush();
        self.component.section(section);
    }

    /// Adds a new custom section to this component.
    pub fn raw_custom_section(&mut self, section: &[u8]) {
        self.flush();
        self.component.section(&RawCustomSection(section));
    }
}

// Helper macro to generate methods on `ComponentBuilder` to get specific
// section encoders that automatically flush and write out prior sections as
// necessary.
macro_rules! section_accessors {
    ($($method:ident => $section:ident)*) => (
        #[derive(Debug, Default)]
        enum LastSection {
            #[default]
            None,
            $($section($section),)*
        }

        impl ComponentBuilder {
            $(
                fn $method(&mut self) -> &mut $section {
                    match &self.last_section {
                        // The last encoded section matches the section that's
                        // being requested, so no change is necessary.
                        LastSection::$section(_) => {}

                        // Otherwise the last section didn't match this section,
                        // so flush any prior section if needed and start
                        // encoding the desired section of this method.
                        _ => {
                            self.flush();
                            self.last_section = LastSection::$section($section::new());
                        }
                    }
                    match &mut self.last_section {
                        LastSection::$section(ret) => ret,
                        _ => unreachable!()
                    }
                }
            )*

            /// Writes out the last section into the final component binary if
            /// there is a section specified, otherwise does nothing.
            fn flush(&mut self) {
                match mem::take(&mut self.last_section) {
                    LastSection::None => {}
                    $(
                        LastSection::$section(section) => {
                            self.component.section(&section);
                        }
                    )*
                }
            }

        }
    )
}

section_accessors! {
    component_instances => ComponentInstanceSection
    instances => InstanceSection
    canonical_functions => CanonicalFunctionSection
    aliases => ComponentAliasSection
    exports => ComponentExportSection
    imports => ComponentImportSection
    types => ComponentTypeSection
    core_types => CoreTypeSection
}

#[derive(Debug, Default)]
struct Namespace {
    count: u32,
    names: NameMap,
}

impl Namespace {
    fn add(&mut self, name: Option<&str>) -> u32 {
        let ret = self.count;
        self.count += 1;
        if let Some(name) = name {
            self.names.append(ret, name);
        }
        ret
    }
}
