package main

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

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

func setValue(s string, val value, envs []environ) (value, error) {
	if !isNameWithIndex(&_unknown{s: s}) {
		return nil, fmt.Errorf("set: %s (%s)", errInvalidName, s)
	}
	name, index := toNameWithIndex(&_unknown{s: s})
	_, ok := reserved[name]
	if ok {
		return nil, fmt.Errorf("set: %s (%s)", errNameReserved, name)
	}
	oldVal, ok := envs[0][name]
	if ok {
		delete(envs[0], name)
		if len(index) > 0 {
			val = &_unknown{s: assign(oldVal.String(), index, val.String(), envs)}
		}
	} else if len(index) > 0 {
		return nil, fmt.Errorf("set: %s (%s)", errNameNotFound, name)
	}
	val = toValue(val, envs)
	envs[0][name] = val
	return val, nil
}

func getValue(s string, envs []environ) (value, error) {
	if !isNameWithIndex(&_unknown{s: s}) {
		return nil, fmt.Errorf("get: %s (%s)", errInvalidName, s)
	}
	name, index := toNameWithIndex(&_unknown{s: s})
	for _, env := range envs {
		val, ok := env[name]
		if ok {
			if len(index) > 0 {
				for _, i := range index {
					list := parseList(val.String())
					r, s := strings.Split(i, ":"), ""
					if len(r) == 1 {
						s = indexOfList(list, toInt(&_unknown{s: r[0]}, envs))
						if !isList(&_unknown{s: s}) {
							s = `"` + s
						}
					} else {
						num1, num2 := 0, len(list)
						if len(r[0]) > 0 {
							num1 = toInt(&_unknown{s: r[0]}, envs)
						}
						if len(r[1]) > 0 {
							num2 = toInt(&_unknown{s: r[1]}, envs)
						}
						s = rangeOfList(list, num1, num2)
					}
					val = &_unknown{s: s}
				}
			}
			return val, nil
		}
	}
	return nil, fmt.Errorf("get: %s (%s)", errNameNotFound, name)
}

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

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

func opErase(val1 value, envs []environ) value {
	name := toName(val1)
	for _, env := range envs {
		val, ok := env[name]
		if ok {
			delete(env, name)
			return val
		}
	}
	panic(fmt.Errorf("erase: %s (%s)", errNameNotFound, name))
}

func opExport(val1 value, envs []environ) value {
	name := toName(val1)
	val, ok := envs[0][name]
	if ok {
		if envs[1] != nil {
			envs[1][name] = val
		}
		return val
	}
	panic(fmt.Errorf("export: %s (%s)", errNameNotFound, name))
}

func opSave(val1 value, env environ) value {
	name := toName(val1)
	s := ""
	for name, val := range env {
		s += `make "` + name + " " + val.String() + "\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, env environ) *_bool {
	name := toName(val1)
	s, err := ioutil.ReadFile(name)
	if err != nil {
		panic(fmt.Errorf("load: %s (%s)", errFileError, name))
	}
	scanner := bufio.NewScanner(strings.NewReader(string(s)))
	scanner.Split(splitFunc)
	interpret(&scanProvider{isList: false, scanner: scanner}, []environ{env, nil, nil})
	return &_bool{b: true}
}

func opErAll(env environ) *_bool {
	for name := range env {
		delete(env, name)
	}
	return &_bool{b: true}
}

func opPoAll(env environ) *_list {
	list := []string{}
	for name := range env {
		list = append(list, name)
	}
	return &_list{list: list}
}
