package main

import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
)

const _name, _nameOrInt = `([A-Za-z]\w*)`, "(" + _name + `|\d+)`
const _index = "(" + _nameOrInt + "|:|" + _nameOrInt + ":|:" + _nameOrInt + "|" + _nameOrInt + ":" + _nameOrInt + ")"

var nameExp = regexp.MustCompile(`^"` + _name + "$")
var nameWithIndexExp = regexp.MustCompile(`^"` + _name + `((\[` + _index + `])*\[` + _index + `\\])?$`)

func isName(s string) bool {
	return nameExp.MatchString(s)
}

func toName(s string) string {
	if !isName(s) {
		panic(fmt.Errorf("name: %s (%s)", errInvalidName, s))
	}
	return s[1:]
}

func isNameWithIndex(s string) bool {
	return nameWithIndexExp.MatchString(s)
}

func toNameWithIndex(s string) (string, []string) {
	if !isNameWithIndex(s) {
		panic(fmt.Errorf("name: %s (%s)", errInvalidName, s))
	}
	index := strings.Split(s, "[")
	name, cnt := index[0][1:], len(index)-1
	for i := 1; i < cnt; i++ {
		index[i] = index[i][:len(index[i])-1]
	}
	index[cnt] = index[cnt][:len(index[cnt])-2]
	return name, index[1:]
}

func isNumber(s string) bool {
	if s != "" && s[0] == '"' {
		s = s[1:]
	}
	_, err := strconv.ParseFloat(s, 64)
	return err == nil
}

func toNumber(s string) float64 {
	if s != "" && s[0] == '"' {
		s = s[1:]
	}
	val, err := strconv.ParseFloat(s, 64)
	if err != nil {
		panic(fmt.Errorf("number: %s (%s)", errInvalidNumber, s))
	}
	return val
}

func toInt(s string) int {
	if isName(`"` + s) {
		val, err := getValue(`"` + s)
		if err == nil {
			s = val.val
		}
	}
	if s != "" && s[0] == '"' {
		s = s[1:]
	}
	val, err := strconv.ParseInt(s, 10, 64)
	if err != nil {
		panic(fmt.Errorf("int: %s (%s)", errInvalidInteger, s))
	}
	return int(val)
}

func isWord(s string) bool {
	return s != "" && s[0] == '"'
}

func isList(s string) bool {
	return s != "" && s[0] == '['
}

func isBool(s string) bool {
	if s != "" && s[0] == '"' {
		s = s[1:]
	}
	_, err := strconv.ParseBool(s)
	return err == nil
}

func toBool(s string) bool {
	if s != "" && s[0] == '"' {
		s = s[1:]
	}
	val, err := strconv.ParseBool(s)
	if err != nil {
		panic(fmt.Errorf("bool: %s (%s)", errInvalidBool, s))
	}
	return val
}

func isEmpty(s string) bool {
	return s == `"` || s == "[ ]"
}

func parseFunc(s string) value {
	if !isList(s) {
		return value{val: s}
	}
	list := parseList(s)
	if len(list) != 2 || !isList(list[0]) || !isList(list[1]) {
		return value{val: s, list: list}
	}
	param := parseList(list[0])
	for _, name := range param {
		if !isName(`"` + name) {
			return value{val: s, list: list}
		}
	}
	local := make(environ)
	for name, val := range env[len(env)-1] {
		local[name] = val
	}
	return value{val: s, list: list, param: param, env: local}
}

func isValue(s string) bool {
	return isNumber(s) || isWord(s) || isList(s) || isBool(s)
}

func toValue(val value) value {
	if !isValue(val.val) {
		panic(fmt.Errorf("mua: %s (%s)", errValueExpected, val.val))
	}
	if isWord(val.val) {
		if val.val[1] == '[' {
			val.val = val.val[:1] + "\\" + val.val[1:]
		}
		if val.val[len(val.val)-1] == ']' {
			val.val = val.val[:len(val.val)-1]
			escape := len(val.val) - len(strings.TrimRight(val.val, "\\"))
			if (escape & 1) == 0 {
				val.val += "\\"
			}
			val.val += "]"
		}
		return val
	}
	if isList(val.val) {
		if val.list == nil {
			return parseFunc(val.val)
		}
		return val
	}
	return val
}

func escapeValue(s string) string {
	for i := 0; i < len(s); i++ {
		if s[i] == '\\' {
			s = s[:i] + s[i+1:]
		}
	}
	return s
}

func opIsName(val1 value) value {
	return value{val: strconv.FormatBool(isName(val1.val))}
}

func opIsNumber(val1 value) value {
	return value{val: strconv.FormatBool(isNumber(val1.val))}
}

func opIsWord(val1 value) value {
	return value{val: strconv.FormatBool(isWord(val1.val))}
}

func opIsList(val1 value) value {
	return value{val: strconv.FormatBool(isList(val1.val))}
}

func opIsBool(val1 value) value {
	return value{val: strconv.FormatBool(isBool(val1.val))}
}

func opIsEmpty(val1 value) value {
	return value{val: strconv.FormatBool(isEmpty(val1.val))}
}
