package table;

import java.util.Stack;

import function.LispFunction;
import sexpression.SExpression;

public class ExecutionContext {

    private static ExecutionContext uniqueInstance = new ExecutionContext();

    public static ExecutionContext getInstance() {
        return uniqueInstance;
    }

    private SymbolTable scope;
    private Stack<LispFunctionRecurInfo> functionCalls;
    private boolean recur;

    private ExecutionContext() {
        clearContext();
    }

    public void clearContext() {
        this.scope = new SymbolTable();
        this.functionCalls = new Stack<>();
        this.clearRecur();
    }

    public SymbolTable getScope() {
        return scope;
    }

    public void setScope(SymbolTable scope) {
        this.scope = scope;
    }

    public void restoreGlobalScope() {
        while (!scope.isGlobal())
            scope = scope.getParent();
    }

    public SExpression lookupSymbolValue(String symbolName) {
        for (SymbolTable t = scope; t != null; t = t.getParent())
            if (t.contains(symbolName))
                return t.get(symbolName);

        return null;
    }

    public void pushFunctionCall(LispFunction function) {
        functionCalls.push(new LispFunctionRecurInfo(function));
    }

    public void popFunctionCall() {
        functionCalls.pop();
    }

    public boolean isInFunctionCall() {
        return !functionCalls.empty();
    }

    public LispFunction getCurrentFunction() {
        return functionCalls.peek().getLispFunction();
    }

    public boolean isRecur() {
        return recur;
    }

    public void setRecur() {
        recur = true;
    }

    public void clearRecur() {
        recur = false;
    }

    public boolean isRecurInitializing() {
        return functionCalls.peek().isRecurInitializing();
    }

    public void setRecurInitializing() {
        functionCalls.peek().setRecurInitializing();
    }

    public void clearRecurInitializing() {
        functionCalls.peek().clearRecurInitializing();
    }

    public static class LispFunctionRecurInfo {

        private LispFunction lispFunction;
        private boolean recurInitializing;

        public LispFunctionRecurInfo(LispFunction lispFunction) {
            this.lispFunction = lispFunction;
            this.clearRecurInitializing();
        }

        public boolean isRecurInitializing() {
            return recurInitializing;
        }

        public void setRecurInitializing() {
            this.recurInitializing = true;
        }

        public void clearRecurInitializing() {
            this.recurInitializing = false;
        }

        public LispFunction getLispFunction() {
            return lispFunction;
        }
    }
}
