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

package ilog.language.design.kernel;

/**
 * @version     Last modified on Fri Oct 11 01:06:31 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.*;
import ilog.language.design.base.*;

import ilog.language.util.ObjectToIntMap;
import java.util.ArrayList;
import java.util.Iterator;

/**
 * This class represents lambda-calculus abstractions. More precisely, it represents
 * multiple-parameter abstractions. This enables application to multiple arguments
 * without having to "curry" it into a composition of single-parameter abstractions.
 * This class extends <a href="Scope.html"><tt>Scope</tt></a> with the added mechanism
 * needed to encapsulate lexically closed variables.
 */
public class Abstraction extends Scope
{
  private ObjectToIntMap _frame = new ObjectToIntMap();

  private int _intFrameSize = 0;
  private int _realFrameSize = 0;
  private int _objectFrameSize = 0;

  public Abstraction (Parameter[] parameters, Expression body)
    {
      super(parameters,body);
    }

  public Abstraction (String parameter, Expression body)
    {
      super(parameter,body);
    }

  public Abstraction (ArrayList parameters, Expression body)
    {
      super(parameters,body);
    }

  public final void addToFrame (Local local)
    {
      _frame.put(local,local.offset());
    }

  public final void sanitizeSorts (Enclosure enclosure)
    {
      if (_isSortSanitized) return;

      enclosure.push(this);
      _body.sanitizeSorts(enclosure);
      enclosure.pop();

      for (Iterator i=_frame.keys(); i.hasNext();)
        {
          Local local = (Local)i.next();
          switch (local.boxSort())
            {
              case Type.INT_SORT:
                _intFrameSize = _maxFrameSize(local,_intFrameSize);
                break;
              case Type.REAL_SORT:
                _realFrameSize = _maxFrameSize(local,_realFrameSize);
                break;
              default:
                _objectFrameSize = _maxFrameSize(local,_objectFrameSize);
            }
        }

      _frame = null;    // no more needed: might as well recycle it!
      _isSortSanitized = true;
    }

  private final int _maxFrameSize (Local local, int frameSize)
    {
      return Math.max(frameSize,local.offset()-_frame.get(local));
    }

  public final Expression shiftOffsets (int intShift, int realShift, int objectShift,
                                        int intDepth, int realDepth, int objectDepth)
    {
      boolean bodyHasShifted = false;

      if (bodyHasShifted |= (_intFrameSize > 0)) _intFrameSize += intShift;
      if (bodyHasShifted |= (_realFrameSize > 0)) _realFrameSize += realShift;
      if (bodyHasShifted |= (_objectFrameSize > 0)) _objectFrameSize += objectShift;

      if (bodyHasShifted)
        _body = _body.shiftOffsets(intShift,realShift,objectShift,
                                   intDepth+_intArity,
                                   realDepth+_realArity,
                                   objectDepth+_objectArity);

      return this;
    }

  protected final PushScope _pushInstruction ()
    {
      return new PushClosure(_intArity,_realArity,_objectArity,
                             _intFrameSize,_realFrameSize,_objectFrameSize);
    }

  final public String toString ()
    {
      String s = "function";

//        if (_intFrameSize+_realFrameSize+_objectFrameSize > 0)
//          s += "<"+_intFrameSize+","+_realFrameSize+","+_objectFrameSize+">";

      s += "(";
      
      for (int i=0; i<arity(); i++)
        {
          s += _parameters[i];
          if (i < arity()-1) s += ",";
        }

      return s + ") " + _body;
    }

  // Added by PV
  final public String toTypedString ()
    {
      String s = "function";

      s += "(";
      
      for (int i=0; i<arity(); i++)
        {
          s += _parameters[i].toTypedString();
          if (i < arity()-1) s += ",";
        }

      return typed(s + ") " + _body.toTypedString());
    }
}
