// FILE. . . . . d:/hak/hlt/src/hlt/fot/FirstOrderTermStructure.java
// EDIT BY . . . Hassan Ait-Kaci
// ON MACHINE. . Hak-Laptop
// STARTED ON. . Sat Jul 14 07:23:42 2018

package hlt.fot;

/**
 * This is a class for first-order terms defined on a signature of <a
 * href="Functor.html"><tt>Functor</tt></a>s. It is of the form
 * <tt>f(t<sub>1</sub>, ..., t<sub>f.arity()</sub>)</tt>, where
 * <tt>f</tt> is a functor and each <tt>t<sub>i</sub></tt> is a <a
 * href="FirstOrderTerm.html"><tt>FirstOrderTerm</tt></a>, for <tt>0
 * &le; i &le; f.arity()</tt>.
 *
 * @see         FirstOrderTerm
 * @see         Functor
 *
 * @version     Last modified on Fri Aug 24 04:31:21 2018 by hak
 * @author      <a href="mailto:hak@acm.org">Hassan A&iuml;t-Kaci</a>
 * @copyright   &copy; <a href="http://www.hassan-ait-kaci.net/">by the author</a>
 */

public class FirstOrderTermStructure implements FirstOrderTerm
{
  /* ************************************************************************ */

  /**
   * This term's functor.
   */
  protected Functor functor;

  /**
   * Returns this term's functor.
   */
  public Functor functor ()
  {
    return functor;
  }

  /**
   * This term's arguments, or <tt>null</tt> for constants (functors of
   * arity <tt>0</tt>).
   */
  protected FirstOrderTerm[] arguments;

  /**
   * Returns this term's arguments or <tt>null</tt> when the functor's
   * arity is <tt>0</tt>.
   */
  public FirstOrderTerm[] arguments ()
  {
    return arguments;
  }

  /* ************************************************************************ */

  // Constructors:

  // This one is never used; but if not defined, the compiler complains!
  protected FirstOrderTermStructure ()
  {
  }

  public FirstOrderTermStructure (Functor functor)
  {
    this.functor = functor;
  }

  public FirstOrderTermStructure (Functor functor, FirstOrderTerm[] arguments)
  {
    this.functor = functor;
    this.arguments = arguments;
  }

  /* ************************************************************************ */

  /**
   * Returns the term's argument at argument <tt>position</tt> or
   * <tt>null</tt> when the functor's arity is <tt>0</tt>. <b>N.B.</b>:
   * argument counting goes from <tt>1</tt> to <tt>functor.arity()</tt>.
   */
  public FirstOrderTerm argument (int position)
  {
    // System.err.println("&&& argument position "+position+" out of "+arguments.length);
    return arguments == null ? null : arguments[position-1];
  }

  /**
   * Sets the term's argument at <tt>position</tt> to the specified
   * term.  <b>N.B.</b>: argument counting goes from <tt>1</tt> to
   * <tt>functor.arity()</tt>.
   */
  public void setArgument (int position, FirstOrderTerm term)
  {
    arguments[position-1] = term;
  }

  /* ************************************************************************ */

  /**
   * Returns <tt>false</tt>. This implements the <tt>FirstOrderTerm</tt>
   * interface's method identifying <tt>this</tt> as a non-variable
   * term; <i>i.e.</i>, a term structure (a constant or one with arguments).
   */
  final public boolean isVariable ()
  {
    return false;
  }

  /**
   * This returns true iff this is a constant (<i>i.e.</i>, it has no arguments).
   */
  final public boolean isConstant ()
  {
    // System.err.println("&&& is functor "+functor+"/"+functor.arity()+
    // 		       " a constant?"+(functor.arity() == 0?" yes!":" no!"));

    return functor.arity() == 0;
  }
  /* ************************************************************************ */
  // DEREFERENCED FORM
  /* ************************************************************************ */

  /**
   * Returns a term that is equal to this term after having dereferenced
   * all its contents.
   */
  public FirstOrderTerm deref ()
  {
    if (isConstant())
      return this;
    
    FirstOrderTerm[] derefArguments = new FirstOrderTerm[arguments.length];

    for (int i = 0; i < arguments.length; i++)
      derefArguments[i] = arguments[i].deref();

    return new FirstOrderTermStructure(functor,derefArguments);
  }

  /* ************************************************************************ */
  // STRING FORM
  /* ************************************************************************ */

  /**
   * A cache to prevent useless recomputations of the same string.
   */
  private String stringForm;
  
  /**
   * Returns a string form of this term structure not taking into
   * account the bindings of its variables.
   */
  public String toString ()
  {
    if (stringForm != null)
      return stringForm;
   
    StringBuffer buffer = new StringBuffer(functor.name());

    if (!isConstant())
      {
	//	System.err.println("&&& arguments = "+arguments);
	
	buffer.append("(");
	for (int i=1; i <= functor.arity(); i++)
	  buffer.append(argument(i).toString()+(i==functor.arity()?")":","));
      }

    return stringForm = buffer.toString();
  }

  /**
   * Returns a string form of this term structure taking into account
   * the bindings of its variables. Overrrides the equivalent but less
   * efficient default in <tt>FirstOrderTerm</tt>. <b>N.B.</b>: no
   * caching for this one as it may change if some variable bindings do.
   */
  public String derefToString ()
  {
    StringBuffer buffer = new StringBuffer(functor.name());    

    if (functor.arity() != 0)
      {
	buffer.append("(");
	for (int i=1; i <= functor.arity(); i++)
	  buffer.append(argument(i).derefToString()+(i==functor.arity()?")":","));
      }

    return buffer.toString();
  }

  /* ************************************************************************ */
  // EQUALITY
  /* ************************************************************************ */

  /**
   * Returns <tt>true</tt> is this term structure is equal symbol for
   * symbol to the given one (although they may be different
   * individuals).
   */
  public boolean equal (FirstOrderTerm term)
  {
    if (this == term)
      return true;

    if (term.isVariable())
      return false;

    FirstOrderTermStructure T = (FirstOrderTermStructure)term;
    
    boolean isEqual = functor.equal(T.functor());
    int i = 0;

    while (isEqual && ++i <= functor.arity())
      isEqual &= argument(i).equal(T.argument(i));

    return isEqual;
  }

}
