package net.ranides.assira.reflection.impl;

import net.ranides.assira.collection.maps.CacheMap;
import net.ranides.assira.reflection.IClass;
import net.ranides.assira.reflection.IContext;
import net.ranides.assira.reflection.IMethod;

import java.util.List;

import static net.ranides.assira.reflection.IAttribute.*;

/**
 * Static factory for lambda methods.
 * Caches results internally using lambda object as cache key.
 */
public class LMethodFactory {

    private static final CacheMap<Object, IMethod> FUNCTIONS = CacheMap.getInstance(LMethodFactory::inewFunction);

    /**
     * Collects information for given lambda expression
     * @param object object
     * @return IMethod
     */
    public static IMethod typeinfo(Object object) {
        return FUNCTIONS.get(object);
    }

    private static IMethod inewFunction(Object object) {
        IClass<?> type = IClass.typefor(object);
        if(type.attributes().contains(SYNTHETIC)) {
            List<IMethod> methods = type.methods().require(DECLARED).discard(STATIC).list();
            if(1 == methods.size()) {
                return new LMethod(methods.get(0), object);
            }
        }

        int methods = type.interfaces().methods().require(ABSTRACT).stream().distinct().size();
        if(1 == methods ) {
            IMethod m = type.methods().require(DECLARED).discard(SYNTHETIC).discard(BRIDGE).first().get();
            return new RMethod(true, IContext.getDefault(), m.reflective(), object);
        }

        throw new IllegalArgumentException(type + " is not lambda");
    }

}
