package Models.Expressions;

import Exceptions.BaseException;
import Models.Program.IDictionary;
import Models.Program.IHeap;
import Models.Types.BooleanType;
import Models.Types.IType;
import Models.Values.BooleanValue;
import Models.Values.IValue;

public class LogicExpression implements IExpression {
    private IExpression left;
    private IExpression right;
    private String operator;

    public LogicExpression(IExpression left, IExpression right, String operator) {
        this.left = left;
        this.right = right;
        this.operator = operator;
    }

    public IValue evaluate(IDictionary<String, IValue> table, IHeap heap) throws BaseException {
        IValue leftValue = left.evaluate(table, heap);
        IValue rightValue = right.evaluate(table, heap);

        if (leftValue.getType().equals(new BooleanType())) {
            boolean leftBool = ((BooleanValue)leftValue).getValue();

            if (rightValue.getType().equals(new BooleanType())) {
                boolean rightBool = ((BooleanValue)rightValue).getValue();

                switch (operator) {
                    case "&&":
                        return new BooleanValue(leftBool && rightBool);
                    case "||":
                        return new BooleanValue(leftBool || rightBool);
                    default:
                        throw new BaseException("Invalid operator!");
                }
            } else {
                throw new BaseException("Second operand is not a boolean!");
            }
        } else {
            throw new BaseException("First operand is not a boolean!");
        }
    }

    public IExpression copy() {
        return new LogicExpression(left.copy(), right.copy(), operator);
    }

    public String toString() {
        return left.toString() + " " + operator + " " + right.toString();
    }

    public IType typeCheck(IDictionary<String, IType> typeTable) throws BaseException {
        IType leftType, rightType;
        leftType = left.typeCheck(typeTable);
        rightType = right.typeCheck(typeTable);
        if (leftType.equals(new BooleanType())) {
            if (rightType.equals(new BooleanType())) {
                return new BooleanType();
            } else {
                throw new BaseException("Second operand is not a boolean!");
            }
        } else {
            throw new BaseException("First operand is not a boolean!");
        }
    }
}
