//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// PLEASE DO NOT EDIT WITHOUT THE EXPLICIT CONSENT OF THE AUTHOR! \\
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\

package ilog.language.design.types;

/**
 * @version     Last modified on Fri Nov 15 14:09:00 2002 by hak
 * @author      <a href="mailto:hak@ilog.fr">Hassan A&iuml;t-Kaci</a>
 * @copyright   &copy; 2000-2002 <a href="http://www.ilog.fr/">ILOG, S.A.</a>
 */

import ilog.language.design.kernel.DefinitionException;
import ilog.language.design.base.Instruction;

import ilog.language.util.Comparable;

import java.util.HashMap;
import java.util.ArrayList;
import java.util.Iterator;

/**
 * A symbol object is essentially a (global) name and its type table. The type
 * table is a list of <a href="CodeEntry.html"><tt>CodeEntry</tt></a>
 * objects.
 */

public class Symbol
{
  private String _name;
  private int _index;
  private boolean _noCurrying = false;
  private ArrayList _typeTable = new ArrayList();

  public Symbol (String name)
    {
      _name = name.intern();
    }

  public Symbol (String name, int index)
    {
      _name = name.intern();
      _index = index;
    }

  public final String name ()
    {
      return _name;
    }

  public final int index ()
    {
      return _index;
    }

  public final ArrayList typeTable ()
    {
      return _typeTable;
    }

  public final Symbol setNoCurrying (boolean flag)
    {
      _noCurrying = flag;
      return this;
    }

  //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\

  /**
   * This is a dummy symbol that is given the types allowed for array index sets.
   * It is used for type-checking array dimension expressions for allocation via a
   * dummy <a href="Global.html"><tt>Global</tt></a> constructed with this
   * symbol, thus enabling a choice point to be created for typing these expressions.
   */
  public static final Symbol INDEX_SET = new Symbol("INDEX_SET");
  static
    {
      INDEX_SET.getCodeEntry(Type.INT());
      INDEX_SET.getCodeEntry(Type.INT_RANGE);
      INDEX_SET.getCodeEntry(new SetType());
    }

  /**
   * This is a dummy symbol that is given the types allowed for map index sets.
   * It is used for type-checking array dimension expressions for allocation via a
   * dummy <a href="../kernel/Global.html"><tt>Global</tt></a> constructed with this
   * symbol, thus enabling a choice point to be created for typing these expressions.
   */
  public static final Symbol INDEXABLE = new Symbol("INDEXABLE");
  static
    {
      INDEXABLE.getCodeEntry(Type.INT_RANGE);
      INDEXABLE.getCodeEntry(new SetType());
    }

  /**
   * This is a dummy symbol that is given the types allowed for collections.  It
   * is used for type-checking it using a dummy <a href="../kernel/Global.html">
   * <tt>Global</tt></a> constructed with this symbol, thus enabling choice points
   * to be created for typing its expression.
   */
  public static final Symbol COLLECTION = new Symbol("COLLECTION");

  static
    { // NB: Although this is currently identical to INDEXABLE, this will not be the case
      // when other collections such as lists and bags are implemented.

      COLLECTION.getCodeEntry(Type.INT_RANGE);
      COLLECTION.getCodeEntry(new SetType());
//        COLLECTION.getCodeEntry(new ListType());
//        COLLECTION.getCodeEntry(new BagType());
    }

  //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\

  /**
   * This method registers a definitively checked type of this global symbol.
   * It is meant to be called <i>only</i> (and it is) by its namesake method in
   * the class <a href="../kernel/Definition.html"><tt>Definition</tt></a>,
   * itself only called after type-checking of this symbol's definition has
   * been completed; namely, after the <tt>setCheckedType</tt> method has been
   * invoked on the definition, which ensures that its type argument has been
   * <i>standardized</i>.
   */
  public final DefinedEntry registerCodeEntry (Type type) throws DefinitionException
    {
      if (_name == "a")
        System.out.println("Registering symbol a with type "+type);

      CodeEntry entry = getCodeEntry(type);

      if (entry.isBuiltIn() || entry.isDefinedField())
        throw new DefinitionException("cannot redefine "+entry);

      return (DefinedEntry)entry;
    }

