package mangler

import (
	"reflect"
	"unsafe"
)

func append_uint16(b []byte, u uint16) []byte {
	return append(b, // LE
		byte(u),
		byte(u>>8),
	)
}

func append_uint32(b []byte, u uint32) []byte {
	return append(b, // LE
		byte(u),
		byte(u>>8),
		byte(u>>16),
		byte(u>>24),
	)
}

func append_uint64(b []byte, u uint64) []byte {
	return append(b, // LE
		byte(u),
		byte(u>>8),
		byte(u>>16),
		byte(u>>24),
		byte(u>>32),
		byte(u>>40),
		byte(u>>48),
		byte(u>>56),
	)
}

type typecontext struct {
	ntype reflect.Type
	rtype reflect.Type
}

func deref_ptr_mangler(ctx typecontext, mangle Mangler, n uint) Mangler {
	if mangle == nil || n == 0 {
		panic("bad input")
	}

	// Non-nested value types,
	// i.e. just direct ptrs to
	// primitives require one
	// less dereference to ptr.
	if ctx.ntype == nil {
		n--
	}

	return func(buf []byte, ptr unsafe.Pointer) []byte {

		// Deref n number times.
		for i := n; i > 0; i-- {

			if ptr == nil {
				// Check for nil values
				buf = append(buf, '0')
				return buf
			}

			// Further deref ptr
			buf = append(buf, '1')
			ptr = *(*unsafe.Pointer)(ptr)
		}

		if ptr == nil {
			// Final nil val check
			buf = append(buf, '0')
			return buf
		}

		// Mangle fully deref'd
		buf = append(buf, '1')
		buf = mangle(buf, ptr)
		return buf
	}
}

func iter_slice_mangler(ctx typecontext, mangle Mangler) Mangler {
	if ctx.rtype == nil || mangle == nil {
		panic("bad input")
	}

	// memory size of elem.
	esz := ctx.rtype.Size()

	return func(buf []byte, ptr unsafe.Pointer) []byte {
		// Get data as slice hdr.
		hdr := (*slice_header)(ptr)

		for i := 0; i < hdr.len; i++ {
			// Mangle data at slice index.
			eptr := array_at(hdr.data, esz, i)
			buf = mangle(buf, eptr)
			buf = append(buf, ',')
		}

		if hdr.len > 0 {
			// Drop final comma.
			buf = buf[:len(buf)-1]
		}

		return buf
	}
}

func iter_array_mangler(ctx typecontext, mangle Mangler) Mangler {
	if ctx.rtype == nil || mangle == nil {
		panic("bad input")
	}

	// no. array elements.
	n := ctx.ntype.Len()

	// memory size of elem.
	esz := ctx.rtype.Size()

	return func(buf []byte, ptr unsafe.Pointer) []byte {
		for i := 0; i < n; i++ {
			// Mangle data at array index.
			offset := esz * uintptr(i)
			eptr := add(ptr, offset)
			buf = mangle(buf, eptr)
			buf = append(buf, ',')
		}

		if n > 0 {
			// Drop final comma.
			buf = buf[:len(buf)-1]
		}

		return buf
	}
}

func iter_struct_mangler(ctx typecontext, manglers []Mangler) Mangler {
	if ctx.rtype == nil || len(manglers) != ctx.rtype.NumField() {
		panic("bad input")
	}

	type field struct {
		mangle Mangler
		offset uintptr
	}

	// Bundle together the fields and manglers.
	fields := make([]field, ctx.rtype.NumField())
	for i := range fields {
		rfield := ctx.rtype.FieldByIndex([]int{i})
		fields[i].offset = rfield.Offset
		fields[i].mangle = manglers[i]
		if fields[i].mangle == nil {
			panic("bad input")
		}
	}

	return func(buf []byte, ptr unsafe.Pointer) []byte {
		for i := range fields {
			// Get struct field ptr via offset.
			fptr := add(ptr, fields[i].offset)

			// Mangle the struct field data.
			buf = fields[i].mangle(buf, fptr)
			buf = append(buf, ',')
		}

		if len(fields) > 0 {
			// Drop final comma.
			buf = buf[:len(buf)-1]
		}

		return buf
	}
}

// array_at returns ptr to index in array at ptr, given element size.
func array_at(ptr unsafe.Pointer, esz uintptr, i int) unsafe.Pointer {
	return unsafe.Pointer(uintptr(ptr) + esz*uintptr(i))
}

// add returns the ptr addition of starting ptr and a delta.
func add(ptr unsafe.Pointer, delta uintptr) unsafe.Pointer {
	return unsafe.Pointer(uintptr(ptr) + delta)
}

type slice_header struct {
	data unsafe.Pointer
	len  int
	cap  int
}

func eface_data(a any) unsafe.Pointer {
	type eface struct{ _, data unsafe.Pointer }
	return (*eface)(unsafe.Pointer(&a)).data
}