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

// term.go
package scan

import (
	"strings"

	"git.portale-stac.it/go-pkg/expr/kern"
	"git.portale-stac.it/go-pkg/expr/sym"
)

type TermPriority uint32

const (
	PriNone TermPriority = iota
	PriBut
	PriAssign
	PriIterOp // map, filter, digest, etc
	PriInsert
	PriOr
	PriAnd
	PriNot
	PriRelational
	PriInterval
	PriBitwiseOr
	PriBitwiseAnd
	PriBitwiseNot
	PriSum
	PriProduct
	PriFraction
	PriSelector
	PriBinShift
	PriSign
	PriFact
	PriIterValue
	PriDefault
	PriIncDec
	PriDot
	PriDereference
	PriValue
	PriBitwiseXor = PriBitwiseOr
)

type TermPosition uint8

const (
	PosLeaf TermPosition = iota
	PosInfix
	PosPrefix
	PosPostfix
	PosMultifix
)

type EvalFuncType func(ctx kern.ExprContext, self *Term) (v any, err error)

type Term struct {
	Tk       Token
	Parent   *Term
	Children []*Term
	Position TermPosition // operator position: leaf, infix, prefix, postfix, multifix
	Priority TermPriority // operator priority: higher value means higher priority
	EvalFunc EvalFuncType
}

func (t *Term) Clone() (d *Term) {
	var children []*Term
	if t.Children != nil {
		children = make([]*Term, len(t.Children))
		for i, c := range t.Children {
			children[i] = c.Clone()
		}
	}
	d = &Term{
		Tk:       *t.Tk.Clone(),
		Parent:   t.Parent,
		Children: children,
		Position: t.Position,
		Priority: t.Priority,
		EvalFunc: t.EvalFunc,
	}
	return
}

func (t *Term) String() string {
	var sb strings.Builder
	t.ToString(&sb)
	return sb.String()
}

func (t *Term) ToString(sb *strings.Builder) {
	if t.Position == PosLeaf {
		sb.WriteString(t.Tk.String())
	} else {
		sb.WriteByte('[')
		sb.WriteString(t.Tk.String())
		if t.Children != nil {
			sb.WriteByte('(')
			for i, c := range t.Children {
				if i > 0 {
					sb.WriteByte(' ')
				}
				c.ToString(sb)
			}
			sb.WriteByte(')')
		}
		sb.WriteByte(']')
	}
}

func (t *Term) GetChildCount() (count int) {
	if t.Position == PosLeaf || t.Children == nil {
		count = 0
	} else {
		count = len(t.Children)
	}
	return
}

func (t *Term) GetRoom() (room int) {
	switch t.Position {
	case PosLeaf:
		room = 0
	case PosInfix:
		room = 2
	case PosPostfix, PosPrefix:
		room = 1
	default:
		panic("Invalid node position")
	}
	return
}

func (t *Term) isComplete() bool {
	return t.GetChildCount() == t.GetRoom()
}

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

func (t *Term) isLeaf() bool {
	return t.Position == PosLeaf
}

func (t *Term) getPriority() TermPriority {
	return t.Priority
}

func (t *Term) SetParent(parent *Term) {
	t.Parent = parent
	if parent != nil && len(parent.Children) < cap(parent.Children) {
		parent.Children = append(parent.Children, t)
	}
}

func (t *Term) Symbol() sym.Symbol {
	return t.Tk.Sym
}

func (t *Term) Source() string {
	return t.Tk.source
}

func (t *Term) Value() any {
	return t.Tk.Value
}

func (t *Term) GetChild(index int) (c kern.Term) {
	if index >= 0 && index < len(t.Children) {
		c = t.Children[index]
	}
	return
}

func (t *Term) GetChildSource(index int) (s string) {
	if index >= 0 && index < len(t.Children) {
		s = t.Children[index].Tk.source
	}
	return
}

func (t *Term) GetLeftChild() (c kern.Term) {
	if t.Position == PosInfix && len(t.Children) > 0 {
		c = t.Children[0]
	}
	return
}

func (t *Term) GetRightChild() (c kern.Term) {
	if t.Position == PosInfix && len(t.Children) > 1 {
		c = t.Children[1]
	}
	return
}

