package main

import (
	"fmt"
	"math"
	"math/rand"
)

func opAdd(val1, val2 value) *_number {
	return &_number{num: toNumber(val1) + toNumber(val2)}
}

func opSub(val1, val2 value) *_number {
	return &_number{num: toNumber(val1) - toNumber(val2)}
}

func opMul(val1, val2 value) *_number {
	return &_number{num: toNumber(val1) * toNumber(val2)}
}

func opDiv(val1, val2 value) *_number {
	if num1, num2 := toNumber(val1), toNumber(val2); num2 != 0 {
		return &_number{num: num1 / num2}
	}
	panic(fmt.Errorf("div: %s (%s / %s)", errDivisionByZero, val1.String(), val2.String()))
}

func opMod(val1, val2 value) *_number {
	if num1, num2 := toNumber(val1), toNumber(val2); num2 != 0 {
		return &_number{num: math.Mod(num1, num2)}
	}
	panic(fmt.Errorf("mod: %s (%s %% %s)", errDivisionByZero, val1.String(), val2.String()))
}

func opEq(val1, val2 value) *_bool {
	if isNumber(val1) && isNumber(val2) {
		return &_bool{b: toNumber(val1) == toNumber(val2)}
	}
	if isBool(val1) && isBool(val2) {
		return &_bool{b: toBool(val1) == toBool(val2)}
	}
	if isWord(val1) && isWord(val2) {
		return &_bool{b: escapeWord(toWord(val1)) == escapeWord(toWord(val2))}
	}
	return &_bool{b: false}
}

func opGt(val1, val2 value) *_bool {
	if isNumber(val1) && isNumber(val2) {
		return &_bool{b: toNumber(val1) > toNumber(val2)}
	}
	if isBool(val1) && isBool(val2) {
		return &_bool{b: toBool(val1) && !toBool(val2)}
	}
	if isWord(val1) && isWord(val2) {
		return &_bool{b: escapeWord(toWord(val1)) > escapeWord(toWord(val2))}
	}
	panic(fmt.Errorf("gt: %s (%s > %s)", errIllegalOperandType, val1.String(), val2.String()))
}

func opLt(val1, val2 value) *_bool {
	if isNumber(val1) && isNumber(val2) {
		return &_bool{b: toNumber(val1) < toNumber(val2)}
	}
	if isBool(val1) && isBool(val2) {
		return &_bool{b: !toBool(val1) && toBool(val2)}
	}
	if isWord(val1) && isWord(val2) {
		return &_bool{b: escapeWord(toWord(val1)) < escapeWord(toWord(val2))}
	}
	panic(fmt.Errorf("lt: %s (%s < %s)", errIllegalOperandType, val1.String(), val2.String()))
}

func opAnd(val1, val2 value) *_bool {
	return &_bool{b: toBool(val1) && toBool(val2)}
}

func opOr(val1, val2 value) *_bool {
	return &_bool{b: toBool(val1) || toBool(val2)}
}

func opNot(val1 value) *_bool {
	return &_bool{b: !toBool(val1)}
}

func opRandom(val1 value) *_number {
	return &_number{num: toNumber(val1) * rand.Float64()}
}

func opInt(val1 value) *_number {
	return &_number{num: math.Floor(toNumber(val1))}
}

func opSqrt(val1 value) *_number {
	if num1 := toNumber(val1); num1 >= 0 {
		return &_number{num: math.Sqrt(num1)}
	}
	panic(fmt.Errorf("sqrt: %s (%s)", errNegativeSquareRoot, val1.String()))
}
