package tracer

import (
	"encoding/binary"
	"errors"
	"fmt"
	"github.com/cilium/ebpf"
	"github.com/cilium/ebpf/btf"
	"runtime"
)

func kernel_struct_field_offset(btfSpec *btf.Spec, member string, field string) int32 {
	typ, err := btfSpec.AnyTypeByName(member)
	if err != nil {
		return ETR_NOTSUPP
	}
	return kernel_struct_field_offset_helper(btfSpec, typ, field)
}

func kernel_struct_field_offset_helper(btfSpec *btf.Spec, typ btf.Type, field string) int32 {
	//var offset btf.Bits
	switch typ.(type) {
	case *btf.Struct, *btf.Union:
		var membersT []btf.Member
		switch t := typ.(type) {
		case *btf.Struct:
			membersT = t.Members
		case *btf.Union:
			membersT = t.Members
		}
		for _, member := range membersT {
			if member.Name == field {
				//fmt.Printf("Struct:%s,0x%x\n", member.Name, member.Offset.Bytes())
				return int32(member.Offset.Bytes())
			}
			if member.Name == "" {
				//fmt.Printf("下钻:%s,0x%x\n", member.Type, member.Offset/8)
				retval := kernel_struct_field_offset_helper(btfSpec, member.Type, field)
				if retval >= 0 {
					return int32(member.Offset.Bytes()) + retval
				}
			}
		}
	}
	return ETR_NOTEXIST
}

func bpf_table_pre_set_value(collectionSpec *ebpf.CollectionSpec, opts *ebpf.CollectionOptions, mapName string, data any) int {
	m, ok := collectionSpec.Maps[mapName]
	if ok {
		newMap, err := ebpf.NewMap(&ebpf.MapSpec{
			Type:       m.Type,
			KeySize:    m.KeySize,
			ValueSize:  m.ValueSize,
			MaxEntries: m.MaxEntries,
		})
		if err != nil {
			return ETR_UPDATE_MAP_FAILD
		}

		//// Convert test_t to slice
		//numCPU := runtime.NumCPU()
		//off := make([]interface{}, numCPU)
		//for i := range off {
		//	off[i] = data
		//}

		key := make([]byte, 4)                // Assuming int key size is 4 bytes
		binary.LittleEndian.PutUint32(key, 0) // Assuming the key is an integer
		if err = newMap.Update(key, data, ebpf.UpdateAny); err != nil {
			fmt.Println(err)
			return ETR_UPDATE_MAP_FAILD
		}
		opts.MapReplacements[mapName] = newMap
		//opts.MapReplacements = map[string]*ebpf.Map{
		//	mapName: newMap,
		//}
	} else {
		return ETR_UPDATE_MAP_FAILD
	}
	return ETR_OK

}

func bpf_table_set_value(collection *ebpf.Collection, mapName string, key uint32, data any) (int, error) {
	m, ok := collection.Maps[mapName]
	//for s, m2 := range collection.Maps {
	//	fmt.Println(s, m2.String())
	//}
	//fmt.Println("bpf_table_set_value", m, mapName, data)
	if ok {
		k := make([]byte, 4)                  // Assuming int k size is 4 bytes
		binary.LittleEndian.PutUint32(k, key) // Assuming the key is an integer
		if err := m.Update(k, data, ebpf.UpdateAny); err != nil {
			return ETR_UPDATE_MAP_FAILD, err
		}
	} else {
		return ETR_UPDATE_MAP_FAILD, errors.New("cannot find map " + mapName)
	}
	return ETR_OK, nil
}

func test(collectionSpec *ebpf.CollectionSpec, opts *ebpf.CollectionOptions) {
	fmt.Println("collectionSpec.Maps start --")
	var newMap *ebpf.Map
	var err error
	m, ok := collectionSpec.Maps["test_heap"]
	if ok {
		newMap, err = ebpf.NewMap(&ebpf.MapSpec{
			Type:       m.Type,       // 设置 Map 的类型
			KeySize:    m.KeySize,    // 设置 Key 的大小
			ValueSize:  m.ValueSize,  // 设置 Value 的大小
			MaxEntries: m.MaxEntries, // 设置最大条目数
		})
		if err != nil {
			fmt.Println("Failed to create map:", err)
		}
		data := &testStruct{
			test_id: 123123123,
		}
		// Convert test_t to slice
		numCPU := runtime.NumCPU()
		value := make([]testStruct, numCPU)
		for i := range value {
			value[i] = *data
		}

		key := make([]byte, 4)                // Assuming int key size is 4 bytes
		binary.LittleEndian.PutUint32(key, 0) // Assuming the key is an integer
		if err := newMap.Update(key, value, 0); err != nil {
			panic(err)
		}

		opts.MapReplacements["test_heap"] = newMap
		//opts.MapReplacements = map[string]*ebpf.Map{
		//	"test_heap": newMap,
		//}
	}
}
