package protocol

import (
	"strings"
)

// Command Parsing Helpers
func ParseCommand(data []byte) (string, map[string]string) {
	s := string(data)
	parts := strings.Split(s, " ")
	cmd := parts[0]
	args := make(map[string]string)

	for _, p := range parts[1:] {
		kv := strings.SplitN(p, "=", 2)
		if len(kv) == 2 {
			val := Unescape(kv[1])
			args[kv[0]] = val
		} else {
			args[p] = ""
		}
	}
	return cmd, args
}

// ParseCommands parses response that may contain multiple items separated by pipe (|)
func ParseCommands(data []byte) []*Command {
	s := string(data)
	// TS3 uses pipe | to separate list items
	items := strings.Split(s, "|")

	cmds := make([]*Command, 0, len(items))

	// First item contains the command name
	name, args := ParseCommand([]byte(items[0]))
	cmds = append(cmds, &Command{Name: name, Params: args})

	// Subsequent items reuse the same command name
	for _, item := range items[1:] {
		// Hack: Prepend command name to reuse ParseCommand logic
		// or better: manually parse args.
		// Since ParseCommand splits by space, we can just use "DUMMY " + item
		// ensuring we trim properly.
		_, itemArgs := ParseCommand([]byte("CMD " + strings.TrimSpace(item)))
		cmds = append(cmds, &Command{Name: name, Params: itemArgs})
	}

	return cmds
}

// Unescape TS3 string
func Unescape(s string) string {
	r := strings.NewReplacer(
		`\/`, `/`,
		`\s`, ` `,
		`\p`, `|`,
		`\a`, "\a",
		`\b`, "\b",
		`\f`, "\f",
		`\n`, "\n",
		`\r`, "\r",
		`\t`, "\t",
		`\v`, "\v",
		`\\`, `\`,
	)
	return r.Replace(s)
}

func Escape(s string) string {
	r := strings.NewReplacer(
		`\`, `\\`,
		`/`, `\/`,
		` `, `\s`,
		`|`, `\p`,
		"\a", `\a`,
		"\b", `\b`,
		"\f", `\f`,
		"\n", `\n`,
		"\r", `\r`,
		"\t", `\t`,
		"\v", `\v`,
	)
	return r.Replace(s)
}

// Command represents a TeamSpeak 3 command for building/encoding
type Command struct {
	Name   string
	Params map[string]string
}

func NewCommand(name string) *Command {
	return &Command{
		Name:   name,
		Params: make(map[string]string),
	}
}

func (c *Command) AddParam(key, value string) {
	c.Params[key] = value
}

func (c *Command) Encode() string {
	var sb strings.Builder
	sb.WriteString(c.Name)
	for k, v := range c.Params {
		sb.WriteString(" ")
		sb.WriteString(k)
		if v != "" {
			sb.WriteString("=")
			sb.WriteString(Escape(v))
		}
	}
	return sb.String()
}
