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

package ilog.language.design.base;

/**
 * @version     Last modified on Sun Nov 03 18:50:05 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.types.Type;
import ilog.language.design.types.FunctionType;
import ilog.language.design.backend.Runtime;
import ilog.language.design.backend.Closure;
import ilog.language.design.backend.NullValueException;

/**
 * An <tt>Apply</tt> instruction is to an <a href="Enter.html"><tt>Enter</tt></a>
 * instruction what an <a href="../kernel/Abstraction.html"><tt>Abstraction</tt></a>
 * expression is to a <a href="../kernel/Scope.html"><tt>Scope</tt></a> expression.
 * In other words, it is an <tt>Enter</tt> using a frame machinery.
 */
public class Apply extends Enter
{
  private boolean _LCO_isOff = true;

  protected Apply ()
    {
    }

  public Apply (int intArity, int realArity, int objectArity)
    {
      super(intArity,realArity,objectArity);
      setName("APPLY");
    }

  public Apply (FunctionType type)
    {
      super(type);
      setName("APPLY");
    }

  public final boolean lcoIsOff ()
    {
      return _LCO_isOff;
    }

  public final Apply setLCO ()
    {
      _LCO_isOff = false;
      setName("LCO_APPLY");
      return this;
    }

  public final Apply curryObject ()
    {
      _objectArity--;
      return this;
    }

  public void execute (Runtime r) throws NullValueException
    {
      _execute((Closure)r.popObject("can't apply a null function"),r);
    }

  protected final void _execute (Closure c, Runtime r) throws NullValueException
    {
      if (arity() == c.arity())
        {
          int[] intArgs = new int[_intArity];
          for (int i=0; i<_intArity; i++)
            intArgs[i] = r.popInt();

          double[] realArgs = new double[_realArity];
          for (int i=0; i<_realArity; i++)
            realArgs[i] = r.popReal();

          Object[] objectArgs = new Object[_objectArity];
          for (int i=0; i<_objectArity; i++)
            objectArgs[i] = r.popObject();

          if (c.isExitable())
            r.enterExitableScope(); // necessarily LCO is off (see ../kernel/Compiler.java)
          else
            if (_LCO_isOff) r.saveState();

          r.pushIntEnv(c.intFrame());
          r.pushIntEnv(intArgs);

          r.pushRealEnv(c.realFrame());
          r.pushRealEnv(realArgs);

          r.pushObjectEnv(c.objectFrame());
          r.pushObjectEnv(objectArgs);

          r.setCode(c.code());
          r.setIP(c.address());
        }
      else // Type-checking guarantees that arity() < c.arity().
        {
          int[] intEnv = c.intFrame();
          double[] realEnv = c.realFrame();
          Object[] objectEnv = c.objectFrame();

          if (_intArity <= c.intArity())
            {
              intEnv = new int[_intArity+c.intFrame().length];
              for (int i=0; i<_intArity; i++)
                intEnv[i] = r.popInt();
              for (int i=_intArity; i<intEnv.length; i++)
                intEnv[i] = c.intFrame()[i-_intArity];
            }

          if (_realArity <= c.realArity())
            {
              realEnv = new double[_realArity+c.realFrame().length];
              for (int i=0; i<_realArity; i++)
                realEnv[i] = r.popReal();
              for (int i=_realArity; i<realEnv.length; i++)
                realEnv[i] = c.realFrame()[i-_realArity];
            }

          if (_objectArity <= c.objectArity())
            {
              objectEnv = new Object[_objectArity+c.objectFrame().length];
              for (int i=0; i<_objectArity; i++)
                objectEnv[i] = r.popObject();
              for (int i=_objectArity; i<objectEnv.length; i++)
                objectEnv[i] = c.objectFrame()[i-_objectArity];
            }

          r.pushObject(new Closure(c.code(),
                                   c.address(),
                                   c.intArity() - _intArity,
                                   c.realArity() - _realArity,
                                   c.objectArity() - _objectArity,
                                   intEnv,
                                   realEnv,
                                   objectEnv)
                                  .setIsExitable(c.isExitable()));
          r.incIP();
        }
    }

  public final boolean equals (Object object)
    {
      if (this == object)
        return true;

      if (!(object instanceof Apply))
        return false;

      Apply apply = (Apply)object;

      return _intArity    == apply.intArity()
          && _realArity   == apply.realArity()
          && _objectArity == apply.objectArity()
          && _LCO_isOff   == apply.lcoIsOff();
    }

}
