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

package ilog.language.design.kernel;

/**
 * @version     Last modified on Fri Oct 18 17:49:57 2002 by hak
 * @version          modified on Wed Jul 24 12:17:48 2002 by pviry
 * @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.*;

/**
 * This is the class of array slot update expressions.
 */
public class ArraySlotUpdate extends ProtoExpression
{
  /**
   * The array slot being updated.
   */
  private Expression _slot;

  /**
   * The expression whose value is assigned to the array slot.
   */
  private Expression _value;

  private boolean _slotIsBoxed = false;
  private boolean _valueIsBoxed = false;

  /**
   * Constructs an array slot update with the specified array slot and value.
   */
  public ArraySlotUpdate (Expression slot, Expression value)
    {
      _slot = slot;
      _value = value; 
    }

  public final int numberOfSubexpressions ()
    {
      return 2;
    }

  public final Expression subexpression (int n) throws NoSuchSubexpressionException
    {
      switch (n)
        {
        case 0:
          return _slot;
        case 1:
          return _value;
        }

      throw new NoSuchSubexpressionException(this,n);
    }

  /**
   *  Returns the array of this array slot update;
   */
  public final Expression array ()
    {
      return ((ArraySlot)_slot).array();
    }

  /**
   *  Returns the index of this array slot update;
   */
  public final Expression index ()
    {
      return ((ArraySlot)_slot).index();
    }

  /**
   * Sanitizes the names of all expressions in the array slot update.
   */
  public final Expression sanitizeNames (ParameterStack parameters,
                                         ClassTypeHandle handle)
    {
      _slot = _slot.sanitizeNames(parameters, handle);
      _value = _value.sanitizeNames(parameters, handle);
      return this;
    }

  /**
   * Sanitizes the sorts of all expressions in the array slot update.
   */
  public final void sanitizeSorts (Enclosure enclosure)
    {
      _slot.sanitizeSorts(enclosure);
      _value.sanitizeSorts(enclosure);
    }

  /**
   * Shifts the offsets of all the expressions in this array slot update.
   */
  public final Expression shiftOffsets (int intShift, int realShift, int objectShift,
                                        int intDepth, int realDepth, int objectDepth)
    {      
      _slot = _slot.shiftOffsets(intShift,realShift,objectShift,
                                 intDepth,realDepth,objectDepth);

      _value = _value.shiftOffsets(intShift,realShift,objectShift,
                                   intDepth,realDepth,objectDepth);

      return this;
    }

  /**
   * Sets the checked type of this array slot update.
   */
  public final void setCheckedType ()
    {
      if (isSetCheckedType()) return;
      setCheckedType(type().copy());
      _slot.setCheckedType();
      _value.setCheckedType();
    }

  /**
   * Type-checks this array slot update in the context of the specified
   * <a href="./TypeChecker.html"> <tt>TypeChecker</tt></a>.
   */
  public final void typeCheck (TypeChecker typeChecker) throws TypingErrorException
    {
      if (typeCheckLocked()) return;

      _slot.typeCheck(typeChecker);
      _slotIsBoxed = _slot.type().isBoxedType();        // DANGER: this should be trailed!

      _value.typeCheck(typeChecker);
      _valueIsBoxed = _value.type().isBoxedType();      // DANGER: this should be trailed!

      typeChecker.typeCheck(this,_slot.typeRef());
      typeChecker.typeCheck(this,_value.typeRef());
      type().setBoxed(_slotIsBoxed);
    }
    
  /**
   * Compiles this array slot update in the context of the specified
   * <a href="./Compiler.html"><tt>Compiler</tt></a>.
   */
  public final void compile (Compiler compiler)
    { // compile in this order:
      //        (1) the index
      //        (2) the array,
      //        (3) the value,
      //        (4) possibly un/box the value,
      //        (5) the 'set' instruction.

      ArrayType arrayType = (ArrayType)array().checkedType();
      boolean isMap = arrayType.isMap();
      boolean isIntIndexed = arrayType.indexType().boxSort() == Type.INT_SORT;

      index().compile(compiler);
      if (isIntIndexed && index().checkedType().isBoxedType())
        compiler.generateUnwrapper(Type.INT_SORT);

      array().compile(compiler);
      _value.compile(compiler);

      if (_slotIsBoxed)
        {
          if (!_valueIsBoxed)
            compiler.generateWrapper(sort());
        }
      else
        if (_valueIsBoxed)
          compiler.generateUnwrapper(sort());

      compiler.generate(_setArraySlot(isMap,
                                      isIntIndexed,
                                      _slotIsBoxed ? Type.OBJECT_SORT : sort()));
    }

  private final Instruction _setArraySlot(boolean isMap, boolean isIntIndexed, byte sort)
    {
      if (isMap)
        switch (sort)
          {
          case Type.INT_SORT:
            return isIntIndexed ? Instruction.SET_INT_INDEXED_MAP_I
                                : Instruction.SET_MAP_I;
          case Type.REAL_SORT:
            return isIntIndexed ? Instruction.SET_INT_INDEXED_MAP_R
                                : Instruction.SET_MAP_R;
          default:
            return isIntIndexed ? Instruction.SET_INT_INDEXED_MAP_O
                                : Instruction.SET_MAP_O;
          }
      else
        switch (sort)
          {
          case Type.INT_SORT:
            return Instruction.SET_ARRAY_I;
          case Type.REAL_SORT:
            return Instruction.SET_ARRAY_R;
          default:
            return Instruction.SET_ARRAY_O;
          }
    }

  public final String toString ()
    {
      return _slot + " = " + _value;
    }

  // Added by PV
  public final String toTypedString ()
    {
      return typed(_slot.toTypedString() + " = " + _value.toTypedString());
    }
}
