package de.rhauswald.util.guice.proxy;

import static org.junit.Assert.assertEquals;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;

import org.junit.Before;
import org.junit.Test;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.name.Names;

public class ProxyBindingProviderTest {

	private Injector injector;

	@Before
	public void setUp() throws Exception {
		injector = Guice.createInjector(new Module[] { new MyModule() });
	}

	@Test
	public void testGet() {
		MyService service;
		try {
			service = injector.getInstance(MyService.class);
			assertEquals("this felt good", service.doSomethingStupid());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private static interface MyService {
		public String doSomethingStupid();
	}

	private static class MyServiceImpl implements MyService {
		public MyServiceImpl() {
		}

		@Override
		public String doSomethingStupid() {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				throw new RuntimeException(
						"I DO NEED SLEEP! DO NOT DISTURB ME!");
			}
			return "this felt good";
		}
	}

	private static class CallTimeProxy implements InvocationHandler {
		private final Object target;

		public CallTimeProxy(Object target) {
			this.target = target;
		}

		@Override
		public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
			long nanoTime = System.nanoTime();
			Object ret;
			try {
				ret = method.invoke(target, args);
			} catch (InvocationTargetException e) {
				logTheTime(method, nanoTime);
				throw e.getCause();
			}
			logTheTime(method, nanoTime);
			return ret;
		}

		private void logTheTime(Method method, long nanoTime) {
			double time = Double.valueOf(System.nanoTime() - nanoTime) / 1000000.0;
			System.out.println(MessageFormat.format("Call of {0} took {1}ms",
					method.toGenericString(), time));
		}

	}

	private static class MyModule extends AbstractModule {
		@SuppressWarnings("unchecked")
		@Override
		protected void configure() {
			bind(MyService.class).annotatedWith(Names.named("intercept")).to(
					MyServiceImpl.class);
			bind(MyService.class).toProvider(
					new ProxyBindingProvider<MyService>(Key.get(
							MyService.class, Names.named("intercept")),
							CallTimeProxy.class));
		}
	}

}
