/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.asm;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import net.ranides.assira.collection.ArrayUtils;
import net.ranides.assira.generic.BiFunction;



/**
 * Generator obiektów proxy, które delegują funkcje do wskazanego obiektu.
 * @author ranides
 */
public final class ProxyDelegator {

    private ProxyDelegator() {
        // utility class
    }

    /**
     * Generuje obiekt proxy implementujący podany interfejs, który deleguje wszystkie
     * wywołania funkcji do podanego obiektu {@code object}. Wszystkie argumenty
     * wywołania są konwertowane przez metodę {@code function.apply}. Wszystkie
     * zwracane wyniki są konwertowane przez metodę {@code function.inverse}.
     * @param <T>
     * @param object obiekt do którego delegowane są wywołania
     * @param intface implementowany interfejs
     * @param function funkcja konwertująca argumenty wywołania oraz wyniki
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T paramWrapper(final T object, Class<T> intface, final BiFunction<Object,Object> function) {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        return (T)Proxy.newProxyInstance(loader, new Class[]{intface}, new ParamWrapper(object, function));
    }

    private static class ParamWrapper implements InvocationHandler {
        private final Object delegator;
        private final BiFunction<Object, Object> function;

        public ParamWrapper(Object delegator, BiFunction<Object, Object> function) {
            this.delegator = delegator;
            this.function = function;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return function.apply(method.invoke(delegator, ArrayUtils.inverse(args, function)));
        }
    }

}
