package code.type.visitor;

import code.type.*;
import static code.type.TypeFactory.*;

import java.util.Hashtable;

/**
 * Rename the type variables of a type expression so that
 * the names in a left-to-right traversa are "0", "1", ...
 * <P>
 * Objects of this class are use-once only.
 * A new object must be created for each type expression normalized.
 *
 * @author Sergio Antoy
 * @since October 30, 2004
 */

public class NormalizeType {

    private NormalizeType() {}

    public static TypeExpression doit(TypeExpression input) {
        NormalizeTypeVisitor ntv = new NormalizeTypeVisitor();
        TypeExpression output = (TypeExpression) input.accept(ntv, null);
        return output;
    }

    private static class NormalizeTypeVisitor
	implements TypeVisitor<TypeExpression,Object> {

        private Hashtable<String,String> map = new Hashtable<String,String>();
        private int counter = 0;

        private String rename(String oldName) {
            String newName = map.get(oldName);
            if (newName == null) {
                newName = "" + counter++;
                map.put(oldName, newName);
            }
            return newName;
        }

        public TypeExpression visit(TypeConstructor t, Object ignore) {
            return t;
        }

        public TypeExpression visit(TypeVariable t, Object ignore) {
            return makeTypeVariable(rename(t.name));
        }

        public TypeExpression visit(FunctionType t, Object ignore) {
	    // TODO: as for next method
            return makeFunctionType(t.domain.accept(this, ignore),
				    t.range.accept(this, ignore));
        }

        public TypeExpression visit(TypeConstructorApplication t,
				    Object ignore) {
            TypeExpression[] arguments = t.arguments;
            TypeExpression[] newArguments
		= new TypeExpression[arguments.length];
            for (int i = 0; i < t.arguments.length; ++i) {
		// TODO: if for all i,
		// arguments[i] == newArguments[i]
		// then return t
                newArguments[i] = t.arguments[i].accept(this, ignore);
            }
            return makeApplicationType(t.typeConstructor,
						  newArguments);
        }

    }

}

