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

package ilog.language.design.kernel;

/**
 * @version     Last modified on Fri Oct 18 17:51:40 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 java.util.ArrayList;
import ilog.language.design.types.*;
import ilog.language.design.base.*;

/**
 * This is the class denoting arrays.  All arrays are represented as
 * unidimensional arrays - multidimensional arrays are simply arrays of arrays.
 */

public class NewArray extends ProtoExpression 
{
  /**
   * This is the array's <i>base type</i> (<i>i.e.</i>, the type of its elements when
   * viewed as a one-dimensional vector).
   */
  private Type _baseType;

  /**
   * This contains the sizes for all dimensions.
   */
  private Expression[] _dimension;

  public NewArray (Type baseType, Expression[] dimension)
    {
      _baseType = baseType;
      _dimension = dimension;
    }  

  public NewArray (Type baseType, ArrayList dimension)
    {
      _baseType = baseType;
      _dimension = new Expression[dimension.size()];
      for (int i = 0; i<_dimension.length; i++)
        _dimension[i] = (Expression)dimension.get(i);
    }  

  public final int numberOfSubexpressions ()
    {
      return _dimension.length;
    }

  public final Expression subexpression (int n) throws NoSuchSubexpressionException
    {
      try
        {
          return _dimension[n];
        }
      catch (ArrayIndexOutOfBoundsException e)
        {
          throw new NoSuchSubexpressionException(this,n);
        }
    }

  public final Expression[] dimension ()
    {
      return _dimension;
    }

  public final Type baseType ()
    {
      return _baseType.value();
    }

  public final Expression sanitizeNames (ParameterStack parameters, ClassTypeHandle handle)
    {
      for (int i=0; i<_dimension.length; i++)
        _dimension[i] = _dimension[i].sanitizeNames(parameters, handle);
      return this;
    }

  public final void sanitizeSorts (Enclosure enclosure)
    {
      for (int i=0; i<_dimension.length; i++)
        _dimension[i].sanitizeSorts(enclosure);
    }

  public final Expression shiftOffsets (int intShift, int realShift, int objectShift,
                                        int intDepth, int realDepth, int objectDepth)
    {
      for (int i=0; i<_dimension.length; i++)
        _dimension[i] = _dimension[i].shiftOffsets(intShift,realShift,objectShift,
                                                   intDepth,realDepth,objectDepth);
      return this;
    }

  public final void setCheckedType ()    
    {
      if (isSetCheckedType()) return;
      for (int i=0; i<_dimension.length; i++)
        _dimension[i].setCheckedType();
      setCheckedType(type().copy());
    }

  /**
   * To typecheck a new array, we:
   * <ol>
   * <li> typecheck each dimension to have an allowed index set type (<i>i.e.</i>, int,
   *      int range, or set),
   * <li> typecheck this as an array type with the base type and dimension types.
   * </ol>
   */
  public final void typeCheck (TypeChecker typeChecker) throws TypingErrorException
    {
      if (typeCheckLocked()) return;

      for (int i=0; i<_dimension.length; i++)
        _dimension[i].typeCheck(Global.dummyIndexSet(),typeChecker);
      
      typeChecker.typeCheck(this,_baseType.array(_dimension));
    }

