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

// operator-rel.go
package expr

//-------- equal term

func newEqualTerm(tk *Token) (inst *term) {
	return &term{
		tk: *tk,
		// class:    classOperator,
		// kind:     kindBool,
		children: make([]*term, 0, 2),
		position: posInfix,
		priority: priRelational,
		evalFunc: evalEqual,
	}
}

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

	if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
		return
	}

	if isNumber(leftValue) && isNumber(rightValue) {
		if isInteger(leftValue) && isInteger(rightValue) {
			li, _ := leftValue.(int64)
			ri, _ := rightValue.(int64)
			v = li == ri
		} else {
			v = numAsFloat(leftValue) == numAsFloat(rightValue)
		}
	} else if isString(leftValue) && isString(rightValue) {
		ls, _ := leftValue.(string)
		rs, _ := rightValue.(string)
		v = ls == rs
	} else {
		err = self.errIncompatibleTypes(leftValue, rightValue)
	}
	return
}

//-------- not equal term

func newNotEqualTerm(tk *Token) (inst *term) {
	return &term{
		tk: *tk,
		// class:    classOperator,
		// kind:     kindBool,
		children: make([]*term, 0, 2),
		position: posInfix,
		priority: priRelational,
		evalFunc: evalNotEqual,
	}
}

func evalNotEqual(ctx ExprContext, self *term) (v any, err error) {
	if v, err = evalEqual(ctx, self); err == nil {
		b, _ := toBool(v)
		v = !b
	}
	return
}

// func evalNotEqual(ctx exprContext, self *term) (v any, err error) {
// 	var leftValue, rightValue any

// 	if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
// 		return
// 	}

// 	if isNumber(leftValue) && isNumber(rightValue) {
// 		if isInteger(leftValue) && isInteger(rightValue) {
// 			li, _ := leftValue.(int64)
// 			ri, _ := rightValue.(int64)
// 			v = li != ri
// 		} else {
// 			v = numAsFloat(leftValue) != numAsFloat(rightValue)
// 		}
// 	} else if isString(leftValue) && isString(rightValue) {
// 		ls, _ := leftValue.(string)
// 		rs, _ := rightValue.(string)
// 		v = ls != rs
// 	} else {
// 		err = self.errIncompatibleTypes(leftValue, rightValue)
// 	}
// 	return
// }

//-------- less term

func newLessTerm(tk *Token) (inst *term) {
	return &term{
		tk: *tk,
		// class:    classOperator,
		// kind:     kindBool,
		children: make([]*term, 0, 2),
		position: posInfix,
		priority: priRelational,
		evalFunc: evalLess,
	}
}

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

	if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
		return
	}

	if isNumber(leftValue) && isNumber(rightValue) {
		if isInteger(leftValue) && isInteger(rightValue) {
			li, _ := leftValue.(int64)
			ri, _ := rightValue.(int64)
			v = li < ri
		} else {
			v = numAsFloat(leftValue) < numAsFloat(rightValue)
		}
	} else if isString(leftValue) && isString(rightValue) {
		ls, _ := leftValue.(string)
		rs, _ := rightValue.(string)
		v = ls < rs
	} else {
		err = self.errIncompatibleTypes(leftValue, rightValue)
	}
	return
}

//-------- less or equal term

func newLessEqualTerm(tk *Token) (inst *term) {
	return &term{
		tk:       *tk,
		children: make([]*term, 0, 2),
		position: posInfix,
		priority: priRelational,
		evalFunc: evalLessEqual,
	}
}

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

	if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
		return
	}

	if isNumber(leftValue) && isNumber(rightValue) {
		if isInteger(leftValue) && isInteger(rightValue) {
			li, _ := leftValue.(int64)
			ri, _ := rightValue.(int64)
			v = li <= ri
		} else {
			v = numAsFloat(leftValue) <= numAsFloat(rightValue)
		}
	} else if isString(leftValue) && isString(rightValue) {
		ls, _ := leftValue.(string)
		rs, _ := rightValue.(string)
		v = ls <= rs
	} else {
		err = self.errIncompatibleTypes(leftValue, rightValue)
	}
	return
}

//-------- greater term

func newGreaterTerm(tk *Token) (inst *term) {
	return &term{
		tk: *tk,
		// class:    classOperator,
		// kind:     kindBool,
		children: make([]*term, 0, 2),
		position: posInfix,
		priority: priRelational,
		evalFunc: evalGreater,
	}
}

func evalGreater(ctx ExprContext, self *term) (v any, err error) {
	if v, err = evalLessEqual(ctx, self); err == nil {
		b, _ := toBool(v)
		v = !b
	}
	return
}

//-------- greater or equal term

func newGreaterEqualTerm(tk *Token) (inst *term) {
	return &term{
		tk: *tk,
		// class:    classOperator,
		// kind:     kindBool,
		children: make([]*term, 0, 2),
		position: posInfix,
		priority: priRelational,
		evalFunc: evalGreaterEqual,
	}
}

func evalGreaterEqual(ctx ExprContext, self *term) (v any, err error) {
	if v, err = evalLess(ctx, self); err == nil {
		b, _ := toBool(v)
		v = !b
	}
	return
}

// init
func init() {
	registerTermConstructor(SymDoubleEqual, newEqualTerm)
	registerTermConstructor(SymNotEqual, newNotEqualTerm)
	registerTermConstructor(SymLess, newLessTerm)
	registerTermConstructor(SymLessOrEqual, newLessEqualTerm)
	registerTermConstructor(SymGreater, newGreaterTerm)
	registerTermConstructor(SymGreaterOrEqual, newGreaterEqualTerm)
}