// Package ext provides wrappers around nonportable system interfaces.
package ext

import (
	"encoding/json"
	"iter"
	"math"
	"strconv"
)

// checked in container/seccomp
type (
	// Uint is equivalent to C.uint.
	Uint = uint32
	// Int is equivalent to C.int.
	Int = int32
)

// Integer limit values.
const (
	MaxUint = math.MaxUint32
	MaxInt  = math.MaxInt32
)

// SyscallNum represents an architecture-specific, Linux syscall number.
type SyscallNum Int

// Syscalls returns an iterator over all wired syscalls.
func Syscalls() iter.Seq2[string, SyscallNum] {
	return func(yield func(string, SyscallNum) bool) {
		for name, num := range syscallNum {
			if !yield(name, num) {
				return
			}
		}
		for name, num := range syscallNumExtra {
			if !yield(name, num) {
				return
			}
		}
	}
}

// SyscallResolveName resolves a syscall number from its string representation.
func SyscallResolveName(name string) (num SyscallNum, ok bool) {
	if num, ok = syscallNum[name]; ok {
		return
	}
	num, ok = syscallNumExtra[name]
	return
}

// MarshalJSON resolves the name of [Syscall] and encodes it as a [json] string.
// If such a name does not exist, the syscall number is encoded instead.
func (num *SyscallNum) MarshalJSON() ([]byte, error) {
	n := *num
	for name, cur := range Syscalls() {
		if cur == n {
			return json.Marshal(name)
		}
	}
	return json.Marshal(n)
}

// SyscallNameError is returned when trying to unmarshal an invalid syscall name into [ScmpSyscall].
type SyscallNameError string

func (e SyscallNameError) Error() string {
	return "invalid syscall name " + strconv.Quote(string(e))
}

// UnmarshalJSON looks up the syscall number corresponding to name encoded in data
// by calling [SyscallResolveName].
func (num *SyscallNum) UnmarshalJSON(data []byte) error {
	var name string
	if err := json.Unmarshal(data, &name); err != nil {
		return err
	}
	if n, ok := SyscallResolveName(name); !ok {
		return SyscallNameError(name)
	} else {
		*num = n
		return nil
	}
}
