/*
 *
 */
package org.jruby.ext.ffi.jffi;

import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
import java.util.concurrent.atomic.AtomicInteger;

import org.jruby.RubyInstanceConfig;
import org.jruby.RubyModule;
import org.jruby.ext.ffi.Type;
import org.jruby.util.cli.Options;

/**
 *
 */
final class JITHandle {

    private static final int THRESHOLD = Options.FFI_COMPILE_THRESHOLD.load();
    private final JITSignature jitSignature;
    private volatile boolean compilationFailed = false;
    private final AtomicInteger counter = new AtomicInteger(0);
    private final JITCompiler compiler;
    private Reference<Class<? extends NativeInvoker>> compiledClassRef = null;

    JITHandle(JITCompiler compiler, JITSignature signature, boolean compilationFailed) {
        this.compiler = compiler;
        this.jitSignature = signature;
        this.compilationFailed = compilationFailed;
    }

    final boolean compilationFailed() {
        return compilationFailed;
    }

    final NativeInvoker compile(RubyModule implementationClass, com.kenai.jffi.Function function, Signature signature, String methodName) {
        if (compilationFailed || (counter.incrementAndGet() < THRESHOLD && Options.COMPILE_MODE.load() != RubyInstanceConfig.CompileMode.FORCE)) {
            return null;
        }

        Class<? extends NativeInvoker> compiledClass;
        synchronized (this) {
            if (compiledClassRef == null || (compiledClass = compiledClassRef.get()) == null) {
                compiledClass = newInvokerClass(jitSignature, methodName);
                if (compiledClass == null) {
                    compilationFailed = true;
                    return null;
                }
                compiler.registerClass(this, compiledClass);
                compiledClassRef = new WeakReference<Class<? extends NativeInvoker>>(compiledClass);
            }
        }

        try {
            Constructor<? extends NativeInvoker> cons = compiledClass.getDeclaredConstructor(RubyModule.class, com.kenai.jffi.Function.class, Signature.class);
            return cons.newInstance(implementationClass, function, signature);
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }

    Class<? extends NativeInvoker> newInvokerClass(JITSignature jitSignature, String methodName) {

        JITMethodGenerator generator = null;
        JITMethodGenerator[] generators = {
            new FastIntMethodGenerator(),
            new FastLongMethodGenerator(),
            new FastNumericMethodGenerator(),};

        for (int i = 0; i < generators.length; i++) {
            if (generators[i].isSupported(jitSignature)) {
                generator = generators[i];
                break;
            }
        }

        if (generator == null) {
            return null;
        }

        return new AsmClassBuilder(generator, jitSignature, methodName).build();
    }
}