func (t *Term) IsSymbol(symbol sym.Symbol) bool {
	return t.Symbol() == symbol
}

func (t *Term) IsOneOf(symbols ...sym.Symbol) bool {
	return t.Tk.IsOneOf(symbols)
}

func (t *Term) IsAssign() bool {
	return t.Symbol() == sym.SymEqual
}

func (t *Term) IsVar() bool {
	return t.Symbol() == sym.SymVariable
}

func (t *Term) SetSymbol(symbol sym.Symbol) {
	t.Tk.SetSymbol(symbol)
}

// func (t *Term) IsFunc() bool {
// 	return t.Symbol() == SymFunction
// }

func (t *Term) Compute(ctx kern.ExprContext) (v any, err error) {
	if t.EvalFunc == nil {
		err = t.Errorf("undefined eval-func for %q term", t.Source())
	} else {
		v, err = t.EvalFunc(ctx, t)
	}
	return
}

func (t *Term) ErrIncompatibleTypes(leftValue, rightValue any) error {
	leftType := kern.TypeName(leftValue)
	leftText := kern.GetFormatted(leftValue, kern.Truncate)
	rightType := kern.TypeName(rightValue)
	rightText := kern.GetFormatted(rightValue, kern.Truncate)
	return t.Tk.Errorf(
		"left operand '%s' [%s] and right operand '%s' [%s] are not compatible with operator %q",
		leftText, leftType,
		rightText, rightType,
		t.Source())
}

func (t *Term) ErrIncompatibleType(value any, side string) error {
	return t.Tk.Errorf(
		"operator %q does not support operand '%v' [%s] on its %s side",
		t.Source(), value, kern.TypeName(value), side)
}

func (t *Term) ErrIncompatiblePrefixPostfixType(value any) error {
	return t.Tk.Errorf(
		"prefix/postfix operator %q does not support operand '%v' [%s]",
		t.Source(), value, kern.TypeName(value))
}

func (t *Term) ErrDivisionByZero() error {
	return t.Tk.Errorf("division by zero")
}

func (t *Term) ErrKeyNotFound(key any) error {
	return t.Tk.Errorf("key '%v' not found", key)
}

func (t *Term) Errorf(template string, args ...any) (err error) {
	err = t.Tk.Errorf(template, args...)
	return
}

func (t *Term) CheckOperands() (err error) {
	switch t.Position {
	case PosInfix:
		if t.Children == nil || len(t.Children) != 2 || t.anyChildrenNil() {
			err = t.Tk.Errorf("infix operator %q requires two non-nil operands, got %d", t.Source(), t.GetChildCount())
		}
	case PosPrefix:
		if t.Children == nil || len(t.Children) != 1 || t.Children[0] == nil {
			err = t.Tk.Errorf("prefix operator %q requires one non-nil operand", t.Tk.String())
		}
	case PosPostfix:
		if t.Children == nil || len(t.Children) != 1 || t.anyChildrenNil() {
			err = t.Tk.Errorf("postfix operator %q requires one non-nil operand", t.Tk.String())
		}
	case PosMultifix:
		if len(t.Children) < 3 || t.anyChildrenNil() {
			err = t.Tk.Errorf("infix operator %q requires at least three not operands, got %d", t.Source(), t.GetChildCount())
		}
	}
	return
}

func (t *Term) anyChildrenNil() bool {
	for _, child := range t.Children {
		if child == nil {
			return true
		}
	}
	return false
}
func (t *Term) EvalInfix(ctx kern.ExprContext) (leftValue, rightValue any, err error) {
	if err = t.CheckOperands(); err == nil {
		if leftValue, err = t.Children[0].Compute(ctx); err == nil {
			rightValue, err = t.Children[1].Compute(ctx)
		}
	}
	return
}

func (t *Term) EvalPrefix(ctx kern.ExprContext) (childValue any, err error) {
	if err = t.CheckOperands(); err == nil {
		childValue, err = t.Children[0].Compute(ctx)
	}
	return
}

// NOTE Temporary solution to support function parameters with default value

func (t *Term) ForceChild(c *Term) {
	if t.Children == nil {
		t.Children = make([]*Term, 0, 1)
	}
	t.Children = append(t.Children, c)
}
