import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SymbolLookup;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;

import static java.lang.foreign.ValueLayout.ADDRESS;
import static java.lang.foreign.ValueLayout.JAVA_INT;
import static java.lang.foreign.ValueLayout.JAVA_LONG;

public class UrutNative {
    public static void main(String[] args) throws Throwable {
        Linker linker = Linker.nativeLinker();
        SymbolLookup libc = linker.defaultLookup();

        // void qsort(void *base, size_t nmemb, size_t size,
        //            int (*compar)(const void *, const void *));
        MethodHandle qsort = linker.downcallHandle(
                libc.find("qsort").get(),
                FunctionDescriptor.ofVoid(ADDRESS, JAVA_LONG, JAVA_LONG, ADDRESS));

        // Deskriptor fungsi pembanding: pointer dengan target layout int.
        FunctionDescriptor deskriptorBanding = FunctionDescriptor.of(JAVA_INT,
                ADDRESS.withTargetLayout(JAVA_INT),
                ADDRESS.withTargetLayout(JAVA_INT));
        MethodHandle pembanding = MethodHandles.lookup().findStatic(
                UrutNative.class, "bandingkan", deskriptorBanding.toMethodType());

        try (Arena arena = Arena.ofConfined()) {
            int[] data = {42, 7, 19, 3, 88, 1, 56, 23};
            MemorySegment larik = arena.allocateFrom(JAVA_INT, data);

            // Upcall stub: method Java dibungkus menjadi function pointer.
            MemorySegment fungsiBanding = linker.upcallStub(
                    pembanding, deskriptorBanding, arena);

            qsort.invoke(larik, (long) data.length, JAVA_INT.byteSize(),
                    fungsiBanding);

            System.out.println("sebelum : " + Arrays.toString(data));
            System.out.println("sesudah : " + Arrays.toString(larik.toArray(JAVA_INT)));
        }
    }

    // Dipanggil dari dalam qsort lewat upcall stub.
    static int bandingkan(MemorySegment a, MemorySegment b) {
        return Integer.compare(a.get(JAVA_INT, 0L), b.get(JAVA_INT, 0L));
    }
}
