package Wasm::Wasmtime::Instance;

use strict;
use warnings;
use Wasm::Wasmtime::FFI;
use Wasm::Wasmtime::Module;
use Wasm::Wasmtime::Extern;
use Wasm::Wasmtime::Trap;

# ABSTRACT: Wasmtime instance class
our $VERSION = '0.01'; # VERSION


$ffi_prefix = 'wasm_instance_';
$ffi->type('opaque' => 'wasm_instance_t');


$ffi->attach( new => ['wasm_store_t','wasm_module_t','wasm_extern_t[]','opaque*'] => 'wasm_engine_t' => sub {
  my($xsub, $class, $module, $imports) = @_;
  my @imports = defined $imports ? map { $_->{ptr} } @$imports : ();
  my $trap;

  # TODO: we don't have an interface to module imports yet, but when
  # we do we should validate that the module and instance imports match
  # otherwise SEGV

  my $ptr = $xsub->($module->store->{ptr}, $module->{ptr}, \@imports, \$trap);
  if($ptr)
  {
    return bless {
      ptr    => $ptr,
      module => $module,
    }, $class;
  }
  else
  {
    if($trap)
    {
      $trap = Wasm::Wasmtime::Trap->new($trap);
      Carp::croak($trap->message);
    }
    Carp::croak("error creating Wasm::Wasmtime::Instance ");
  }
});


sub get_export
{
  my($self, $name) = @_;
  $self->{exports} ||= do {
    my @exports = $self->exports;
    my @module_exports   = $self->module->exports;
    my %exports;
    foreach my $i (0..$#exports)
    {
      $exports{$module_exports[$i]->name} = $exports[$i];
    }
    \%exports;
  };
  $self->{exports}->{$name};
}


sub module { shift->{module} }


$ffi->attach( exports => ['wasm_instance_t','wasm_extern_vec_t*'] => sub {
  my($xsub, $self) = @_;
  my $externs = Wasm::Wasmtime::ExternVec->new;
  $xsub->($self->{ptr}, $externs);
  $externs->to_list;
});

$ffi->attach( [ 'delete' => 'DESTROY' ] => ['wasm_engine_t'] => sub {
  my($xsub, $self) = @_;
  $xsub->($self->{ptr}) if $self->{ptr};
});

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Wasm::Wasmtime::Instance - Wasmtime instance class

=head1 VERSION

version 0.01

=head1 SYNOPSIS

 use Wasm::Wasmtime;
 
 my $module = Wasm::Wasmtime::Module->new(wat => '(module)');
 my $instance = Wasm::Wasmtime::Instance->new($module, []);

=head1 DESCRIPTION

B<WARNING>: WebAssembly and Wasmtime are a moving target and the interface for these modules
is under active development.  Use with caution.

This class represents an instance of a WebAssembly module L<Wasm::Wasmtime::Module>.

=head1 CONSTRUCTOR

=head2 new

 my $instance = Wasm::Wasmtime::Instance->new(
   $module     # Wasm::Wasmtime::Module
 );
 my $instance = Wasm::Wasmtime::Instance->new(
   $module,    # Wasm::Wasmtime::Module
   \@imports,  # array reference of Wasm::Wasmtime::Extern
 );

Create a new instance of the instance class.

=head1 METHODS

=head2 get_export

 my $extern = $instance->get_export($name);

Returns a L<Wasm::Wasmtime::Extern> object with the given C<$name>.
If no such object exists, then C<undef> will be returned.

Extern objects represent functions, globals, tables or memory in WebAssembly.

=head2 module

 my $module = $instance->module;

Returns the L<Wasm::Wasmtime::Module> for this instance.

=head2 exports

 my @externs = $instance->exports;

Returns a list of L<Wasm::Wasmtime::Extern> objects for the functions,
globals, tables and memory exported by the WebAssembly instance.

=head1 AUTHOR

Graham Ollis <plicease@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2020 by Graham Ollis.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
