package org.robobinding.util;

import java.util.Queue;
import java.util.Set;

/**
 * 
 * @since 1.0
 * @version $Revision: 1.0 $
 * @author Cheng Wei
 */
public class SearchableClasses {
	private final Set<Class<?>> classes;

	public SearchableClasses(Set<Class<?>> classes) {
		this.classes = classes;
	}

	public Class<?> findNearestAssignableFrom(Class<?> clazz) {
		if ((clazz == Object.class) || (clazz == null)) {
			return null;
		}

		if (classes.contains(clazz)) {
			return clazz;
		}

		for (Class<?> interfaceClass : clazz.getInterfaces()) {
			Class<?> classFound = findNearestAssignableFrom(interfaceClass);
			if (classFound != null) {
				return classFound;
			}
		}

		return findNearestAssignableFrom(clazz.getSuperclass());
	}

	public Queue<Class<?>> findAssignablesInOrderFrom(Class<?> clazz) {
		Queue<Class<?>> assignablesInOrder = Lists.newLinkedList();

		findAssignablesInOrderFromClass(clazz, assignablesInOrder);
		return assignablesInOrder;
	}

	private void findAssignablesInOrderFromClass(Class<?> clazz, Queue<Class<?>> assignablesInOrder) {
		if (clazz == Object.class) {
			return;
		}

		if (classes.contains(clazz)) {
			assignablesInOrder.add(clazz);
		}

		findAssignablesInOrderFromInterface(clazz.getInterfaces(), assignablesInOrder);
		findAssignablesInOrderFromClass(clazz.getSuperclass(), assignablesInOrder);
	}

	private void findAssignablesInOrderFromInterface(Class<?>[] interfaceClasses, Queue<Class<?>> assignablesInOrder) {
		for (Class<?> interfaceClass : interfaceClasses) {
			if (classes.contains(interfaceClass) && (!assignablesInOrder.contains(interfaceClass))) {
				assignablesInOrder.add(interfaceClass);
			}
			findAssignablesInOrderFromInterface(interfaceClass.getInterfaces(), assignablesInOrder);
		}
	}
}
