package com.github.davidmoten.rx;

import static java.lang.annotation.ElementType.METHOD;
import static org.junit.Assert.assertEquals;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;

import org.junit.Test;

public class DynamicProxyTest {

    @Target({ METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    public static @interface Column {
        String value();
    }

    @Target({ METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    public static @interface Index {
        /**
         * 1 based index corresponding the index in a
         * <code>ResultSet.getObject(index)</code> call.
         * 
         * @return the 1 based index that the annotated method corresponds to in
         *         the ResultSet
         */
        int value();
    }

    public static interface Thing {
        @Column("table_id")
        int id();

        @Index(2)
        String name();

        @Column("desc")
        String description();
    }

    @Test
    public void testDynamicProxy() {
        Thing t = ProxyService.newInstance(Thing.class);
        assertEquals(123, t.id());
        assertEquals("fred name", t.name());
        assertEquals("he's the business! desc", t.description());
    }

    public static class ProxyService implements java.lang.reflect.InvocationHandler {

        @SuppressWarnings("unchecked")
        public static <T> T newInstance(Class<T> cls) {
            return (T) java.lang.reflect.Proxy.newProxyInstance(cls.getClassLoader(),
                    new Class[] { cls }, new ProxyService());
        }

        public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
            Column a = m.getAnnotation(Column.class);
            final String column;
            if (a == null)
                column = null;
            else
                column = a.value();
            String name = m.getName();
            if (name.equals("id")) {
                return 123;
            } else if (name.equals("name")) {
                return "fred " + column;
            } else if (name.equals("description")) {
                return "he's the business! " + column;
            } else
                throw new RuntimeException("unexpected");
        }
    }

}

@@ -944,16 +944,54 @@
 nt id();
+%0A        %0A        int nonNullNumber();
 %0A%0A      
@@ -1253,20 +1253,17 @@
 s(%22fred 
-name
+2
 %22, t.nam
@@ -1332,33 +1332,243 @@
 ption());%0A    %7D%0A
+    %0A    @Test(expected=NullPointerException.class)%0A    public void testDynamicProxyReturnsNullForNonNullMethod() %7B%0A        Thing t = ProxyService.newInstance(Thing.class);%0A        t.nonNullNumber();%0A    %7D%0A    
 %0A
-
     public stati
@@ -2356,30 +2356,60 @@
 n %22fred %22 + 
-column
+m.getAnnotation(Index.class).value()
 ;%0A          
@@ -2498,32 +2498,32 @@
 ss! %22 + column;%0A
-
             %7D el
@@ -2520,24 +2520,104 @@
       %7D else
+ if (name.equals(%22nonNullNumber%22)) %0A               return null;%0A            else
 %0A           

add dynamic proxy test