package com.hammurapi.common;

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

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

/**
 * Simple context implementation.
 * @author Pavel Vlasov
 *
 */
public class SimpleMutableContext implements MutableContext {
	
	private Map<String, Object> bindings = new HashMap<String, Object>();
	private Map<Class<?>, Collection<?>> services = new HashMap<Class<?>, Collection<?>>();
	private Context[] chain;

	public SimpleMutableContext(Context... chain) {
		this.chain = chain;
	}
	
	/**
	 * @param classLoader ClassLoader for services. Can be null.
	 * @param bindings Name bindings. Can be null.
	 */
	public SimpleMutableContext(Map<Class<?>, Collection<?>> services, Map<String, ?> bindings, Context... chain) {
		this(chain);
		if (services!=null) {
			this.services.putAll(services);
		}
		
		if (bindings!=null) {
			this.bindings.putAll(bindings);
		}
	}
	
	public synchronized void bind(String name, Object value) {
		bindings.put(name, value);
	}

	public synchronized Object unbind(String name) {
		return bindings.remove(name);
	}

	@SuppressWarnings("unchecked")
	public synchronized <T> void register(Class<? super T> serviceType, T service) {
		Collection<Object> sl = (Collection<Object>) services.get(serviceType);
		if (sl==null) {
			sl = new ArrayList<Object>();
			services.put(serviceType, sl);
		}
		sl.add(service);
	}

	public synchronized <T> void unregister(Class<? super T> serviceType, T service) {
		Collection<?> sl = services.get(serviceType);
		if (sl!=null) {
			sl.remove(service);
			if (sl.isEmpty()) {
				services.remove(serviceType);
			}
		}
	}
	
	public Object lookup(String name) {
		Object ret = bindings.get(name);
		if (ret==null) {
			for (Context cc: chain) {
				ret = cc==null ? null : cc.lookup(name);
				if (ret!=null) {
					break;
				}
			}
		}
		return ret;
	}

	@SuppressWarnings("unchecked")
	public <T> T lookup(Class<T> serviceClass, Predicate<T, Context>... selectors) {
		Collection<?> sl = services.get(serviceClass);

		if (sl!=null) {
			Iterator<?> sit = sl.iterator();
			And<T, Context> and = new And<T, Context>(0, null, selectors);
			while (sit.hasNext()) {
				T service = (T) sit.next();
				if (and.extract(this, null, service)) {
					return service;
				}
			}
		}
		
		for (Context cc: chain) {
			T ret = cc==null ? null : cc.lookup(serviceClass, selectors);
			if (ret!=null) {
				return ret;
			}
		}
		
		return null;
	}

	@SuppressWarnings("unchecked")
	public <T> Iterator<T> lookupAll(Class<T> serviceClass, Predicate<T, Context>... selectors) {
		Collection<?> sl = services.get(serviceClass);		
		And<T, Context> and = new And<T, Context>(0, null, selectors);
		Collection<T> ret = new ArrayList<T>();
		if (sl!=null) {
			Iterator<?> sit = sl.iterator();		
			while (sit.hasNext()) {
				T service = (T) sit.next();
				if (and.extract(this, null, service)) {
					ret.add(service);
				}
			}
		}
			
		for (Context cc: chain) {
			Iterator<T> cit = cc==null ? null : cc.lookupAll(serviceClass, selectors);
			if (cit!=null) {
				while (cit.hasNext()) {
					ret.add(cit.next());
				}
			}
		}
			
				
		return (Iterator<T>) ret.iterator();
	}

}
