package table;

import static function.builtin.cons.LIST.makeList;
import static sexpression.Nil.NIL;

import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

import sexpression.Cons;
import sexpression.SExpression;
import sexpression.Symbol;

public class SymbolTable {

    private HashMap<String, SExpression> table;
    private SymbolTable parent;

    public SymbolTable() {
        this(null);
    }

    public SymbolTable(SymbolTable parent) {
        this.table = new HashMap<String, SExpression>();
        this.parent = parent;
    }

    public boolean contains(String symbolName) {
        return table.containsKey(symbolName);
    }

    public SExpression get(String symbolName) {
        return table.get(symbolName);
    }

    public void put(String symbolName, SExpression value) {
        table.put(symbolName, value);
    }

    public SymbolTable getParent() {
        return parent;
    }

    public boolean isGlobal() {
        return parent == null;
    }

    public Cons toList() {
        Cons context = NIL;

        for (Entry<String, SExpression> binding : getSortedBindings())
            context = append(context, makeList(makeSymbolValuePair(binding)));

        return context;
    }

    private Set<Entry<String, SExpression>> getSortedBindings() {
        return new TreeMap<>(table).entrySet();
    }

    private Cons makeSymbolValuePair(Entry<String, SExpression> binding) {
        return new Cons(new Symbol(binding.getKey()), makeList(binding.getValue()));
    }

    // TODO - extract into built in append function
    private Cons append(Cons firstList, Cons secondList) {
        if (firstList.isNull())
            return secondList; // FIXME - copy

        Cons appendedList = firstList; // FIXME - copy
        Cons appender = appendedList;

        for (; !((Cons) appender.getRest()).isNull(); appender = (Cons) appender.getRest()) {}

        appender.setRest(secondList); // FIXME - copy

        return appendedList;
    }

}
