package de.dhbwstuttgart.typeinference.unify.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

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

/**
 * An unbounded placeholder type.
 * @author Florian Steurer
 */
public final class PlaceholderType extends UnifyType{
	
	/**
	 * Static list containing the names of all existing placeholders.
	 * Used for generating fresh placeholders.
	 */
	protected static final HashSet<String> EXISTING_PLACEHOLDERS = new HashSet<String>();
	
	/**
	 * Prefix of auto-generated placeholder names.
	 */
	protected static String nextName = "gen_";
	
	/**
	 * Random number generator used to generate fresh placeholder name.
	 */
	protected static Random rnd = new Random(43558747548978L);
	
	/**
	 * True if this object was auto-generated, false if this object was user-generated.
	 */
	private final boolean IsGenerated;
	
	/**
	 * Creates a new placeholder type with the specified name.
	 */
	public PlaceholderType(String name) {
		super(name, new TypeParams());
		EXISTING_PLACEHOLDERS.add(name); // Add to list of existing placeholder names
		IsGenerated = false; // This type is user generated
	}
	
	/**
	 * Creates a new placeholdertype 
	 * @param isGenerated true if this placeholder is auto-generated, false if it is user-generated.
	 */
	protected PlaceholderType(String name, boolean isGenerated) {
		super(name, new TypeParams());
		EXISTING_PLACEHOLDERS.add(name); // Add to list of existing placeholder names
		IsGenerated = isGenerated;
	}
	
	/**
	 * Creates a fresh placeholder type with a name that does so far not exist.
	 * A user could later instantiate a type using the same name that is equivalent to this type.
	 * @return A fresh placeholder type.
	 */
	public static PlaceholderType freshPlaceholder() {
		String name = nextName + (char) (rnd.nextInt(22) + 97); // Returns random char between 'a' and 'z'
		// Add random chars while the name is in use.
		while(EXISTING_PLACEHOLDERS.contains(name));
			name += (char) (rnd.nextInt(22) + 97); // Returns random char between 'a' and 'z'
		return new PlaceholderType(name, true);
	}
	
	/**
	 * True if this placeholder is auto-generated, false if it is user-generated.
	 */
	public boolean isGenerated() {
		return IsGenerated;
	}

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

	@Override
	Set<UnifyType> grArg(IFiniteClosure fc) {
		return fc.grArg(this);
	}
	
	@Override
	public UnifyType setTypeParams(TypeParams newTp) {
		return this; // Placeholders never have params.
	}
	
	@Override
	public int hashCode() {
		return typeName.hashCode();
	}
	
	@Override
	UnifyType apply(Unifier unif) {
		if(unif.hasSubstitute(this))
			return unif.getSubstitute(this);
		return this;
	}
	
	@Override
	public boolean equals(Object obj) {
		if(!(obj instanceof PlaceholderType))
			return false;
		
		return ((PlaceholderType) obj).getName().equals(typeName);
	}
	

	@Override
	public Collection<? extends PlaceholderType> getInvolvedPlaceholderTypes() {
		ArrayList<PlaceholderType> ret = new ArrayList<>();
		ret.add(this);
		return ret;
	}
}
