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

// term.go
package expr

import (
	"fmt"
	"strings"
)

type termKind uint16

const (
	kindUnknown termKind = iota
	kindBool
	kindInteger
	kindFloat
	kindString
	kindIdentifier
)

type termClass uint16

const (
	classNull termClass = iota
	classConst
	classVar
	classFunc
	classOperator
)

type termPriority uint32

const (
	priNone termPriority = iota
	priOr
	priAnd
	priNot
	priRelational
	priSum
	priProduct
	priSign
	priFact
	priValue
)

type termPosition uint8

const (
	posLeaf termPosition = iota
	posInfix
	posPrefix
	posPostfix
)

type evalFuncType func(ctx exprContext, self *term) (v any, err error)

// type iterm interface {
// 	getClass() termClass
// 	getKind() termKind
// 	compute(ctx exprContext) (v any, err error)
// 	// isOperator() bool
// 	// isOperand() bool
// 	source() string
// 	setParent(parentNode term)
// }

type term struct {
	tk       Token
	class    termClass
	kind     termKind
	parent   *term
	children []*term
	position termPosition // operator position: infix, prefix, suffix
	priority termPriority // operator priority: higher value means higher priority
	evalFunc evalFuncType
}

func (self *term) String() string {
	var sb strings.Builder
	self.toString(&sb)
	return sb.String()
}

func (self *term) toString(sb *strings.Builder) {
	if self.position == posLeaf {
		sb.WriteString(self.tk.String())
	} else {
		sb.WriteByte('[')
		sb.WriteString(self.tk.String())
		if self.children != nil {
			sb.WriteByte('(')
			for i, c := range self.children {
				if i > 0 {
					sb.WriteByte(' ')
				}
				c.toString(sb)
			}
			sb.WriteByte(')')
		}
		sb.WriteByte(']')
	}
}

func (self *term) getChildrenCount() (count int) {
	if self.position == posLeaf || self.children == nil {
		count = 0
	} else {
		count = len(self.children)
	}
	return
}

func (self *term) getRoom() (room int) {
	switch self.position {
	case posLeaf:
		room = 0
	case posInfix:
		room = 2
	case posPostfix, posPrefix:
		room = 1
	default:
		panic("Invalid node position")
	}
	return
}

func (self *term) isComplete() bool {
	return self.getChildrenCount() == self.getRoom()
}

func (self *term) removeLastChild() (child *term) {
	if self.children != nil {
		child = self.children[len(self.children)-1]
		self.children = self.children[0 : len(self.children)-1]
	} else {
		panic("Can't get last child")
	}
	return
}

func (self *term) isLeaf() bool {
	return self.position == posLeaf
}

// func (self *term) getKind() termKind {
// 	return self.kind
// }

// func (self *term) getClass() termClass {
// 	return self.class
// }

func (self *term) getPriority() termPriority {
	return self.priority
}

func (self *term) setParent(parent *term) {
	self.parent = parent
	if parent != nil && len(parent.children) < cap(parent.children) {
		parent.children = append(parent.children, self)
	}
}

// func (self *term) isOperand() bool {
// 	return self.getClass() != classOperator
// }

// func (self *term) isOperator() bool {
// 	return self.getClass() == classOperator
// }

func (self *term) source() string {
	return self.tk.source
}

func (self *term) compute(ctx exprContext) (v any, err error) {
	if self.evalFunc == nil {
		err = fmt.Errorf("undfined eval-func for %v term type", self.kind)
	} else {
		v, err = self.evalFunc(ctx, self)
	}
	return
}

func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
	return fmt.Errorf(
		"left operand '%v' [%T] is not compatible with right operand '%v' [%T] with respect to operator %q",
		leftValue, leftValue,
		rightValue, rightValue,
		self.source())
}

func (self *term) errIncompatibleType(value any) error {
	return fmt.Errorf(
		"prefix/postfix operator %q do not support operand '%v' [%T]", self.source(), value, value)
}

func (self *term) checkOperands() (err error) {
	switch self.position {
	case posInfix:
		if self.children == nil || len(self.children) != 2 || self.children[0] == nil || self.children[1] == nil {
			err = fmt.Errorf("infix operator %q requires two operands, got %d", self.source(), self.getChildrenCount())
		}
	case posPrefix:
		if self.children == nil || len(self.children) != 1 || self.children[0] == nil {
			err = fmt.Errorf("prefix operator %q requires one operand", self.tk.String())
		}
	case posPostfix:
		if self.children == nil || len(self.children) != 1 || self.children[0] == nil {
			err = fmt.Errorf("postfix operator %q requires one operand", self.tk.String())
		}
	}
	return
}

func (self *term) evalInfix(ctx exprContext) (leftValue, rightValue any, err error) {
	if err = self.checkOperands(); err == nil {
		if leftValue, err = self.children[0].compute(ctx); err == nil {
			rightValue, err = self.children[1].compute(ctx)
		}
	}
	return
}

func (self *term) evalPrefix(ctx exprContext) (rightValue any, err error) {
	if err = self.checkOperands(); err == nil {
		rightValue, err = self.children[0].compute(ctx)
	}
	return
}