// Package interceptor 提供了动态加载和调用共享库函数的功能。
// 该包使用purego库实现跨平台的动态库加载，支持Windows的DLL和Linux的SO文件。
package interceptor

import (
	"fmt"
	"path/filepath"
	"reflect"
	"strings"

	"github.com/ebitengine/purego"
)

// funcType 定义了函数的参数类型和返回值类型。
// 用于在动态加载库函数时指定函数签名。
type funcType struct {
	ParamTypes  []string
	ReturnTypes []string
}

// typeMap 将字符串类型名映射到对应的Go reflect.Type。
// 支持基本数据类型如int、float、string等，以及特殊的"void"类型表示无返回值。
var typeMap = map[string]reflect.Type{
	"int":     reflect.TypeOf(int(0)),
	"int32":   reflect.TypeOf(int32(0)),
	"int64":   reflect.TypeOf(int64(0)),
	"uint":    reflect.TypeOf(uint(0)),
	"float32": reflect.TypeOf(float32(0)),
	"float64": reflect.TypeOf(float64(0)),
	"string":  reflect.TypeOf(""),
	"bool":    reflect.TypeOf(false),
	"void":    nil, // 用于表示无返回值
}

// makeFuncType 根据给定的参数类型和返回值类型字符串列表，创建对应的函数类型。
// paramTypes: 函数参数类型的字符串列表
// returnTypes: 函数返回值类型的字符串列表
// 返回: 创建的函数类型和可能的错误
func makeFuncType(paramTypes, returnTypes []string) (reflect.Type, error) {
	var in, out []reflect.Type

	// 参数类型
	for _, t := range paramTypes {
		rt, ok := typeMap[t]
		if !ok {
			return nil, fmt.Errorf("unknown parameter type: %s", t)
		}
		in = append(in, rt)
	}

	// 返回值类型
	for _, t := range returnTypes {
		if t == "void" {
			continue // 无返回值
		}
		rt, ok := typeMap[t]
		if !ok {
			return nil, fmt.Errorf("unknown return type: %s", t)
		}
		out = append(out, rt)
	}

	return reflect.FuncOf(in, out, false), nil
}

// loadLibrary 加载指定路径的动态库文件，并根据提供的函数映射表注册库中的函数。
// 如果库已经被加载过，则直接返回之前加载的实例。
// libPath: 动态库文件的路径
// libFuncMap: 库中函数名到函数类型的映射表
// 返回: 包含所有注册函数的接口实例
func (a *Ankointerceptor) loadLibrary(libPath string, libFuncMap map[string]funcType) interface{} {
	libName := filepath.Base(libPath)
	libName = strings.TrimSuffix(libName, filepath.Ext(".dll"))
	// 是不是so文件？libName里边是否包含.so
	if strings.Contains(libName, ".so") {
		soIndex := strings.Index(libName, ".so")
		libName = libName[0:soIndex]

	}
	libName = strings.TrimPrefix(libName, "lib")
	if _, ok := a.libMap[libName]; ok {
		return a.libMap[libName] // 如果已经加载过，直接返回
	}
	lib, err := purego.Dlopen(libPath, purego.RTLD_LAZY)
	if err != nil {
		panic("Load library error")
	}
	a.libhabdles = append(a.libhabdles, lib)
	fnmap := make(map[string]interface{})
	fileds := make([]reflect.StructField, 0, len(fnmap))
	for name, types := range libFuncMap {
		ft, err := makeFuncType(types.ParamTypes, types.ReturnTypes)
		if err != nil {
			panic(err)
		}
		fn := reflect.New(ft).Elem().Addr().Interface()
		purego.RegisterLibFunc(fn, lib, name)

		//Get ft 指针类型
		// ftp := reflect.PtrTo(ft)
		fileds = append(fileds, reflect.StructField{
			Name: name,
			Type: ft,
		})
		fnmap[name] = fn
	}

	// 根据fnmap构建一个匿名结构体，结构体的字段名为函数名，字段值为函数指针
	libstruct := reflect.StructOf(fileds)
	// 创建一个匿名结构体的实例
	libinstance := reflect.New(libstruct).Elem()
	for name, fn := range fnmap {
		// 将函数指针指向的函数设置到结构体实例的对应字段中
		libinstance.FieldByName(name).Set(reflect.ValueOf(fn).Elem())
		// libinstance.FieldByName(name).Set()
	}
	a.libMap[libName] = libinstance.Interface()

	return a.libMap[libName]
}

// Close 关闭所有已加载的动态库句柄，释放资源。
// 应在不再需要使用动态库时调用此方法。
func (a *Ankointerceptor) Close() {
	for _, lib := range a.libhabdles {
		purego.Dlclose(lib)
	}
}
