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

import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaConversionException;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Type;
import java.util.List;
import net.ranides.assira.reflection.IAttribute;
import static net.ranides.assira.reflection.IAttribute.BRIDGE;
import static net.ranides.assira.reflection.IAttribute.DECLARED;
import static net.ranides.assira.reflection.IAttribute.SYNTHETIC;
import net.ranides.assira.reflection.IClass;
import net.ranides.assira.reflection.IMethod;
import net.ranides.assira.reflection.IMethods;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class LRunner {
    
    private static Class<?> ttype(IClass<?> ic) {
        Type t = ic.reflective();
        return t instanceof Class ? (Class)t : Object.class;
    }
    
    public static void main(String[] args) throws LambdaConversionException, Throwable {
        IClass<Hello> ic = IClass.typeinfo(Hello.class);
        
        
        MethodHandle mh = ic.method("sum").get().handle();

        IClass<IFunction> icf = IClass.typeinfo(IFunction.class);
        
        IMethod mf = icf.methods().require(DECLARED).discard(SYNTHETIC).discard(BRIDGE).first().get();
        System.out.println("f = " + mf);
        
        Class<?> rf = ttype(mf.returns());
        Class<?>[] rfparams = mf.arguments().types().stream().map(t -> ttype(t)).array(Class[]::new);
        MethodType fmt = MethodType.methodType(rf, rfparams);
        System.out.println("R = " + fmt);
        
        CallSite cs = LambdaMetafactory.metafactory(
                MethodHandles.lookup(),
                "apply",
                MethodType.methodType(IFunction.class),
//                MethodType.methodType(int.class, Object.class, int.class, int.class), /*mh.type(),*/
                fmt,
                mh,
                mh.type() 
        );
        
        IFunction lambda=(IFunction)cs.getTarget().invokeExact();
        
        System.out.println("d = " + lambda.apply(new Hello(400), 3, 0));
    }
    
    
    public static interface IFunction<T> {
        
        int apply(T that, int a, int b);
        
    }
    
    public static class Hello {
        
        private final int seed;

        public Hello(int seed) {
            this.seed = seed;
        }
        
        public int sum(int a, int b) {
            return seed+a+b;
        }
        
        public int mul(int a, int b) {
            return seed+a*b;
        }
        
    };
}
