package de.dhbwstuttgart.typeinference.unify.model;

import java.util.Set;

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

/**
 * A reference type e.q. Integer or List<T>.
 * @author Florian Steurer
 *
 */
public final class ReferenceType extends UnifyType {
	
	/**
	 * The buffered hashCode
	 */
	private final int hashCode;
	
	public ReferenceType(String name, UnifyType... params) {
		super(name, new TypeParams(params));
		hashCode = 31 + 17 * typeName.hashCode() + 17 * typeParams.hashCode();
	}
	
	public ReferenceType(String name, TypeParams params) {
		super(name, params);
		hashCode = 31 + 17 * typeName.hashCode() + 17 * typeParams.hashCode();
	}

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

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

	@Override
	UnifyType apply(Unifier unif) {
		TypeParams newParams = typeParams.apply(unif);
		
		if(newParams.hashCode() == typeParams.hashCode() && newParams.equals(typeParams))
			return this;
		
		return new ReferenceType(typeName, newParams);
	}
	
	@Override
	public UnifyType setTypeParams(TypeParams newTp) {
		if(newTp.hashCode() == typeParams.hashCode() && newTp.equals(typeParams))
			return this; // reduced the amount of objects created
		return new ReferenceType(typeName, newTp);
	}
	
	@Override
	public int hashCode() {
		return hashCode;
	}
	
	@Override
	public boolean equals(Object obj) {
		if(!(obj instanceof ReferenceType))
			return false;
		
		if(obj.hashCode() != this.hashCode())
			return false;
		
		ReferenceType other = (ReferenceType) obj;
		
		if(!other.getName().equals(typeName))
			return false;

		return other.getTypeParams().equals(typeParams);
	}
}
