package com.example.Models.Expressions;

import com.example.Exceptions.BaseException;
import com.example.Models.Program.IDictionary;
import com.example.Models.Program.IHeap;
import com.example.Models.Types.BooleanType;
import com.example.Models.Types.IType;
import com.example.Models.Types.IntegerType;
import com.example.Models.Values.BooleanValue;
import com.example.Models.Values.IValue;
import com.example.Models.Values.IntegerValue;

public class RelationalExpression implements IExpression {
    private IExpression leftExpression;
    private IExpression rightExpression;
    private String operator;

    public RelationalExpression(IExpression leftExpression, IExpression rightExpression, String operator) {
        this.leftExpression = leftExpression;
        this.rightExpression = rightExpression;
        this.operator = operator;
    }

    public IExpression copy() {
        return new RelationalExpression(this.leftExpression.copy(), this.rightExpression.copy(), this.operator);
    }

    public String toString() {
        return this.leftExpression.toString() + " " + this.operator + " " + this.rightExpression.toString();
    }

    public IValue evaluate(IDictionary<String, IValue> symbolTable, IHeap heap) throws BaseException {
        var leftValue = this.leftExpression.evaluate(symbolTable, heap);
        var rightValue = this.rightExpression.evaluate(symbolTable, heap);
        if (leftValue.getType().equals(new IntegerType()) && rightValue.getType().equals(new IntegerType())) {
            var leftIntegerValue = (IntegerValue) leftValue;
            var rightIntegerValue = (IntegerValue) rightValue;
            var leftInteger = leftIntegerValue.getValue();
            var rightInteger = rightIntegerValue.getValue();
            switch (this.operator) {
                case "<":
                    return new BooleanValue(leftInteger < rightInteger);
                case "<=":
                    return new BooleanValue(leftInteger <= rightInteger);
                case "==":
                    return new BooleanValue(leftInteger == rightInteger);
                case "!=":
                    return new BooleanValue(leftInteger != rightInteger);
                case ">":
                    return new BooleanValue(leftInteger > rightInteger);
                case ">=":
                    return new BooleanValue(leftInteger >= rightInteger);
                default:
                    throw new BaseException("RelationalExpression: Invalid operator");
            }
        }
        throw new BaseException("RelationalExpression: Operands are not of type IntegerType");
    }
    
    public IType typeCheck(IDictionary<String, IType> typeTable) throws BaseException {
        var leftType = this.leftExpression.typeCheck(typeTable);
        var rightType = this.rightExpression.typeCheck(typeTable);
        if (leftType.equals(new IntegerType())) {
            if (rightType.equals(new IntegerType())) {
                return new BooleanType();
            }
            throw new BaseException("RelationalExpression: Right operand is not of type IntegerType");
        }
        throw new BaseException("RelationalExpression: Left operand is not of type IntegerType");
    }
}