from dataclasses import dataclass
from exports.bindings import Exports
from imports.bindings import add_imports_to_linker, Imports
from typing import Tuple, List
import exports.bindings as e
import imports.bindings as i
import sys
import wasmtime

@dataclass
class HostState(i.HostState):
    val: int

    def __init__(self, val: int) -> None:
        self.val = val


HOST_STATE2_CLOSED = False


@dataclass
class HostState2(i.HostState2):
    val: int

    def __init__(self, val: int) -> None:
        self.val = val

    def drop(self) -> None:
        global HOST_STATE2_CLOSED
        HOST_STATE2_CLOSED = True


@dataclass
class Markdown(i.Markdown2):
    buf: str = ''

    def append(self, data: str) -> None:
        self.buf += data

    def render(self) -> str:
        return self.buf.replace('red', 'green')


class OddName(i.OddName):
    def frob_the_odd(self) -> None:
        pass


class MyImports:
    def host_state_create(self) -> i.HostState:
        return HostState(100)

    def host_state_get(self, a: i.HostState) -> int:
        assert(isinstance(a, HostState))
        return a.val

    def host_state2_create(self) -> i.HostState2:
        return HostState2(101)

    def host_state2_saw_close(self) -> bool:
        return HOST_STATE2_CLOSED

    def two_host_states(self, a: i.HostState, b: i.HostState2) -> Tuple[i.HostState, i.HostState2]:
        return (b, a)

    def host_state2_param_record(self, a: i.HostStateParamRecord) -> None:
        pass

    def host_state2_param_tuple(self, a: i.HostStateParamTuple) -> None:
        pass

    def host_state2_param_option(self, a: i.HostStateParamOption) -> None:
        pass

    def host_state2_param_result(self, a: i.HostStateParamResult) -> None:
        pass

    def host_state2_param_variant(self, a: i.HostStateParamVariant) -> None:
        pass

    def host_state2_param_list(self, a: List[i.HostState2]) -> None:
        pass

    def host_state2_result_record(self) -> i.HostStateResultRecord:
        return i.HostStateResultRecord(HostState(2))

    def host_state2_result_tuple(self) -> i.HostStateResultTuple:
        return (HostState(2),)

    def host_state2_result_option(self) -> i.HostStateResultOption:
        return HostState(2)

    def host_state2_result_result(self) -> i.HostStateResultResult:
        return i.Ok(HostState2(2))

    def host_state2_result_variant(self) -> i.HostStateResultVariant:
        return HostState2(2)

    def host_state2_result_list(self) -> List[i.HostState2]:
        return [HostState2(2), HostState2(5)]

    def markdown2_create(self) -> i.Markdown2:
        return Markdown()

    def odd_name_create(self) -> i.OddName:
        return OddName()

def run(wasm_file: str) -> None:
    store = wasmtime.Store()
    module = wasmtime.Module.from_file(store.engine, wasm_file)
    linker = wasmtime.Linker(store.engine)
    linker.define_wasi()
    wasi = wasmtime.WasiConfig()
    wasi.inherit_stdout()
    wasi.inherit_stderr()
    store.set_wasi(wasi)

    imports = MyImports()
    add_imports_to_linker(linker, store, imports)
    wasm = Exports(store, linker, module)

    wasm.test_imports(store)

    # Param/result of a handle works in a simple fashion
    s: e.WasmState = wasm.wasm_state_create(store)
    assert(wasm.wasm_state_get_val(store, s) == 100)

    # Deterministic destruction is possible
    assert(wasm.wasm_state2_saw_close(store) == False)
    s2: e.WasmState2 = wasm.wasm_state2_create(store)
    assert(wasm.wasm_state2_saw_close(store) == False)
    s2.drop(store)
    assert(wasm.wasm_state2_saw_close(store) == True)

    arg1 = wasm.wasm_state_create(store)
    arg2 = wasm.wasm_state2_create(store)
    c, d = wasm.two_wasm_states(store, arg1, arg2)
    arg1.drop(store)
    arg2.drop(store)

    wasm.wasm_state2_param_record(store, e.WasmStateParamRecord(d))
    wasm.wasm_state2_param_tuple(store, (d,))
    wasm.wasm_state2_param_option(store, d)
    wasm.wasm_state2_param_option(store, None)
    wasm.wasm_state2_param_result(store, e.Ok(d))
    wasm.wasm_state2_param_result(store, e.Err(2))
    wasm.wasm_state2_param_variant(store, d)
    wasm.wasm_state2_param_variant(store, 2)
    wasm.wasm_state2_param_list(store, [])
    wasm.wasm_state2_param_list(store, [d])
    wasm.wasm_state2_param_list(store, [d, d])

    c.drop(store)
    d.drop(store)

    wasm.wasm_state2_result_record(store).a.drop(store)
    wasm.wasm_state2_result_tuple(store)[0].drop(store)
    opt = wasm.wasm_state2_result_option(store)
    assert(opt is not None)
    opt.drop(store)
    result = wasm.wasm_state2_result_result(store)
    assert(isinstance(result, e.Ok))
    result.value.drop(store)
    variant = wasm.wasm_state2_result_variant(store)
    print(variant)
    assert(isinstance(variant, e.WasmState2))
    variant.drop(store)
    for val in wasm.wasm_state2_result_list(store):
        val.drop(store)

    s.drop(store)

    md = e.Markdown.create(store, wasm)
    if md:
        md.append(store, "red is the best color")
        assert(md.render(store) == "green is the best color")
        md.drop(store)

if __name__ == '__main__':
    run(sys.argv[1])
