package ilog.language.design.types;

/**
 * @version     Last modified on Thu Jul 11 15:53:38 2002 by hak
 * @version          modified on Fri May 03 18:50:10 2002 by pviry
 * @author      <a href="mailto:hak@ilog.fr">Hassan A&iuml;t-Kaci</a>
 * @copyright   &copy; 2002 <a href="http://www.ilog.fr/">ILOG, S.A.</a>
 */

import java.util.HashMap;
import java.util.ArrayList;

import ilog.language.tools.Misc;
import ilog.language.design.kernel.TupleFieldName;

/**
 * This is the type of tuples whose components are identified by field names. It derives
 * from <a href="./TupleType.html"><tt>TupleType</tt></a>, and in addition keeps an
 * array of <a href="../kernel/TupleFieldName.html"><tt>TupleFieldName</tt></a>s
 * recording the names. The reason for using <tt>TupleFieldName</tt>s rather than
 * <tt>String</tt>s is so that the array of field names may be sorted lexicographically
 * for the purpose of normalizing all named tuples and named tuple types, while
 * recording their original indices for displaying components in their "original"
 * order. Note that this notion is rather fuzzy as it corresponds to the ordering of
 * components found by the constructor (which is meant to be used by a parser) and can
 * only be that of a specific written named tuple (type) occurrence. Thus, this order
 * may only be <i>one</i> among many if distinct occurrences of this type, or tuples of
 * this type, are written using differring orders of components. Nevertheless, for
 * consistently written tuples, the components will be displayed in the expected order.
 */

public class NamedTupleType extends TupleType
{
  private TupleFieldName[] _fields;
  private int[] _index;

  public NamedTupleType (Type[] components, TupleFieldName[] fields)
    {
      _components = new TupleTypeComponents(components);
      _fields = fields;
    }

  public NamedTupleType (ArrayList components, ArrayList names)
    {
      // NB: this assumes that both lists are non-empty, and have the same size.

      _fields = new TupleFieldName[names.size()];
      for (int i=0; i<_fields.length; i++)
        _fields[i] = new TupleFieldName((String)names.get(i),i);
      if(_fields.length>0)
          Misc.sort(_fields);

      TupleTypeComponents newComponents = new TupleTypeComponents(new Type[components.size()]);
      for (int i=0; i<newComponents.dimension(); i++)
        newComponents.setComponentType(i,(Type)components.get(_fields[i].index()));
    }

  public final TupleFieldName[] fields ()
    {
      return _fields;
    }

  public final int[] index ()
    {
      if (_index == null)
        {
          _index = new int[dimension()];

          for (int i=0; i<_index.length; i++)
            _index[_fields[i].index()] = i;
        }

      return _index;      
    }

  public final String fieldSet ()
    {
      StringBuffer buf = new StringBuffer("{");

      for (int i=0; i<dimension(); i++)
        buf.append(_fields[index()[i]]).append(i==dimension()-1?"":", ");

      return buf.append("}").toString();
    }

  public final int position (String name)
    {
      for (int i=0; i<_fields.length; i++)
        if (_fields[i].isEqualTo(name))
          return i+1;

      return 0;
    }

  public final byte kind ()
    {
      return NAMED_TUPLE;
    }

  public final void unify (Type type, TypeChecker typeChecker) throws FailedUnificationException
    {
      type = type.value();
      switch (type.kind())
        {
        case PARAMETER:
          type.unify(this,typeChecker);
          return;

        case NAMED_TUPLE:
          if (type == this)
            return;

          if (components() != null)
            components().unify(((TupleType)type).components(),typeChecker);
          return;

        default:
          typeChecker.error(new TypeClashException(this,type));
        }
    }

  public final Type copy (HashMap parameters)
    {
      if (dimension() == 0)
        return this;

      Type[] newComponents = new Type[dimension()];

      for (int i=0; i<dimension(); i++)
        newComponents[i] = component(i).copy(parameters);

      return new NamedTupleType(newComponents,_fields);
    }

  public final boolean isEqualTo (Type type)
    {
      if (this == type)
        return true;

      if (!(type instanceof NamedTupleType))
        return false;

      NamedTupleType tupleType = (NamedTupleType)type;
          
      if (dimension() != tupleType.dimension())
        return false;

      for (int i=0; i<dimension(); i++)
        if (!_fields[i].equals(tupleType.fields()[i])
            || !component(i).isEqualTo(tupleType.component(i)))
          return false;

      return true;
    }

  public final boolean isEqualTo (Type type, HashMap parameters)
    {
      if (this == type)
        return true;

      if (!(type instanceof NamedTupleType))
        return false;

      NamedTupleType tupleType = (NamedTupleType)type;
          
      if (dimension() != tupleType.dimension())
        return false;

      for (int i=0; i<dimension(); i++)
        if (!_fields[i].equals(tupleType.fields()[i])
            || !component(i).isEqualTo(tupleType.component(i),parameters))
          return false;

      return true;
    }

  public final String toString ()
    {
      StringBuffer buf = new StringBuffer("<");

      if (isIndefinite())
        buf.append("...");
      else
        for (int i=0; i<dimension(); i++)
            buf.append(_fields[index()[i]])
               .append(":")
               .append(component(index()[i]))
               .append(i==dimension()-1?"":",");
      
      return buf.append(">").toString();
    }
}
