package ilog.language.design.types;

/**
 * @version     Last modified on Thu Jul 11 16:04:52 2002 by hak
 * @version          modified on Wed Jun 12 19:40:03 2002 by pviry
 * @author      <a href="mailto:hak@ilog.fr">Hassan A&iuml;t-Kaci</a>
 * @copyright   &copy; 2001 <a href="http://www.ilog.fr/">ILOG, S.A.</a>
 */

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

import ilog.language.design.kernel.ParameterStack;

/**
 * This is the type of tuples whose components are identified by position.
 */

public class TupleType extends ConstructedType
{
  protected TupleTypeComponents _components; // = new TupleTypeComponents();

  protected TupleType ()
    {
    }

  public final static TupleType EMPTY = new TupleType();

  public TupleType (Type[] components)
    {
      _components = new TupleTypeComponents(components);
    }

  public TupleType (TupleType tupleType)
    {
      _components = new TupleTypeComponents(tupleType.components());
    }

  public final static TupleType newTupleType (ArrayList components)
    {
      if (components == null || components.size() == 0)
        return EMPTY;

      Type[] types = new Type[components.size()];
      for (int i=0; i<types.length; i++)
        types[i] = (Type)components.get(i);

      return new TupleType(types);
    }

  public byte kind ()
    {
      return TUPLE;
    }

  public final TupleTypeComponents components ()
    {
      return _components == null ? null : _components.value();
    }

  public final boolean isIndefinite ()
    {
      return _components != null && _components.isIndefinite();
    }

  public final Type component (int i)
    {
      return components().type(i);
    }

  public final void setComponentType (int i, Type type)
    {
      components().setComponentType(i,type);
    }

  public final int dimension ()
    {
      return _components == null ? 0 : _components.dimension();
    }

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

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

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

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

  public final void checkOccurrence (TypeParameter parameter, Type context, TypeChecker typeChecker)
  throws FailedUnificationException
    {
      for (int i=0; i<dimension(); i++)
        component(i).checkOccurrence(parameter,context,typeChecker);
    }

  public final Type flatten ()
    {
      for (int i=0; i<dimension(); i++)
        setComponentType(i,component(i).flatten());
      return this;
    } 

  public 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 TupleType(newComponents);
    }

  public Type instantiate (HashMap substitution)
    {
      if (dimension() == 0)
        return this;

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

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

      return new TupleType(newComponents);
    }

  public final int eqCode ()
    {
      int code =  kind() + dimension();

      for (int i=0; i<dimension(); i++)
        code += (i+1)*component(i).eqCode();

      return code;
    }

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

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

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

      for (int i=0; i<dimension(); i++)
        if (!component(i).isEqualTo(tupleType.component(i)))
          return false;

      return true;
    }

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

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

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

      for (int i=0; i<dimension(); i++)
        if (!component(i).isEqualTo(tupleType.component(i),parameters))
        return false;

      return true;
    }

  public Type sanitizeTypeReferences (ParameterStack parameters, ClassTypeHandle handle)
    {
      for (int i=0; i<dimension(); i++)
        _components.setComponentType(i,_components.type(i).sanitizeTypeReferences(parameters,handle));

      return this;
    }

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

      if (isIndefinite())
        buf.append("...");
      else
        for (int i=0; i<dimension(); i++)
          buf.append(component(i)+(i==dimension()-1?"":","));

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

}

