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

package ilog.language.design.kernel;

/**
 * @version     Last modified on Fri Oct 18 17:52:06 2002 by hak
 * @version          modified on Wed Jul 24 12:19:35 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.*;

import java.util.ArrayList;

/**
 * This is the class of tuple projection expressions.
 */
public class TupleProjection extends ProtoExpression
{
  private Expression _tuple;
  private Constant _field;
  private int _position;

  public TupleProjection (Expression tuple, Constant field)
    {
      _tuple = tuple;
      _field = field;
    }

  public final Expression tuple ()
    {
      return _tuple;
    }

  public final Constant field ()
    {
      return _field;
    }

  public final int numberOfSubexpressions ()
    {
      return 2;
    }

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

      throw new NoSuchSubexpressionException(this,n);
    }

  public final Expression sanitizeNames (ParameterStack parameters, ClassTypeHandle handle)
    {
      _tuple = _tuple.sanitizeNames(parameters, handle);
      return this;
    }

  public final void sanitizeSorts (Enclosure enclosure)
    {
      _tuple.sanitizeSorts(enclosure);
    }

  public final Expression shiftOffsets (int intShift, int realShift, int objectShift,
                                        int intDepth, int realDepth, int objectDepth)
    {
      _tuple = _tuple.shiftOffsets(intShift,realShift,objectShift,
                                   intDepth,realDepth,objectDepth);
      return this;
    }

  public final void setCheckedType ()
    {
      if (isSetCheckedType()) return;
      _tuple.setCheckedType();
      setCheckedType(type().copy());
    }

  public final void typeCheck (TypeChecker typeChecker) throws TypingErrorException
    {
      if (typeCheckLocked()) return;

      _tuple.typeCheck(typeChecker);

      if (!(_tuple.type().actualType() instanceof TupleType))
        typeChecker.error(locate(new TypingErrorException("bad tuple type: "+_tuple.type())));

      TupleType tupleType = (TupleType)_tuple.type().actualType();

      if (tupleType.dimension() == 0)
        typeChecker.error(locate(new TypingErrorException("empty tuple projection")));

      if (_field instanceof StringConstant)
        if (tupleType.kind() != Type.NAMED_TUPLE)
          typeChecker.error(locate(new TypingErrorException("bad tuple field position: "+_field+
                                                            " should be an integer literal in [1,"+
                                                            tupleType.dimension()+"] range")));
        else
          {
            _position = ((NamedTupleType)tupleType).position(((StringConstant)_field).stringValue());
            if (_position == 0)
              typeChecker.error(locate(new TypingErrorException("bad tuple field name: "+_field+
                                                                " is not in "+
                                                                ((NamedTupleType)tupleType).fieldSet())));
          }
      else
        if (_field instanceof Int)
          if (tupleType.kind() == Type.NAMED_TUPLE)
            typeChecker.error(locate(new TypingErrorException("bad tuple field name: "+_field+
                                                              " should be a name in "+
                                                              ((NamedTupleType)tupleType).fieldSet())));
          else
            {
              _position = ((Int)_field).value();
              if (_position <= 0 || _position > tupleType.dimension())
                typeChecker.error(locate(new TypingErrorException("bad tuple field position: "+_field+
                                                                  " is not in [1,"+
                                                                  tupleType.dimension()+"] range")));
            }
        else
          typeChecker.error(locate(new TypingErrorException("bad tuple field: "+_field)),_field);

      typeChecker.typeCheck(this,tupleType.component(_position-1));
    }

  public final void compile (Compiler compiler)
    {
      _tuple.compile(compiler);

      switch (boxSort())
        {
        case Type.INT_SORT:
          compiler.generate(new GetIntTupleComponent(offset()));
          return;
        case Type.REAL_SORT:
          compiler.generate(new GetRealTupleComponent(offset()));
          return;
        default:
          compiler.generate(new GetObjectTupleComponent(offset()));
        }
    }

  public final int offset ()
    {
      int offset = 0;
      byte sort = boxSort();
      TupleType tupleType = (TupleType)_tuple.checkedType().actualType();

      for (int i=0; i<_position; i++)
        if (tupleType.component(i).boxSort() == sort)
          offset++;

      return offset;
    }

  public final String toString ()
    {
      return _tuple+"@"+_field;
    }

  // Added by PV
  public final String toTypedString ()
    {
        return typed(_tuple.toTypedString()+"@"+_field.toTypedString());
    }
}
