package code.table;

import code.stuff.Logger;
import code.stuff.Tracer;
import code.symbols.DataSymbol;
import code.symbols.Symbol;

import java.util.Enumeration;
import java.util.Vector;

/**
 * This class defines a mapping from the integers 1,2,3,...  to
 * the symbols of a program.  A symbol in a term is identified
 * by this integer.  The mapping is injective and provides fast
 * access to the symbol.
 * <P>
 * This table is written during the modules loading phase by ModuleTable
 * and/or SymbolTable and is read during the execution of a program.
 * The visibility of methods is suggested by this modus of use.
 *
 * @author Sergio Antoy
 * @since June 24, 2004
 */

public class MapTable {

    /** Singleton class */
    private MapTable() {}

    /** Mapping representation */
    private final static Vector intToSymbol = new Vector();

    /** Counts the ids used so far */
    private static int nextId = 0;

    static int nextId() {
        intToSymbol.add(null);
        return nextId++;
    }

    // ------------------------------------------------------------------

    /** Saved value of NextId when setBaseline was called. */
    private static int baseline;
    /** Save the value of nextId in baseline.
     *	The intent is to set this value after all the builtin
     *	modules have been loaded.
     *  When an ordinary (non-builtin) module is loaded,
     *  and all its imported modules are loaded as well,
     *  everything above the baseline in the MapTable is discarded.
     *  This has the effect to remove all the symbols except
     *  those belonging to builtin modules.
     */
    static void setBaseline() {
	baseline = nextId;
    }
    /** Remove all the symbols except those below the baseline.
     *  This operation also reset nextId with the value saved in
     *  the baseline.
     */
    static void resetBaseline() {
	while (nextId > baseline)
	    intToSymbol.removeElementAt(--nextId);
    }

    // ------------------------------------------------------------------
    // Symbol management operations

    static void installSymbol(Symbol symbol, int id) {
        intToSymbol.setElementAt(symbol, id);
    }

    public static DataSymbol getSymbol(int id) {
        try {
	    DataSymbol symbol = (DataSymbol) intToSymbol.elementAt(id);
	    if (symbol == null) {
		// TODO: find the symbol name by looking at every symbol table
		String qualifiedName = ModuleTable.getUndefinedSymbol(id);
		throw new UndefinedSymbol(qualifiedName);
	    } else
		return symbol;
        } catch (ArrayIndexOutOfBoundsException ex) {
            throw new RuntimeException
                    ("Consistency error in MapTable. No symbol id " + id);
        }
    }

    public static int getId(String symbolName)
            throws AmbiguousSymbolException {
        // this is quite inefficient,
        // it should be used only for parsing user interaction
        int howManyFound = 0;
        // legal ids are non-negative
        int id = -1;
        for (int i = 0; i < intToSymbol.size(); i++) {
            DataSymbol ds = (DataSymbol) intToSymbol.elementAt(i);
            if (ds != null && symbolName.equals(ds.symbolName)) {
                howManyFound++;
                id = i;
            }
        }
        if (howManyFound <= 0)
            throw new UndefinedSymbol(symbolName);
        else if (howManyFound >= 2)
            throw new AmbiguousSymbolException(symbolName);
        else    // howManyFound == 1
            return id;
    }

    // ------------------------------------------------------------------

    public static Enumeration getAllSymbols() {
        return intToSymbol.elements();
    }


}

