package aima.logic;
import java.util.Hashtable;

/**
 * The HashBindings class implements the <code>Bindings</code> interface
 * using a <code>Hashtable</code>.
 *
 * <p> 
 * Note: This implementation may not be appropriate for the various
 * theorem provers which need an implementation of <code>Bindings</code>
 * that allow bindings to be cheaply extended in several different
 * directions.  For that, consider using <code>ListBindings</code>.
 *
 * @author Michael S. Braverman
 * @see ListBindings
 */

public class HashBindings implements Bindings {
  /**
   * Obtains the Failure (representing no consistent <code>Bindings</code>)
   * object.
   *
   * @return The failure bindings object.
   *
   */
   public Bindings getFailure()  { return FAILURE; }

  /**
   * Obtains the Empty (representing no current bindings) object.
   *
   * @return The empty bindings object.
   *
   */
  static public Bindings getEmpty() { return EMPTYBINDINGS; }

  /**
   * Determine if the given <code>Variable</code> is currently bound.
   *
   * @return true iff the <code>Variable</code> is bound.
   * @param var The variable to check.
   */
  public boolean isBound(Variable var) {
    return (getBinding(var) != null);
  }

  /**
   * Determine if this <code>HashBindings</code> object represents the
   * Empty (no current bindings) object.
   *
   * @return true iff this object is the Empty bindings object.
   */
  public boolean  isEmpty() { return (this == EMPTYBINDINGS); }

  /**
   * Determine if this <code>HashBindings</code> object represents the
   * Failure (no consistent bindings) object.
   *
   * @return true iff this object is the Failure bindings object.
   */
  public boolean  isFailure() { return (this == FAILURE); }

  /**
   * Get the current binding of the given <code>Variable</code>.
   *
   * @return The value associated with the variable.
   * @param var The variable to look up.
   */
  public Object getBinding(Variable var) {
    return theTable.get(var);
  }

  /**
   * Extends this <code>HashBindings</code> object with an association between
   * the given <code>Variable</code> and value.
   *
   * <p> 
   * Note: This method changes the <code>HashBindings</code>
   * object on which it is invoked.  This is fine, and efficient, for
   * simple unification applications, but will not work well for the
   * various theorem provers which may want to share a
   * <code>Bindings</code> instance (or part thereof) among many lines
   * of of reasoning.  Instead, consider using a
   * <code>ListBindings</code> instance to manage
   * <code>Bindings</code> for such applications.
   *
   * @return An extended <code>HashBindings</code> object.
   * @param var The variable to bind.
   * @param val The value to which the <code>Variable</code> is to be bound.
   * @see ListBindings 
   */
  public Bindings extendBindings (Variable var, Object val) {
    HashBindings result = this;
    if (this == EMPTYBINDINGS) {
      result = new HashBindings();
    }
    result.theTable.put(var,val);
    return result;
  }

  /**
   * Returns a <code>String</code> representation of this object.
   *
   * @return a <code>String</code> representation of this object.
   */
  public String toString () {
    if (this == EMPTYBINDINGS) {
      return   "[Bindings Empty]";
    } else if (this == FAILURE) {
      return "[Bindings *FAILURE*]";
    } else {
      return "[Bindings: "+ theTable.toString() +"]";
    }
  }

  /**
   * Placeholder (private) so one can not create their own 
   * <code>HashBindings</code>, and must instead start with the initial
   * bindings returned by <code>getEmpty</code> and extend from there.
   *
   * @see getEmpty
   */
  private HashBindings() {
    theTable = new Hashtable();
  }

  /**
   * Contains the <code>Hashtable</code> object the actually stores
   * the <code>Variable</code> bindings.
   */
  private Hashtable theTable;

  /**
   * Indicates an empty set of bindings (indicating unification
   * success, with no variables)
   */ 
  /* (defconstant +no-bindings+ '((nil)) 
     "Indicates unification success, with no variables.") */
  static private Bindings EMPTYBINDINGS = new HashBindings();

  /**
   * Indicates no consistent binding (unification failure).
   *
   * @see getFailure()
   */
  /* (defconstant +fail+ nil "Indicates unification failure") */
  static private Bindings FAILURE = new HashBindings();

  /**
   * Tests out the functionality of this class.
   * @param args Ignored.
   */
  static public void main(String [] args) {
    System.err.println(FAILURE);
    System.err.println(EMPTYBINDINGS);
    Bindings b = HashBindings.getEmpty();
    // test to make sure that variables with the same base name
    // (but maybe represented by different Variable instances)
    // refer to the same variable in the Bindings object.
    // (We need want this to be the case since it is difficult
    //  to intern Variables, so we allow two Variables that just
    //  "look alike" to be the same variable with respect to
    //  the Bindings object)

    // Note that since this is a HashBindings object, only the final
    // bound value of a given variable will be present in the
    // final Bindings (compare with ListBindings)
    b = b.extendBindings(new Variable("x"), new Integer(1));
    b = b.extendBindings(new Variable("y"), new Integer(2));
    b = b.extendBindings(new Variable("y"), new Integer(3));
    b = b.extendBindings(new Variable("y"), new Integer(4));
    b = b.extendBindings(new Variable("z"), new Integer(5));
    b = b.extendBindings(new Variable("x"), new Integer(5));
    System.err.println(b);
  }

}