package com.example.Models.Statements;


import com.example.Exceptions.BaseException;
import com.example.Models.Expressions.IExpression;
import com.example.Models.Program.IDictionary;
import com.example.Models.Program.ProgramState;
import com.example.Models.Types.IType;
import com.example.Models.Types.IntegerType;
import com.example.Models.Types.StringType;
import com.example.Models.Values.IntegerValue;
import com.example.Models.Values.StringValue;

public class ReadFileStatement implements IStatement{
    private IExpression expression;
    private String variableName;
    
    public ReadFileStatement(IExpression expression, String variableName){
        this.expression = expression;
        this.variableName = variableName;
    }

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

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

    public ProgramState execute(ProgramState programState) throws BaseException{
        var symbolTable = programState.getSymbolTable();
        var fileTable = programState.getFileTable();
        var heap = programState.getHeap();
        if(!symbolTable.contains(this.variableName)){
            throw new BaseException("ReadFileStatement: Variable is not defined");
        }
        var value = symbolTable.get(this.variableName);
        if(!value.getType().equals(new IntegerType())){
            throw new BaseException("ReadFileStatement: Variable is not of type IntegerType");
        }
        var expressionValue = this.expression.evaluate(symbolTable, heap);
        if(!expressionValue.getType().equals(new StringType())){
            throw new BaseException("ReadFileStatement: Expression is not of type StringType");
        }
        if(!fileTable.contains((StringValue)expressionValue)){
            throw new BaseException("ReadFileStatement: File is not open");
        }
        var bufferedReader = fileTable.get((StringValue)expressionValue);
        try{
            var line = bufferedReader.readLine();
            if(line == null){
                line = "0";
            }
            symbolTable.update(this.variableName, new IntegerValue(Integer.parseInt(line)));
        }
        catch(Exception exception){
            throw new BaseException("ReadFileStatement: File is empty");
        }
        return null;
    }

    public IDictionary<String, IType> typeCheck(IDictionary<String, IType> typeTable) throws BaseException{
        var expressionType = this.expression.typeCheck(typeTable);
        if(!expressionType.equals(new StringType())){
            throw new BaseException("ReadFileStatement: Expression is not of type StringType");
        }
        var variableType = typeTable.get(this.variableName);
        if(!variableType.equals(new IntegerType())){
            throw new BaseException("ReadFileStatement: Variable is not of type IntegerType");
        }
        return typeTable;
    }
}
