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

// operator-default.go
package expr

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

//-------- default term

func newDefaultTerm(tk *scan.Token) (inst *scan.Term) {
	return &scan.Term{
		Tk:       *tk,
		Children: make([]*scan.Term, 0, 2),
		Position: scan.PosInfix,
		Priority: scan.PriDefault,
		EvalFunc: evalDefault,
	}
}

func evalDefault(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
	var rightValue any

	if err = opTerm.CheckOperands(); err != nil {
		return
	}

	leftTerm := opTerm.Children[0]
	if leftTerm.Tk.Sym != sym.SymVariable {
		// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
		err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
		return
	}

	if leftValue, exists := ctx.GetVar(leftTerm.Source()); exists {
		v = leftValue
	} else if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
		v = rightValue
	}
	return
}

//-------- alternate term

func newAlternateTerm(tk *scan.Token) (inst *scan.Term) {
	return &scan.Term{
		Tk:       *tk,
		Children: make([]*scan.Term, 0, 2),
		Position: scan.PosInfix,
		Priority: scan.PriDefault,
		EvalFunc: evalAlternate,
	}
}

func evalAlternate(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
	var rightValue any

	if err = opTerm.CheckOperands(); err != nil {
		return
	}

	leftTerm := opTerm.Children[0]
	if leftTerm.Tk.Sym != sym.SymVariable {
		// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
		err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
		return
	}

	if leftValue, exists := ctx.GetVar(leftTerm.Source()); exists && leftValue != nil {
		if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
			v = rightValue
		}
	} else {
		v = leftValue
	}
	return
}

//-------- default assign term

func newDefaultAssignTerm(tk *scan.Token) (inst *scan.Term) {
	return &scan.Term{
		Tk:       *tk,
		Children: make([]*scan.Term, 0, 2),
		Position: scan.PosInfix,
		Priority: scan.PriDefault,
		EvalFunc: evalAssignDefault,
	}
}

func evalAssignDefault(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
	var rightValue any

	if err = opTerm.CheckOperands(); err != nil {
		return
	}

	leftTerm := opTerm.Children[0]
	if leftTerm.Tk.Sym != sym.SymVariable {
		// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
		err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
		return
	}

	if leftValue, exists := ctx.GetVar(leftTerm.Source()); exists {
		v = leftValue
	} else if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
		if functor, ok := rightValue.(kern.Functor); ok {
			//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
			ctx.RegisterFunc(leftTerm.Source(), functor, kern.TypeAny, []kern.ExprFuncParam{
				kern.NewFuncParamFlag(kern.ParamValue, kern.PfDefault|kern.PfRepeat),
			})
		} else {
			v = rightValue
			ctx.UnsafeSetVar(leftTerm.Source(), rightValue)
		}
	}
	return
}

// init
func init() {
	scan.RegisterTermConstructor(sym.SymDoubleQuestion, newDefaultTerm)
	scan.RegisterTermConstructor(sym.SymQuestionEqual, newDefaultAssignTerm)
	scan.RegisterTermConstructor(sym.SymQuestionExclam, newAlternateTerm)
}
