package Models.Statements;

import Exceptions.BaseException;
import Models.Expressions.IExpression;
import Models.Program.IDictionary;
import Models.Program.IHeap;
import Models.Program.IStack;
import Models.Program.ProgramState;
import Models.Types.BooleanType;
import Models.Types.IType;
import Models.Values.IValue;
import Models.Values.BooleanValue;

public class WhileStatement implements IStatement {
    private IStatement statement;
    private IExpression expression;

    public WhileStatement(IStatement statement, IExpression expression) {
        this.statement = statement;
        this.expression = expression;
    }

    public ProgramState execute(ProgramState state) throws BaseException {
        IStack<IStatement> stack = state.getStack();
        IHeap heap = state.getHeap();
        IDictionary<String, IValue> symbolTable = state.getSymbolTable();

        IValue condition = expression.evaluate(symbolTable, heap);
        if (!condition.getType().equals(new BooleanType())) {
            throw new BaseException("Condition is not a boolean.");
        }

        if (((BooleanValue) condition).getValue()) {
            stack.push(this);
            stack.push(statement);
        }

        return null;
    }

    public String toString() {
        return "while(" + expression.toString() + ") {" + statement.toString() + "}";
    }

    public IStatement copy() {
        return new WhileStatement(statement.copy(), expression.copy());
    }

    public IDictionary<String, IType> typeCheck(IDictionary<String, IType> typeTable) throws BaseException {
        IType expressionType = expression.typeCheck(typeTable);
        if (expressionType.equals(new BooleanType())) {
            statement.typeCheck(typeTable.copy());
            return typeTable;
        } else {
            throw new BaseException("Condition is not a boolean!");
        }
    }
}
