package de.dhbwstuttgart.typeinference.unify.model;

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

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

/**
 * An extends wildcard type "? extends T". 
 */
public final class ExtendsType extends WildcardType {
	
	public <T> UnifyType accept(UnifyTypeVisitor<T> visitor, T ht) {
		return visitor.visit(this, ht);
	}
	
	/**
	 * Creates a new extends wildcard type.
	 * @param extendedType The extended type e.g. Integer in "? extends Integer"
	 */
	public ExtendsType(UnifyType extendedType) {
		super("? extends " + extendedType.getName(), extendedType);
		if (extendedType instanceof ExtendsType) {
			System.out.print("");
		}
	}

	/**
	 * The extended type e.g. Integer in "? extends Integer"
	 */
	public UnifyType getExtendedType() {
		return wildcardedType;
	}
	
	/**
	 * Sets the type parameters of the wildcarded type and returns a new extendstype that extends that type.
	 */
	@Override
	public UnifyType setTypeParams(TypeParams newTp) {
		UnifyType newType = wildcardedType.setTypeParams(newTp);
		if(newType == wildcardedType)
			return this; // Reduced the amount of objects created
		return new ExtendsType(wildcardedType.setTypeParams(newTp));
	}

	@Override
	Set<UnifyType> smArg(IFiniteClosure fc, Set<UnifyType> fBounded) {
		return fc.smArg(this, fBounded);
	}

	@Override
	Set<UnifyType> grArg(IFiniteClosure fc, Set<UnifyType> fBounded) {
		return fc.grArg(this, fBounded);
	}

	@Override
	UnifyType apply(Unifier unif) {
		UnifyType newType = wildcardedType.apply(unif);
		if(newType.hashCode() == wildcardedType.hashCode() && newType.equals(wildcardedType))
			return this; // Reduced the amount of objects created
		return new ExtendsType(newType);
	}
	
	@Override
	public int hashCode() {
		/*
		 * It is important that the prime that is added is different to the prime added in hashCode() of SuperType.
		 * Otherwise ? extends T and ? super T have the same hashCode() for every Type T.
		 */
		return wildcardedType.hashCode() + 229; 
	}
	
	@Override
	public boolean equals(Object obj) {
		if(!(obj instanceof ExtendsType))
			return false;
		
		if(obj.hashCode() != this.hashCode())
			return false;
	
		ExtendsType other = (ExtendsType) obj;
		
	
		return other.getWildcardedType().equals(wildcardedType);
	}
	
	@Override
	public String toString() {
		return "? extends " + wildcardedType;
	}

	
}
