package experimental_test

import (
	"bytes"
	"context"
	_ "embed"
	"fmt"
	"log"

	"github.com/tetratelabs/wazero"
	"github.com/tetratelabs/wazero/api"
	"github.com/tetratelabs/wazero/experimental"
	"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
)

var (
	// These wasm files were generated by the following:
	// cd testdata
	// wat2wasm --debug-names inoutdispatcher.wat
	// wat2wasm --debug-names inoutdispatcherclient.wat

	//go:embed testdata/inoutdispatcher.wasm
	inoutdispatcherWasm []byte
	//go:embed testdata/inoutdispatcherclient.wasm
	inoutdispatcherclientWasm []byte
)

func Example_importResolver() {
	ctx := context.Background()

	r := wazero.NewRuntime(ctx)
	defer r.Close(ctx)

	// The client imports the inoutdispatcher module that reads from stdin and writes to stdout.
	// This means that we need multiple instances of the inoutdispatcher module to have different stdin/stdout.
	// This example demonstrates a way to do that.
	type mod struct {
		in  bytes.Buffer
		out bytes.Buffer

		client api.Module
	}

	wasi_snapshot_preview1.MustInstantiate(ctx, r)

	idm, err := r.CompileModule(ctx, inoutdispatcherWasm)
	if err != nil {
		log.Panicln(err)
	}
	idcm, err := r.CompileModule(ctx, inoutdispatcherclientWasm)
	if err != nil {
		log.Panicln(err)
	}

	const numInstances = 3
	mods := make([]*mod, numInstances)
	for i := range mods {
		mods[i] = &mod{}
		m := mods[i]

		const inoutDispatcherModuleName = "inoutdispatcher"

		dispatcherInstance, err := r.InstantiateModule(ctx, idm,
			wazero.NewModuleConfig().
				WithStdin(&m.in).
				WithStdout(&m.out).
				WithName("")) // Makes it an anonymous module.
		if err != nil {
			log.Panicln(err)
		}

		ctx = experimental.WithImportResolver(ctx, func(name string) api.Module {
			if name == inoutDispatcherModuleName {
				return dispatcherInstance
			}
			return nil
		})

		m.client, err = r.InstantiateModule(ctx, idcm, wazero.NewModuleConfig().WithName(fmt.Sprintf("m%d", i)))
		if err != nil {
			log.Panicln(err)
		}

	}

	for i, m := range mods {
		m.in.WriteString(fmt.Sprintf("Module instance #%d", i))
		_, err := m.client.ExportedFunction("dispatch").Call(ctx)
		if err != nil {
			log.Panicln(err)
		}
	}

	for i, m := range mods {
		fmt.Printf("out%d: %s\n", i, m.out.String())
	}

	// Output:
	// out0: Module instance #0
	// out1: Module instance #1
	// out2: Module instance #2
}
