package de.dhbwstuttgart.typeinference.unify.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import de.dhbwstuttgart.syntaxtree.StatementVisitor;
import de.dhbwstuttgart.typeinference.unify.interfaces.IFiniteClosure;
import de.dhbwstuttgart.typeinference.unify.interfaces.UnifyTypeVisitor;

/**
 * Represents a java type.
 * @author Florian Steurer
 */
public abstract class UnifyType {
	
	/**
	 * The name of the type e.q. "Integer", "? extends Integer" or "List" for (List<T>)
	 */
	protected final String typeName;
	
	/**
	 * The type parameters of the type.
	 */
	protected final TypeParams typeParams;
	
	/**
	 * Creates a new instance
	 * @param name Name of the type (e.q. List for List<T>, Integer or ? extends Integer)
	 * @param typeParams Parameters of the type (e.q. <T> for List<T>)
	 */
	protected UnifyType(String name, TypeParams p) {
		typeName = name;
		typeParams = p;
	}
	

	abstract public <T> UnifyType accept(UnifyTypeVisitor<T> visitor, T ht); 
	
	/**
	 * Returns the name of the type.
	 * @return The name e.q. List for List<T>, Integer or ? extends Integer
	 */
	public String getName() {
		return typeName;
	}
	
	/**
	 * The parameters of the type.
	 * @return Parameters of the type, e.q. <T> for List<T>.
	 */
	public TypeParams getTypeParams() {
		return typeParams;
	}
	
	/**
	 * Returns a new type that equals this type except for the type parameters.
	 * @param newTp The type params of the new type.
	 * @return A new type object.
	 */
	public abstract UnifyType setTypeParams(TypeParams newTp);
	
	/**
	 * Implementation of the visitor-pattern. Returns the set of smArg
	 * by calling the most specific overload in the FC.
	 * @param fc The FC that is called.
	 * @return The set that is smArg(this)
	 */
	abstract Set<UnifyType> smArg(IFiniteClosure fc, Set<UnifyType> fBounded);
	
	/**
	 * Implementation of the visitor-pattern. Returns the set of grArg
	 * by calling the most specific overload in the FC.
	 * @param fc The FC that is called.
	 * @return The set that is grArg(this)
	 */
	abstract Set<UnifyType> grArg(IFiniteClosure fc, Set<UnifyType> fBounded);
	
	/**
	 * Applies a unifier to this object.
	 * @param unif The unifier
	 * @return A UnifyType, that may or may not be a new object, that has its subtypes substituted.
	 */
	abstract UnifyType apply(Unifier unif);
	
	@Override
	public String toString() {
		String params = "";
		if(typeParams.size() != 0) {
			for(UnifyType param : typeParams)
				params += param.toString() + ",";
			params = "<" + params.substring(0, params.length()-1) + ">";
		}
		
		return typeName + params;
	}

	public Collection<PlaceholderType> getInvolvedPlaceholderTypes() {
		ArrayList<PlaceholderType> ret = new ArrayList<>();
		ret.addAll(typeParams.getInvolvedPlaceholderTypes());
		return ret;
	}
	
	public Boolean wrongWildcard() {//default
		return false;
	}

	@Override
	public int hashCode() {
		return this.toString().hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		return this.toString().equals(obj.toString());
	}
}