package jnr.ffi.provider.jffi;

import jnr.ffi.Pointer;
import jnr.ffi.Runtime;
import jnr.ffi.mapper.CompositeTypeMapper;
import jnr.ffi.mapper.DefaultSignatureType;
import jnr.ffi.mapper.FromNativeContext;
import jnr.ffi.mapper.FromNativeConverter;
import jnr.ffi.provider.ClosureManager;
import ru.serce.jnrfuse.FuseException;

import java.lang.reflect.Field;
import java.util.Collections;
import java.util.concurrent.ConcurrentHashMap;

public class ClosureHelper {

    private final AsmClassLoader classLoader;
    private final CompositeTypeMapper ctm;
    private final SimpleNativeContext ctx;

    private final ConcurrentHashMap<Class<?>, FromNativeConverter<?, Pointer>> cache = new ConcurrentHashMap<>();

    public static ClosureHelper getInstance() {
        return SingletonHolder.INSTANCE;
    }

    @SuppressWarnings("unchecked")
    public <T> FromNativeConverter<T, Pointer> getNativeConveter(Class<T> closureClass) {
        FromNativeConverter<T, Pointer> result = (FromNativeConverter<T, Pointer>) cache.get(closureClass);
        if (result != null) {
            return result;
        }
        result = (FromNativeConverter<T, Pointer>) ClosureFromNativeConverter.
                getInstance(Runtime.getSystemRuntime(), DefaultSignatureType.create(closureClass, (FromNativeContext) ctx), classLoader, ctm);
        cache.putIfAbsent(closureClass, result);
        return result;
    }

    public FromNativeContext getFromNativeContext() {
        return ctx;
    }


    private static class SingletonHolder {
        private static final ClosureHelper INSTANCE = new ClosureHelper();
    }

    private ClosureHelper() {
        try {
            ClosureManager closureManager = jnr.ffi.Runtime.getSystemRuntime().getClosureManager();
            Field classLoaderField = NativeClosureManager.class.getDeclaredField("classLoader");
            classLoaderField.setAccessible(true);

            classLoader = (AsmClassLoader) classLoaderField.get(closureManager);

            Field typeMapperField = NativeClosureManager.class.getDeclaredField("typeMapper");
            typeMapperField.setAccessible(true);
            ctm = (CompositeTypeMapper) typeMapperField.get(closureManager);
            ctx = new SimpleNativeContext(Runtime.getSystemRuntime(), Collections.emptyList());
        } catch (Exception e) {
            throw new FuseException("Unable to create helper", e);
        }
    }
}

@@ -386,16 +386,38 @@
 ctions;%0A
+import java.util.Map;%0A
 import j
@@ -519,17 +519,20 @@
 sLoader 
-c
+asmC
 lassLoad
@@ -1336,17 +1336,20 @@
 ) ctx), 
-c
+asmC
 lassLoad
@@ -1806,17 +1806,20 @@
   Field 
-c
+asmC
 lassLoad
@@ -1816,24 +1816,25 @@
 mClassLoader
+s
 Field = Nati
@@ -1874,17 +1874,20 @@
 dField(%22
-c
+asmC
 lassLoad
@@ -1888,16 +1888,17 @@
 ssLoader
+s
 %22);%0A    
@@ -1897,33 +1897,36 @@
 %22);%0A            
-c
+asmC
 lassLoaderField.
@@ -1915,24 +1915,25 @@
 mClassLoader
+s
 Field.setAcc
@@ -1960,17 +1960,53 @@
         
-c
+Map%3CClassLoader, AsmClassLoader%3E asmC
 lassLoad
@@ -2007,20 +2007,38 @@
 ssLoader
+s
  = (
+Map%3CClassLoader, 
 AsmClass
@@ -2043,19 +2043,23 @@
 ssLoader
+%3E
 ) 
-c
+asmC
 lassLoad
@@ -2060,16 +2060,17 @@
 ssLoader
+s
 Field.ge
@@ -2087,16 +2087,104 @@
 anager);
+%0A            asmClassLoader = asmClassLoaders.get(ClosureHelper.class.getClassLoader());
 %0A%0A      

Make jnr-fuse work with jnr-ffi 2.1.8 (#61)