package com.ninty.nativee.sun.misc;

import com.ninty.nativee.INativeMethod;
import com.ninty.nativee.NaMethodManager;
import com.ninty.runtime.LocalVars;
import com.ninty.runtime.NiFrame;
import com.ninty.runtime.heap.NiObject;
import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * Created by ninty on 2017/10/8.
 */
public class NaUnsafe {
    private static Unsafe unsafe;

    static {
        Field field;
        try {
            field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    private final static String className = "sun/misc/Unsafe";

    public static void init() {
        NaMethodManager.register(className, "arrayBaseOffset", "(Ljava/lang/Class;)I",
                new arrayBaseOffset());
        NaMethodManager.register(className, "arrayIndexScale", "(Ljava/lang/Class;)I",
                new arrayIndexScale());
        NaMethodManager.register(className, "addressSize", "()I",
                new addressSize());
        NaMethodManager.register(className, "allocateMemory", "(J)J",
                new allocateMemory());
        NaMethodManager.register(className, "putLong", "(JJ)V",
                new putLong());
        NaMethodManager.register(className, "getByte", "(J)B",
                new getByte());
        NaMethodManager.register(className, "freeMemory", "(J)V",
                new freeMemory());
        NaMethodManager.register(className, "objectFieldOffset", "(Ljava/lang/reflect/Field;)J",
                new objectFieldOffset());
        NaMethodManager.register(className, "compareAndSwapObject", "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z",
                new compareAndSwapObject());
        NaMethodManager.register(className, "getIntVolatile", "(Ljava/lang/Object;J)I",
                new getIntVolatile());
    }

    public static class arrayBaseOffset implements INativeMethod {
        @Override
        public void invoke(NiFrame frame) {
            frame.getOperandStack().pushInt(0);
        }
    }

    public static class arrayIndexScale implements INativeMethod {
        @Override
        public void invoke(NiFrame frame) {
            frame.getOperandStack().pushInt(1);
        }
    }

    public static class addressSize implements INativeMethod {
        @Override
        public void invoke(NiFrame frame) {
            frame.getOperandStack().pushInt(unsafe.addressSize());
        }
    }

    public static class allocateMemory implements INativeMethod {
        @Override
        public void invoke(NiFrame frame) {
            long size = frame.getLocalVars().getLong(1);
            long addr = unsafe.allocateMemory(size);
            frame.getOperandStack().pushLong(addr);
        }
    }

    public static class putLong implements INativeMethod {
        @Override
        public void invoke(NiFrame frame) {
            LocalVars localVars = frame.getLocalVars();
            long l1 = localVars.getLong(1);
            long l2 = localVars.getLong(3);
            unsafe.putLong(l1, l2);
        }
    }

    public static class getByte implements INativeMethod {
        @Override
        public void invoke(NiFrame frame) {
            LocalVars localVars = frame.getLocalVars();
            long addr = localVars.getLong(1);
            byte data = unsafe.getByte(addr);
            frame.getOperandStack().pushInt(data);
        }
    }

    public static class freeMemory implements INativeMethod {
        @Override
        public void invoke(NiFrame frame) {
            LocalVars localVars = frame.getLocalVars();
            long addr = localVars.getLong(1);
            unsafe.freeMemory(addr);
        }
    }

    public static class objectFieldOffset implements INativeMethod {
        @Override
        public void invoke(NiFrame frame) {
            LocalVars localVars = frame.getLocalVars();
            NiObject fieldObj = localVars.getRef(1);
            frame.getOperandStack().pushLong(fieldObj.getFieldInt("slot"));
        }
    }

    // TODO: CAS
    public static class compareAndSwapObject implements INativeMethod {
        @Override
        public void invoke(NiFrame frame) {
            LocalVars localVars = frame.getLocalVars();
            NiObject obj = localVars.getRef(1);
            int offset = (int) localVars.getLong(2);
            NiObject expect = localVars.getRef(4);
            NiObject update = localVars.getRef(5);
            if (obj.getFields().getRef(offset) == expect) {
                obj.getFields().setRef(offset, update);
                frame.getOperandStack().pushBoolean(true);
            } else {
                frame.getOperandStack().pushBoolean(false);
            }
        }
    }

    public static class getIntVolatile implements INativeMethod {
        @Override
        public void invoke(NiFrame frame) {
            LocalVars localVars = frame.getLocalVars();
            NiObject obj = localVars.getRef(1);
            long offset = localVars.getLong(2);
            LocalVars fields = obj.getFields();
            int result = fields.getInt((int) offset);
            frame.getOperandStack().pushInt(result);
        }
    }
}

@@ -1913,24 +1913,159 @@
 pObject());%0A
+        NaMethodManager.register(className, %22compareAndSwapInt%22, %22(Ljava/lang/Object;JII)Z%22,%0A                new compareAndSwapInt());%0A
         NaMe
@@ -5045,32 +5045,698 @@
 %0A        %7D%0A    %7D
+%0A    public static class compareAndSwapInt implements INativeMethod %7B%0A        @Override%0A        public void invoke(NiFrame frame) %7B%0A            LocalVars localVars = frame.getLocalVars();%0A            NiObject obj = localVars.getRef(1);%0A            int offset = (int) localVars.getLong(2);%0A            int expect = localVars.getInt(4);%0A            int update = localVars.getInt(5);%0A            if (obj.getFields().getInt(offset) == expect) %7B%0A                obj.getFields().setInt(offset, update);%0A                frame.getOperandStack().pushBoolean(true);%0A            %7D else %7B%0A                frame.getOperandStack().pushBoolean(false);%0A            %7D%0A        %7D%0A    %7D
 %0A%0A    public sta

add native method: sun.misc.unsafe.compareAndSwapInt