package Models.Statements;

import Exceptions.BaseException;
import Models.Expressions.IExpression;
import Models.Program.IDictionary;
import Models.Program.ProgramState;
import Models.Types.IType;
import Models.Types.ReferenceType;
import Models.Values.ReferenceValue;

public class NewStatement implements IStatement {
    private String variableName;
    private IExpression expression;

    public NewStatement(String variableName, IExpression expression) {
        this.variableName = variableName;
        this.expression = expression;
    }

    public IStatement copy() {
        return new NewStatement(this.variableName, this.expression.copy());
    }

    public String toString() {
        return "new(" + this.variableName + ", " + this.expression.toString() + ")";
    }

    public ProgramState execute(ProgramState programState) throws BaseException {
        var symbolTable = programState.getSymbolTable();
        var heap = programState.getHeap();
        if (!symbolTable.contains(this.variableName)) {
            throw new BaseException("NewStatement: Variable is not defined");
        }
        var value = symbolTable.get(this.variableName);
        var expressionValue = this.expression.evaluate(symbolTable, heap);
        if (!value.getType().equals(new ReferenceType(expressionValue.getType()))) {
            throw new BaseException("NewStatement: Variable is not of type ReferenceType");
        }
        var address = heap.add(expressionValue);
        symbolTable.update(this.variableName, new ReferenceValue(address, expressionValue.getType()));
        return null;
    }

    public IDictionary<String, IType> typeCheck(IDictionary<String, IType> typeTable) throws BaseException {
        var variableType = typeTable.get(this.variableName);
        var expressionType = this.expression.typeCheck(typeTable);
        if (!variableType.equals(new ReferenceType(expressionType))) {
            throw new BaseException("NewStatement: Right hand side and Left hand side have different types");
        }
        return typeTable;
    }
}
