package com.example.Models.Program;

import java.io.BufferedReader;
import java.util.Set;

import com.example.Exceptions.BaseException;
import com.example.Models.Statements.IStatement;
import com.example.Models.Types.IType;
import com.example.Models.Values.IValue;
import com.example.Models.Values.StringValue;;

public class ProgramState{
    //region Fields
    private IStack<IStatement> stack;
    private IDictionary<String, IValue> symbolTable;
    private IQueue<IValue> output;
    private IDictionary<StringValue, BufferedReader> fileTable;
    private IStatement originalProgram;
    private IHeap heap;
    private ISemaphoreTable semaphoreTable;
    public Integer id;
    private static Set<Integer> ids = new java.util.HashSet<>();
    //endregion

    //region Exposed Methods
    public ProgramState(IStack<IStatement> stack, IDictionary<String, IValue> symbolTable, IQueue<IValue> output, IDictionary<StringValue, BufferedReader> fileTable, IHeap heap, ISemaphoreTable semaphoreTable, IStatement program) throws BaseException{
        this.stack = stack;
        this.symbolTable = symbolTable;
        this.output = output;
        this.fileTable = fileTable;
        this.originalProgram = program.copy();
        this.stack.push(program);
        this.heap = heap;
        this.semaphoreTable = semaphoreTable;
        this.id = getNewId();
    }

    private static Integer getNewId(){
        Integer newId = 1;
        synchronized (ids){
            while(ids.contains(newId)){
                newId++;
            }
            ids.add(newId);
        }
        return newId;
    }

    public IStack<IStatement> getStack(){
        return this.stack;
    }

    public IDictionary<String, IValue> getSymbolTable(){
        return this.symbolTable;
    }

    public IQueue<IValue> getOutput(){
        return this.output;
    }

    public IDictionary<StringValue, BufferedReader> getFileTable(){
        return this.fileTable;
    }

    public IStatement getOriginalProgram(){
        return this.originalProgram;
    }

    public IHeap getHeap(){
        return this.heap;
    }

    public ISemaphoreTable getSemaphoreTable(){
        return this.semaphoreTable;
    }

    public boolean isNotCompleted(){
        return !this.stack.isEmpty();
    }

    public ProgramState oneStep() throws BaseException{
        IStack<IStatement> stack = this.getStack();
        if (stack.isEmpty()) {
            throw new RuntimeException("Execution stack is empty!");
        }
        IStatement currentStatement = stack.pop();
        return currentStatement.execute(this);
    }

    public String toString(){
        return "Id: " + id + "\n" +  "Stack:\n" + this.stack.toString() + "\nSymbol Table:\n" + this.symbolTable.toString() + "\nOutput:\n" + this.output.toString() + "\nFile Table:\n" + this.fileTable.toString() + "\n" + "Heap:" + "\n" + this.heap.toString() + "\n" + "Semaphore Table:" + "\n" + this.semaphoreTable.toString() + "\n";
    }

    public void typeCheck() throws BaseException{
        this.originalProgram.typeCheck(new GenericDictionary<String, IType>());
    }
    //#endregion
}