  /**
   * To compile a new array, we:
   * <ol>
   * <li> compile the last dimension,
   * <li> generate a <tt>PUSH_[ARRAY,MAP]_[I,R,O]</tt> instruction,
   * <li> compile the next to last dimension,
   * <li> generate a <tt>FILL_[ARRAY,MAP]_[I,R,O][AM]</tt> instruction,
   * <li> compile each of the other dimensions followed with a <tt>FILL_[ARRAY,MAP]_O[A,M]</tt>
   *      instruction.
   * </ol>
   */
  public final void compile (Compiler compiler)
    {
      Expression dimension = _dimension[_dimension.length-1];
      Type dimensionType = dimension.checkedType();
      boolean hasIntIndex = dimensionType.isInt();
      byte innerSort = ((ArrayType)_checkedType).innerType(_dimension.length).boxSort();

      byte previous;

      dimension.compile(compiler);

      if (hasIntIndex)
        {
          if (dimensionType.isBoxedType())
            compiler.generateUnwrapper(Type.INT_SORT);

          switch (innerSort)
            {
            case Type.INT_SORT:
              previous = _kind(compiler.generate(Instruction.PUSH_ARRAY_I));
              break;

            case Type.REAL_SORT:
              previous = _kind(compiler.generate(Instruction.PUSH_ARRAY_R));
              break;

            default:
              previous = _kind(compiler.generate(Instruction.PUSH_ARRAY_O));
            }
        }
      else
        switch (innerSort)
          {
          case Type.INT_SORT:
            previous = _kind(compiler.generate(Instruction.PUSH_MAP_I));
            break;

          case Type.REAL_SORT:
            previous = _kind(compiler.generate(Instruction.PUSH_MAP_R));
            break;

          default:
            previous = _kind(compiler.generate(Instruction.PUSH_MAP_O));
          }

      for (int i=_dimension.length-1; i-->0;)
        {
          dimension = _dimension[i];
          hasIntIndex = dimension.checkedType().isInt();
          dimension.compile(compiler);

          if (hasIntIndex)
            {
              switch (previous)
                {
                case _IA:
                  compiler.generate(Instruction.FILL_ARRAY_IA);
                  break;
                case _IM:
                  compiler.generate(Instruction.FILL_ARRAY_IM);
                  break;
                case _RA:
                  compiler.generate(Instruction.FILL_ARRAY_RA);
                  break;
                case _RM:
                  compiler.generate(Instruction.FILL_ARRAY_RM);
                  break;
                case _OA:
                  compiler.generate(Instruction.FILL_ARRAY_OA);
                  break;
                case _OM:
                  compiler.generate(Instruction.FILL_ARRAY_OM);
                  break;
                }
              previous = _OA;
            }
          else
            {
              switch (previous)
                {
                case _IA:
                  compiler.generate(Instruction.FILL_MAP_IA);
                  break;
                case _IM:
                  compiler.generate(Instruction.FILL_MAP_IM);
                  break;
                case _RA:
                  compiler.generate(Instruction.FILL_MAP_RA);
                  break;
                case _RM:
                  compiler.generate(Instruction.FILL_MAP_RM);
                  break;
                case _OA:
                  compiler.generate(Instruction.FILL_MAP_OA);
                  break;
                case _OM:
                  compiler.generate(Instruction.FILL_MAP_OM);
                  break;
                }
              previous = _OM;
            }
        }
    }

  private static final byte _IA = 0;
  private static final byte _IM = 1;
  private static final byte _RA = 2;
  private static final byte _RM = 3;
  private static final byte _OA = 4;
  private static final byte _OM = 5;

  private static final byte _kind (Instruction inst)
    {
      if (inst == Instruction.PUSH_ARRAY_I)
        return _IA;
      if (inst == Instruction.PUSH_ARRAY_R)
        return _RA;
      if (inst == Instruction.PUSH_ARRAY_O)
        return _OA;
      if (inst == Instruction.PUSH_MAP_I)
        return _IM;
      if (inst == Instruction.PUSH_MAP_R)
        return _RM;
      if (inst == Instruction.PUSH_MAP_O)
        return _OM;
      return _OM;
    }

  public final String toString ()
    {
      StringBuffer buf = new StringBuffer("newArray ("+baseType()+")");
      for (int i=0; i<_dimension.length; i++)
        buf.append("["+_dimension[i]+"]");
      return buf.toString();
    }

  // Added by PV
  public final String toTypedString ()
    {
      StringBuffer buf = new StringBuffer("newArray ("+baseType()+")");
      for (int i=0; i<_dimension.length; i++)
        buf.append("["+_dimension[i].toTypedString()+"]");
      return typed(buf.toString());
    }
}
