package vars

import (
	
)

type elem struct {
	variable Var
	assocers []interface{}
	indices  []interface{}
	setValue interface{}
}

func ( *elem) ( interface{}) error {
	var  error
	 := 
	// Evaluate the actual new value from inside out. See comments in
	// MakeElement for how element assignment works.
	for  := len(.assocers) - 1;  >= 0; -- {
		,  = vals.Assoc(.assocers[], .indices[], )
		if  != nil {
			return 
		}
	}
	 = .variable.Set()
	// TODO(xiaq): Remember the set value for use in Get.
	.setValue = 
	return 
}

func ( *elem) () interface{} {
	// TODO(xiaq): This is only called from fixNilVariables. We don't want to
	// waste time accessing the variable, so we simply return the value that was
	// set.
	return .setValue
}

// MakeElement returns a variable, that when set, simulates the mutation of an
// element.
func ( Var,  []interface{}) (Var, error) {
	// Assignment of indexed variables actually assigns the variable, with
	// the right hand being a nested series of Assocs. As the simplest
	// example, `a[0] = x` is equivalent to `a = (assoc $a 0 x)`. A more
	// complex example is that `a[0][1][2] = x` is equivalent to
	//	`a = (assoc $a 0 (assoc $a[0] 1 (assoc $a[0][1] 2 x)))`.
	// Note that in each assoc form, the first two arguments can be
	// determined now, while the last argument is only known when the
	// right-hand-side is known. So here we evaluate the first two arguments
	// of each assoc form and put them in two slices, assocers and indices.
	// In the previous example, the two slices will contain:
	//
	// assocers: $a $a[0] $a[0][1]
	// indices:   0     1        2
	//
	// When the right-hand side of the assignment becomes available, the new
	// value for $a is evaluated by doing Assoc from inside out.
	 := make([]interface{}, len())
	 := .Get()
	[0] = 
	for ,  := range [:len()-1] {
		 := []
		,  := vals.Index(, )
		if  != nil {
			return nil, 
		}
		[+1] = 
	}
	return &elem{, , , nil}, nil
}

// DelElement deletes an element. It uses a similar process to MakeElement,
// except that the last level of container needs to be Dissoc-able instead of
// Assoc-able.
func ( Var,  []interface{}) error {
	var  error
	// In "del a[0][1][2]",
	//
	// indices:   0  1     2
	// assocers: $a $a[0]
	// dissocer:          $a[0][1]
	 := make([]interface{}, len()-1)
	 := .Get()
	for ,  := range [:len()-1] {
		[] = 

		var  error
		,  = vals.Index(, )
		if  != nil {
			return 
		}
	}

	 := vals.Dissoc(, [len()-1])
	if  == nil {
		return elemErr{len(), "value does not support element removal"}
	}

	for  := len() - 1;  >= 0; -- {
		,  = vals.Assoc([], [], )
		if  != nil {
			return 
		}
	}
	return .Set()
}

type elemErr struct {
	level int
	msg   string
}

func ( elemErr) () string {
	return .msg
}

// HeadOfElement gets the underlying head variable of an element variable, or
// nil if the argument is not an element variable.
func ( Var) Var {
	if ,  := .(*elem);  {
		return .variable
	}
	return nil
}

// ElementErrorLevel returns the level of an error returned by MakeElement or
// DelElement. Level 0 represents that the error is about the variable itself.
// If the argument was not returned from MakeVariable, -1 is returned.
func ( error) int {
	if ,  := .(elemErr);  {
		return .level
	}
	return -1
}