package com.hammurapi.common;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;

import com.hammurapi.extract.And;
import com.hammurapi.extract.Predicate;

/**
 * Default context uses ServiceLoader to find services in 
 * a given classloader and map of bindings.
 * @author Pavel Vlasov
 *
 */
public class DefaultContext implements Context {
	
	private ClassLoader classLoader;
	private Map<String, Object> bindings;

	/**
	 * @param classLoader ClassLoader for services. Can be null.
	 * @param bindings Name bindings. Can be null.
	 */
	public DefaultContext(ClassLoader classLoader, Map<String, Object> bindings) {
		this.classLoader = classLoader==null ? this.getClass().getClassLoader() : classLoader;
		this.bindings = bindings;
	}

	public Object lookup(String name) {
		return bindings==null ? null : bindings.get(name);
	}

	public <T> T lookup(Class<T> serviceClass, Predicate<T, Context>... selectors) {		
		ServiceLoader<T> sl = ServiceLoader.load(serviceClass, classLoader);
		Iterator<T> sit = sl.iterator();		
		And<T, Context> and = new And<T, Context>(0, null, selectors);
		while (sit.hasNext()) {
			T service = sit.next();
			if (and.extract(this, null, service)) {
				return service;
			}
		}
		return null;
	}

	public <T> Iterator<T> lookupAll(Class<T> serviceClass, Predicate<T, Context>... selectors) {
		ServiceLoader<T> sl = ServiceLoader.load(serviceClass, classLoader);
		And<T, Context> and = new And<T, Context>(0, null, selectors);
		Collection<T> ret = new ArrayList<T>();
		Iterator<T> sit = sl.iterator();		
		while (sit.hasNext()) {
			T service = sit.next();
			if (and.extract(this, null, service)) {
				ret.add(service);
			}
		}
		return Collections.unmodifiableCollection(ret).iterator();
	}

}
