package table;

import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import static sexpression.Nil.NIL;
import static sexpression.Symbol.T;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class ExecutionContextTest {

    private ExecutionContext executionContext;

    public ExecutionContextTest() {
        this.executionContext = ExecutionContext.getInstance();
    }

    @Before
    public void setUp() {
        executionContext.clearContext();
    }

    @After
    public void tearDown() {
        executionContext.clearContext();
    }

    @Test
    public void assignANewScope() {
        SymbolTable scope = new SymbolTable();
        executionContext.setScope(scope);

        assertThat(executionContext.getScope(), is(scope));
    }

    @Test
    public void clearContext() {
        SymbolTable scope = new SymbolTable();
        executionContext.setScope(scope);

        assertThat(executionContext.getScope(), is(scope));
        executionContext.clearContext();
        assertThat(executionContext.getScope(), is(not(scope)));
        assertThat(executionContext.getScope().getParent(), is(nullValue()));
    }

    @Test
    public void lookupVariable() {
        executionContext.getScope().put("test", T);

        assertThat(executionContext.lookupSymbolValue("test"), is(T));
    }

    @Test
    public void lookupLocalVariable() {
        SymbolTable scope = new SymbolTable(executionContext.getScope());

        scope.put("local", T);
        executionContext.setScope(scope);

        assertThat(executionContext.lookupSymbolValue("local"), is(T));
    }

    @Test
    public void lookupGlobalVariable() {
        SymbolTable scope = new SymbolTable(executionContext.getScope());

        executionContext.getScope().put("global", T);
        executionContext.setScope(scope);

        assertThat(executionContext.lookupSymbolValue("global"), is(T));
    }

    @Test
    public void lookupShadowedVariable() {
        SymbolTable scope = new SymbolTable(executionContext.getScope());

        scope.put("shadowed", NIL);
        executionContext.getScope().put("shadowed", T);
        executionContext.setScope(scope);

        assertThat(executionContext.lookupSymbolValue("shadowed"), is(NIL));
    }

    @Test
    public void restoreGlobalContext() {
        SymbolTable global = executionContext.getScope();
        SymbolTable scope1 = new SymbolTable(global);
        SymbolTable scope2 = new SymbolTable(scope1);
        SymbolTable scope3 = new SymbolTable(scope2);
        executionContext.setScope(scope3);

        assertThat(executionContext.getScope().isGlobal(), is(false));
        executionContext.restoreGlobalScope();
        assertThat(executionContext.getScope().isGlobal(), is(true));
        assertThat(executionContext.getScope(), is(global));
    }

}
