package main

import (
	"bufio"
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"
)

func assign(s string, index []string, val string) string {
	if len(index) == 0 {
		return val
	}
	list, i := parseList(s), toInt(index[0])
	return spliceList(list, i, 1, assign(indexOfList(list, i), index[1:], val))
}

func setValue(s string, val value) (value, error) {
	if !isNameWithIndex(s) {
		return value{}, fmt.Errorf("setvalue: %s (%s)", errInvalidName, s)
	}
	name, index := toNameWithIndex(s)
	_, ok := reserved[name]
	if ok {
		return value{}, fmt.Errorf("setvalue: %s (%s)", errNameReserved, name)
	}
	oldVal, ok := env[len(env)-1][name]
	if !ok && len(index) > 0 {
		return value{}, fmt.Errorf("setvalue: %s (%s)", errNameNotFound, name)
	}
	if len(index) > 0 {
		if isWord(val.val) {
			val.val = val.val[1:]
		}
		val = value{val: assign(oldVal.val, index, val.val)}
	}
	val = toValue(val)
	env[len(env)-1][name] = val
	return val, nil
}

func getValue(s string) (value, error) {
	if !isNameWithIndex(s) {
		return value{}, fmt.Errorf("getvalue: %s (%s)", errInvalidName, s)
	}
	name, index := toNameWithIndex(s)
	envs := []int{len(env) - 1, len(env) - 2, 1}
	for _, id := range envs {
		val, ok := env[id][name]
		if ok {
			for _, i := range index {
				list, r := parseList(val.val), strings.Split(i, ":")
				if len(r) == 1 {
					val = value{val: indexOfList(list, toInt(r[0]))}
					if !isList(val.val) {
						val.val = `"` + val.val
					}
				} else {
					if r[0] == "" {
						r[0] = "0"
					}
					if r[1] == "" {
						r[1] = strconv.FormatInt(int64(len(list)), 10)
					}
					val = value{val: rangeOfList(list, toInt(r[0]), toInt(r[1]))}
				}
			}
			return val, nil
		}
	}
	return value{}, fmt.Errorf("getvalue: %s (%s)", errNameNotFound, name)
}

func opMake(val1, val2 value) value {
	val, err := setValue(val1.val, val2)
	if err != nil {
		panic(err)
	}
	return val
}

func opThing(val1 value) value {
	val, err := getValue(val1.val)
	if err != nil {
		panic(err)
	}
	return val
}

func opErase(val1 value) value {
	name := toName(val1.val)
	val, ok := env[len(env)-1][name]
	if ok {
		delete(env[len(env)-1], name)
		return val
	}
	panic(fmt.Errorf("erase: %s (%s)", errNameNotFound, name))
}

func opExport(val1 value) value {
	name := toName(val1.val)
	val, ok := env[len(env)-1][name]
	if ok {
		env[1][name] = val
		return val
	}
	panic(fmt.Errorf("export: %s (%s)", errNameNotFound, name))
}

func opSave(val1 value) value {
	name := toName(val1.val)
	s := ""
	for name, val := range env[len(env)-1] {
		s += `make "` + name + " " + val.val + "\n"
	}
	err := ioutil.WriteFile(name, []byte(s), 0666)
	if err != nil {
		panic(fmt.Errorf("save: %s (%s)", errFileError, name))
	}
	return val1
}

func opLoad(val1 value) value {
	name := toName(val1.val)
	s, err := ioutil.ReadFile(name)
	if err != nil {
		panic(fmt.Errorf("load: %s (%s)", errFileError, name))
	}
	interpret(bufio.NewScanner(strings.NewReader(string(s))))
	return value{val: "true"}
}

func opErAll() value {
	env[len(env)-1] = make(environ)
	return value{val: "true"}
}

func opPoAll() value {
	list := []string{}
	for name := range env[len(env)-1] {
		list = append(list, name)
	}
	return value{val: makeList(list)}
}
