package wasm

import (
	
	
	
	
	
	

	
)

type paramsKind byte

const (
	paramsKindNoContext paramsKind = iota
	paramsKindContext
	paramsKindContextModule
)

// Below are reflection code to get the interface type used to parse functions and set values.

var (
	moduleType    = reflect.TypeOf((*api.Module)(nil)).Elem()
	goContextType = reflect.TypeOf((*context.Context)(nil)).Elem()
	errorType     = reflect.TypeOf((*error)(nil)).Elem()
)

// compile-time check to ensure reflectGoModuleFunction implements
// api.GoModuleFunction.
var _ api.GoModuleFunction = (*reflectGoModuleFunction)(nil)

type reflectGoModuleFunction struct {
	fn              *reflect.Value
	params, results []ValueType
}

// Call implements the same method as documented on api.GoModuleFunction.
func ( *reflectGoModuleFunction) ( context.Context,  api.Module,  []uint64) {
	callGoFunc(, , .fn, )
}

// EqualTo is exposed for testing.
func ( *reflectGoModuleFunction) ( interface{}) bool {
	if ,  := .(*reflectGoModuleFunction); ! {
		return false
	} else {
		// TODO compare reflect pointers
		return bytes.Equal(.params, .params) && bytes.Equal(.results, .results)
	}
}

// compile-time check to ensure reflectGoFunction implements api.GoFunction.
var _ api.GoFunction = (*reflectGoFunction)(nil)

type reflectGoFunction struct {
	fn              *reflect.Value
	pk              paramsKind
	params, results []ValueType
}

// EqualTo is exposed for testing.
func ( *reflectGoFunction) ( interface{}) bool {
	if ,  := .(*reflectGoFunction); ! {
		return false
	} else {
		// TODO compare reflect pointers
		return .pk == .pk &&
			bytes.Equal(.params, .params) && bytes.Equal(.results, .results)
	}
}

// Call implements the same method as documented on api.GoFunction.
func ( *reflectGoFunction) ( context.Context,  []uint64) {
	if .pk == paramsKindNoContext {
		 = nil
	}
	callGoFunc(, nil, .fn, )
}

// callGoFunc executes the reflective function by converting params to Go
// types. The results of the function call are converted back to api.ValueType.
func callGoFunc( context.Context,  api.Module,  *reflect.Value,  []uint64) {
	 := .Type()

	var  []reflect.Value
	 := .NumIn()
	if  != 0 {
		 = make([]reflect.Value, )

		 := 0
		if  != nil {
			[0] = newContextVal()
			++
		}
		if  != nil {
			[1] = newModuleVal()
			++
		}

		for  := 0;  < ; ++ {
			 := .In()
			 := reflect.New().Elem()
			 := .Kind()
			 := []
			++

			switch  {
			case reflect.Float32:
				.SetFloat(float64(math.Float32frombits(uint32())))
			case reflect.Float64:
				.SetFloat(math.Float64frombits())
			case reflect.Uint32, reflect.Uint64, reflect.Uintptr:
				.SetUint()
			case reflect.Int32, reflect.Int64:
				.SetInt(int64())
			default:
				panic(fmt.Errorf("BUG: param[%d] has an invalid type: %v", , ))
			}
			[] = 
		}
	}

	// Execute the host function and push back the call result onto the stack.
	for ,  := range .Call() {
		switch .Kind() {
		case reflect.Float32:
			[] = uint64(math.Float32bits(float32(.Float())))
		case reflect.Float64:
			[] = math.Float64bits(.Float())
		case reflect.Uint32, reflect.Uint64, reflect.Uintptr:
			[] = .Uint()
		case reflect.Int32, reflect.Int64:
			[] = uint64(.Int())
		default:
			panic(fmt.Errorf("BUG: result[%d] has an invalid type: %v", , .Kind()))
		}
	}
}

func newContextVal( context.Context) reflect.Value {
	 := reflect.New(goContextType).Elem()
	.Set(reflect.ValueOf())
	return 
}

func newModuleVal( api.Module) reflect.Value {
	 := reflect.New(moduleType).Elem()
	.Set(reflect.ValueOf())
	return 
}

// MustParseGoReflectFuncCode parses Code from the go function or panics.
//
// Exposing this simplifies FunctionDefinition of host functions in built-in host
// modules and tests.
func ( interface{}) Code {
	, , ,  := parseGoReflectFunc()
	if  != nil {
		panic()
	}
	return 
}

func parseGoReflectFunc( interface{}) (,  []ValueType,  Code,  error) {
	 := reflect.ValueOf()
	 := .Type()

	if .Kind() != reflect.Func {
		 = fmt.Errorf("kind != func: %s", .Kind().String())
		return
	}

	,  := kind()
	if  != nil {
		 = 
		return
	}

	 := 0
	switch  {
	case paramsKindNoContext:
	case paramsKindContext:
		 = 1
	case paramsKindContextModule:
		 = 2
	}

	 := .NumIn() - 
	if  > 0 {
		 = make([]ValueType, )
	}
	for  := 0;  < len(); ++ {
		 := .In( + )
		if ,  := getTypeOf(.Kind());  {
			[] = 
			continue
		}

		// Now, we will definitely err, decide which message is best
		var  reflect.Type
		if  := .Implements(moduleType);  {
			 = moduleType
		} else if  := .Implements(goContextType);  {
			 = goContextType
		}

		if  != nil {
			 = fmt.Errorf("param[%d] is a %s, which may be defined only once as param[0]", +, )
		} else {
			 = fmt.Errorf("param[%d] is unsupported: %s", +, .Kind())
		}
		return
	}

	 := .NumOut()
	if  > 0 {
		 = make([]ValueType, )
	}
	for  := 0;  < len(); ++ {
		 := .Out()
		if ,  := getTypeOf(.Kind());  {
			[] = 
			continue
		}

		// Now, we will definitely err, decide which message is best
		if .Implements(errorType) {
			 = fmt.Errorf("result[%d] is an error, which is unsupported", )
		} else {
			 = fmt.Errorf("result[%d] is unsupported: %s", , .Kind())
		}
		return
	}

	 = Code{}
	if  == paramsKindContextModule {
		.GoFunc = &reflectGoModuleFunction{fn: &, params: , results: }
	} else {
		.GoFunc = &reflectGoFunction{pk: , fn: &, params: , results: }
	}
	return
}

func kind( reflect.Type) (paramsKind, error) {
	 := .NumIn()
	if  > 0 && .In(0).Kind() == reflect.Interface {
		 := .In(0)
		if .Implements(moduleType) {
			return 0, errors.New("invalid signature: api.Module parameter must be preceded by context.Context")
		} else if .Implements(goContextType) {
			if  >= 2 && .In(1).Implements(moduleType) {
				return paramsKindContextModule, nil
			}
			return paramsKindContext, nil
		}
	}
	// Without context param allows portability with reflective runtimes.
	// This allows people to more easily port to wazero.
	return paramsKindNoContext, nil
}

func getTypeOf( reflect.Kind) (ValueType, bool) {
	switch  {
	case reflect.Float64:
		return ValueTypeF64, true
	case reflect.Float32:
		return ValueTypeF32, true
	case reflect.Int32, reflect.Uint32:
		return ValueTypeI32, true
	case reflect.Int64, reflect.Uint64:
		return ValueTypeI64, true
	case reflect.Uintptr:
		return ValueTypeExternref, true
	default:
		return 0x00, false
	}
}