package aima.logic;
import aima.util.DottedPair;
import aima.util.Expression;


/**
 * The ListBindings class implements the <code>Bindings</code> interface
 * using a <code>DottedPair</code> (association list) representation.
 *
 * @author Michael S. Braverman
 * @see DottedPair
 */

public class ListBindings 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 <code>Bindings</code>)
   * object.
   *
   * @return The empty bindings object.
   *
   */
  static public Bindings getEmpty() { return EMPTYBINDINGS; }

  /**
   * Determine if this <code>Bindings</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>Bindings</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); }

  /**
   * 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);
  }

  /**
   * 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) {
    DottedPair found = theList.assoc(var);
    return (found.isNull() ? null: found.cdr()); 
  }

  /**
   * Extends this <code>Bindings</code> object with an association between
   * the given <code>Variable</code> and value.
   *
   * <p>
   * Note:
   * This method does not change the contents of the 
   * <code>ListBindings</code> object on which it is invoked, which may
   * make it more appropriate for theorem-proving applications.
   *
   * @return An extended <code>ListBindings</code> object.
   * @param var The variable to bind.
   * @param val The value to which the <code>Variable</code> is to be bound.
   */
  public Bindings extendBindings (Variable var, Object val) {
    return new ListBindings(var,val,this);
  }

  /**
   * 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: "+ theList.toString() +"]";
    }
  }


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

  /**
   * Private constructor for extending an existing <code>ListBindings</code>
   */
  private ListBindings(Variable var, Object value,  ListBindings oldBindings) {
    theList = new Expression(new Expression(var,value),oldBindings.theList);
  }

  /**
   * Contains the <code>DottedPair</code> list the actually stores
   * the <code>Variable</code> bindings.
   */
  private DottedPair theList;

  /**
   * 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 ListBindings();

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

  
  /**
   * 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 = ListBindings.getEmpty();
    // In the "List" implementation of bindings, even adding an
    //  identical variable will result in the variable
    //  appearing more than once in Bindings:
    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);
  }
}