package bwrap

import (
	"slices"
	"strconv"
)

/*
	static boolean args
*/

type BoolArg int

func (b BoolArg) Unwrap() []string {
	return boolArgs[b]
}

const (
	UnshareAll BoolArg = iota
	UnshareUser
	UnshareIPC
	UnsharePID
	UnshareNet
	UnshareUTS
	UnshareCGroup
	ShareNet

	UserNS
	Clearenv

	NewSession
	DieWithParent
	AsInit
)

var boolArgs = [...][]string{
	UnshareAll:    {"--unshare-all", "--unshare-user"},
	UnshareUser:   {"--unshare-user"},
	UnshareIPC:    {"--unshare-ipc"},
	UnsharePID:    {"--unshare-pid"},
	UnshareNet:    {"--unshare-net"},
	UnshareUTS:    {"--unshare-uts"},
	UnshareCGroup: {"--unshare-cgroup"},
	ShareNet:      {"--share-net"},

	UserNS:   {"--disable-userns", "--assert-userns-disabled"},
	Clearenv: {"--clearenv"},

	NewSession:    {"--new-session"},
	DieWithParent: {"--die-with-parent"},
	AsInit:        {"--as-pid-1"},
}

func (c *Config) boolArgs() Builder {
	b := boolArg{
		UserNS:   !c.UserNS,
		Clearenv: c.Clearenv,

		NewSession:    c.NewSession,
		DieWithParent: c.DieWithParent,
		AsInit:        c.AsInit,
	}

	if c.Unshare == nil {
		b[UnshareAll] = true
		b[ShareNet] = c.Net
	} else {
		b[UnshareUser] = c.Unshare.User
		b[UnshareIPC] = c.Unshare.IPC
		b[UnsharePID] = c.Unshare.PID
		b[UnshareNet] = c.Unshare.Net
		b[UnshareUTS] = c.Unshare.UTS
		b[UnshareCGroup] = c.Unshare.CGroup
	}

	return &b
}

type boolArg [len(boolArgs)]bool

func (b *boolArg) Len() (l int) {
	for i, v := range b {
		if v {
			l += len(boolArgs[i])
		}
	}
	return
}

func (b *boolArg) Append(args *[]string) {
	for i, v := range b {
		if v {
			*args = append(*args, BoolArg(i).Unwrap()...)
		}
	}
}

/*
	static integer args
*/

type IntArg int

func (i IntArg) Unwrap() string {
	return intArgs[i]
}

const (
	UID IntArg = iota
	GID
)

var intArgs = [...]string{
	UID: "--uid",
	GID: "--gid",
}

func (c *Config) intArgs() Builder {
	return &intArg{
		UID: c.UID,
		GID: c.GID,
	}
}

type intArg [len(intArgs)]*int

func (n *intArg) Len() (l int) {
	for _, v := range n {
		if v != nil {
			l += 2
		}
	}
	return
}

func (n *intArg) Append(args *[]string) {
	for i, v := range n {
		if v != nil {
			*args = append(*args, IntArg(i).Unwrap(), strconv.Itoa(*v))
		}
	}
}

/*
	static string args
*/

type StringArg int

func (s StringArg) Unwrap() string {
	return stringArgs[s]
}

const (
	Hostname StringArg = iota
	Chdir
	UnsetEnv
	LockFile
)

var stringArgs = [...]string{
	Hostname: "--hostname",
	Chdir:    "--chdir",
	UnsetEnv: "--unsetenv",
	LockFile: "--lock-file",
}

func (c *Config) stringArgs() Builder {
	n := stringArg{
		UnsetEnv: c.UnsetEnv,
		LockFile: c.LockFile,
	}

	if c.Hostname != "" {
		n[Hostname] = []string{c.Hostname}
	}
	if c.Chdir != "" {
		n[Chdir] = []string{c.Chdir}
	}

	return &n
}

type stringArg [len(stringArgs)][]string

func (s *stringArg) Len() (l int) {
	for _, arg := range s {
		l += len(arg) * 2
	}
	return
}

func (s *stringArg) Append(args *[]string) {
	for i, arg := range s {
		for _, v := range arg {
			*args = append(*args, StringArg(i).Unwrap(), v)
		}
	}
}

/*
	static pair args
*/

type PairArg int

func (p PairArg) Unwrap() string {
	return pairArgs[p]
}

const (
	SetEnv PairArg = iota
)

var pairArgs = [...]string{
	SetEnv: "--setenv",
}

func (c *Config) pairArgs() Builder {
	var n pairArg
	n[SetEnv] = make([][2]string, len(c.SetEnv))
	keys := make([]string, 0, len(c.SetEnv))
	for k := range c.SetEnv {
		keys = append(keys, k)
	}
	slices.Sort(keys)
	for i, k := range keys {
		n[SetEnv][i] = [2]string{k, c.SetEnv[k]}
	}

	return &n
}

type pairArg [len(pairArgs)][][2]string

func (p *pairArg) Len() (l int) {
	for _, v := range p {
		l += len(v) * 3
	}
	return
}

func (p *pairArg) Append(args *[]string) {
	for i, arg := range p {
		for _, v := range arg {
			*args = append(*args, PairArg(i).Unwrap(), v[0], v[1])
		}
	}
}