// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.

// operator-dot.go
package expr

import "fmt"

// -------- dot term
func newDotTerm(tk *Token) (inst *term) {
	return &term{
		tk:       *tk,
		children: make([]*term, 0, 2),
		position: posInfix,
		priority: priDot,
		evalFunc: evalDot,
	}
}

func verifyIndex(ctx ExprContext, indexTerm *term, maxValue int) (index int, err error) {
	var v int
	var indexValue any
	if indexValue, err = indexTerm.compute(ctx); err == nil {
		if v, err = indexTerm.toInt(indexValue, "index expression value must be integer"); err == nil {
			if v >= 0 && v < maxValue {
				index = v
			} else if index >= -maxValue {
				index = maxValue + v
			} else {
				err = indexTerm.Errorf("index %d out of bounds", v)
			}
		}
	}
	return
}

func evalDot(ctx ExprContext, self *term) (v any, err error) {
	var leftValue, rightValue any

	if err = self.checkOperands(); err != nil {
		return
	}
	if leftValue, err = self.children[0].compute(ctx); err != nil {
		return
	}

	indexTerm := self.children[1]

	switch unboxedValue := leftValue.(type) {
	case []any:
		var index int
		if index, err = verifyIndex(ctx, indexTerm, len(unboxedValue)); err == nil {
			v = unboxedValue[index]
		}
	case string:
		var index int
		if index, err = verifyIndex(ctx, indexTerm, len(unboxedValue)); err == nil {
			v = unboxedValue[index]
		}
	case map[any]any:
		var ok bool
		var indexValue any
		if indexValue, err = indexTerm.compute(ctx); err == nil {
			if v, ok = unboxedValue[indexValue]; !ok {
				err = fmt.Errorf("key %v does not belong to the dictionary", rightValue)
			}
		}
	// case *dataCursor:
	// 	if indexTerm.symbol() == SymIdentifier {
	// 		opName := indexTerm.source()
	// 		if opName == resetName {
	// 			_, err = unboxedValue.Reset()
	// 		} else if opName == cleanName {
	// 			_, err = unboxedValue.Clean()
	// 		} else {
	// 			err = indexTerm.Errorf("iterators do not support command %q", opName)
	// 		}
	// 		v = err == nil
	// 	}
	case ExtIterator:
		if indexTerm.symbol() == SymIdentifier {
			opName := indexTerm.source()
			if unboxedValue.HasOperation(opName) {
				v, err = unboxedValue.CallOperation(opName, []any{})
			} else {
				err = indexTerm.Errorf("this iterator do not support the %q command", opName)
				v = false
			}
		}
	default:
		err = self.errIncompatibleTypes(leftValue, rightValue)
	}
	return
}

// init
func init() {
	registerTermConstructor(SymDot, newDotTerm)
}