  /**
   * Assigns and returns a <a href="CodeEntry.html"><tt>CodeEntry</tt></a>
   * corresponding to the specified <a href="Type.html"><tt>Type</tt></a>
   * for this symbol if one does not exist for with this type; otherwise, returns
   * the existing one.
   */
  public final CodeEntry getCodeEntry (Type type)
    {
      return getCodeEntry(type,false);
    }

  /**
   * Assigns and returns a <a href="CodeEntry.html"><tt>CodeEntry</tt></a>
   * corresponding to the specified <a href="Type.html"><tt>Type</tt></a>
   * for this symbol if one does not exist for with this type.  If the
   * <tt>noDuplicates</tt> is true, a <tt>DuplicateCodeEntryException</tt>
   * is thrown whenever an entry already existed for this type; otherwise,
   * returns the existing code entry.
   */
  public final CodeEntry getCodeEntry (Type type, boolean noDuplicates)
    throws DuplicateCodeEntryException
    {
      return getCodeEntry(new DefinedEntry(this,type),noDuplicates);
    }

  /**
   * Assigns the specified <a href="CodeEntry.html"><tt>CodeEntry</tt></a>
   * to this symbol if none exists with its type for this symbol.  If the
   * <tt>noDuplicates</tt> is true, a <tt>DuplicateCodeEntryException</tt>
   * is thrown whenever an entry already existed for this type; otherwise,
   * returns the existing code entry.
   */
  public final CodeEntry getCodeEntry (CodeEntry entry, boolean noDuplicates)
    throws DuplicateCodeEntryException
    {
      if (_name == "a")
        System.out.println("Looking for entry "+entry+" in table "+_typeTable);

      int index = _typeTable.lastIndexOf(entry);
      
      if (index == -1)
        {
          _typeTable.add(entry);

          if (_name == "a")
            {
              System.out.println("Not found - adding entry "+entry);
              ilog.language.tools.Debug.step("Now table is "+_typeTable);
            }

          return entry;
        }

      if (noDuplicates) throw new DuplicateCodeEntryException(entry);

      if (_name == "a")
        {
          System.out.println("Found: "+_typeTable.get(index));
          ilog.language.tools.Debug.step("Now table is "+_typeTable);
        }
      
      return (CodeEntry)_typeTable.get(index);
    }

  /**
   * Removes the latest code entry in this symbol's type table.
   */
  public final void removeLatestEntry ()
    {
      _typeTable.remove(_typeTable.size()-1);
    }      

  /**
   * Installs this symbol as a built-in with specified type and instruction.
   * <b>N.B.:</b> If this type was defined for this symbol before, a
   * <tt>DuplicateCodeEntryException</tt> is thrown.
   */
  public final void defineBuiltIn (Type type, Instruction builtIn) throws DuplicateCodeEntryException
    {
      if (isDefined(type))
        throw new DuplicateCodeEntryException(this + " : " + type);

      if (type.kind() == Type.FUNCTION && _noCurrying)
        ((FunctionType)type).setNoCurrying();

      _typeTable.add(new BuiltinEntry(this,type.standardize(),builtIn));
    }

  public final boolean isDefined ()
    {
      return !_typeTable.isEmpty();
    }

  public final boolean isDefined (Type type)
    {
      if (!isDefined())
        return false;

      type = type.standardize();

      for (int i=_typeTable.size(); i-->0;)
        if (type.equals(((CodeEntry)_typeTable.get(i)).type()))
          return true;

      return false;
    }

  public final void showDefinedEntries ()
    {
      boolean foundEntries = false;

      for (Iterator i=_typeTable.iterator(); i.hasNext();)
        {
          CodeEntry entry = (CodeEntry)i.next();
          if (entry.isBuiltIn()) continue;
          Type.resetNames();
          System.out.println("\t"+entry);
          foundEntries = true;
        }

      if (foundEntries) System.out.println();
    }

  public final void showCodeEntries ()
    {
      for (Iterator i=_typeTable.iterator(); i.hasNext();)
        {
          Type.resetNames();
          System.out.println("\t"+i.next());
        }

      System.out.println();
    }

  public final String toString ()
    {
      return _name;
    }
}
