package traits;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

@Component
public class TraitManager {

	@Reference
	volatile List<Trait> traits;

	interface Trait {

		void augment(TraitHost host);

		boolean handles(Class<?> c);

	}

	interface TraitHost {
		<T> void addInstance(Class<T> c, T instance);
	}

	class TraitHostImpl implements InvocationHandler, TraitHost {
		final Map<Class<?>, Object> map = new ConcurrentHashMap<>();

		TraitHostImpl() {
			map.put(Object.class, this);
		}

		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			Class<?> declaringClass = method.getDeclaringClass();
			Object instance = map.get(declaringClass);
			if (instance == null)
				throw new IllegalStateException("No implementation for " + method);
			return method.invoke(instance, args);
		}

		@Override
		public <T> void addInstance(Class<T> c, T instance) {
			map.put(c, instance);
		}

	}


	public <T> T create(Class<T> view) {
		TraitHostImpl host = new TraitHostImpl();

		for (Class<?> c : view.getInterfaces()) {
			findTrait(c).augment(host);
		}
		return view.cast(Proxy.newProxyInstance(view.getClassLoader(), new Class[] {
			view
		}, host));
	}

	private Trait findTrait(Class<?> c) {
		for (Trait trait : traits) {
			if (trait.handles(c))
				return trait;
		}
		throw new IllegalArgumentException("No such trait " + c);
	}
}
