package examples.advanced;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import org.diablitozzz.jin.Jin;
import org.diablitozzz.jin.JinException;
import org.diablitozzz.jin.impl.JinBuilder;
import org.diablitozzz.jin.impl.JinClassLoaderImpl;
import org.testng.Assert;
import org.testng.annotations.Test;

import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyObject;

public class Proxy {

    public static class UserService {

        public void run() {
        }

    }
    
    @Test
    public void test() throws JinException {

        final Set<String> before = new HashSet<>();
        final Set<String> after = new HashSet<>();
        
        final Jin jin = JinBuilder.NEW().build();
        jin.classLoader(new JinClassLoaderImpl() {

            @Override
            public <T> T createInstance(final Class<T> targetClass, final Object[] params) throws JinException {

                final javassist.util.proxy.ProxyFactory proxyFactory = new javassist.util.proxy.ProxyFactory();
                proxyFactory.setSuperclass(targetClass);
                proxyFactory.setFilter(new MethodFilter() {
                    @Override
                    public boolean isHandled(final Method method) {
                        return method.getDeclaringClass() != Object.class;
                    }
                });
                @SuppressWarnings("unchecked")
                final Class<T> proxyClass = proxyFactory.createClass(); //TODO create cache for performance
                
                final T instance = super.createInstance(proxyClass, params);
                ((ProxyObject) instance).setHandler(new MethodHandler() {

                    @Override
                    public Object invoke(final Object self, final Method origMethod, final Method proxyMethod, final Object[] args) throws Throwable {
                        try {
                            System.out.println("before " + origMethod + " " + Arrays.toString(args));
                            before.add(origMethod.getName());
                            final Object result = proxyMethod.invoke(self, args);
                            System.out.println("after " + origMethod + " " + Arrays.toString(args));
                            after.add(origMethod.getName());
                            return result;
                        } catch (final InvocationTargetException e) {
                            throw e.getTargetException();
                        } catch (final Throwable e) {
                            throw e;
                        }
                    }
                });
                return instance;
            }
            
        });
        
        final UserService us = jin.singleton(UserService.class);

        Assert.assertFalse(before.contains("run"));
        Assert.assertFalse(after.contains("run"));

        us.run();

        Assert.assertTrue(before.contains("run"));
        Assert.assertTrue(after.contains("run"));

    }
}
