package com.hammurapi.common;

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

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

public class SingletonChainingContext<ST> implements MutableContext {
	
	private Class<ST> type;
	private ST service;
	private Context chain;

	public SingletonChainingContext(Class<ST> type, ST service, Context chain) {
		this.type = type;
		this.service = service;
		this.chain = chain;
	}

	public void bind(String name, Object obj) {
		if (chain instanceof MutableContext) {
			((MutableContext) chain).bind(name, obj);
		} else {
			throw new IllegalStateException("Chain context is not mutable");
		}
	}

	public <T> void register(Class<? super T> type, T service) {
		if (chain instanceof MutableContext) {
			((MutableContext) chain).register(type, service);
		} else {
			throw new IllegalStateException("Chain context is not mutable");
		}
	}

	public Object unbind(String name) {
		if (chain instanceof MutableContext) {
			return ((MutableContext) chain).unbind(name);
		} else {
			throw new IllegalStateException("Chain context is not mutable");
		}		
	}

	public <T> void unregister(Class<? super T> type, T service) {
		if (chain instanceof MutableContext) {
			((MutableContext) chain).unregister(type, service);
		} else {
			throw new IllegalStateException("Chain context is not mutable");
		}
	}

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

	@SuppressWarnings("unchecked")
	public <T> T lookup(Class<T> serviceClass, Predicate<T, Context>... selectors) {
		And<T, Context> and = new And<T, Context>(0, null, selectors);
		if (serviceClass.equals(type) && and.extract(this, null, (T) service)) {
			return (T) service;
		}
		
		return chain==null ? null : chain.lookup(serviceClass, selectors);
	}

	@SuppressWarnings("unchecked")
	public <T> Iterator<T> lookupAll(Class<T> serviceClass, Predicate<T, Context>... selectors) {
		And<T, Context> and = new And<T, Context>(0, null, selectors);
		if (serviceClass.equals(type) &&  and.extract(this, null, (T) service)) {
			Collection<T> c = new ArrayList<T>();
			c.add((T) service);
			if (chain!=null) {
				Iterator<T> it = chain.lookupAll(serviceClass, selectors);
				while (it.hasNext()) {
					c.add(it.next());
				}
			}
			return c.iterator();
		}
		return (Iterator<T>) (chain==null ? Collections.emptyList().iterator() : chain.lookupAll(serviceClass, selectors));
	}

}
