import javax.swing.*;
import java.awt.image.BufferedImage;

private static final int WIDTH = 500;
private static final int HEIGHT = 500;
private static float timeOffset = 0.0f;
private static ScheduledExecutorService executor;
private static long timestamp = System.currentTimeMillis();

private static void createAndShowGUI() {
    executor = Executors.newSingleThreadScheduledExecutor();

    // Create a memory segment for RGBA pixel data with a longer-lived arena
    Arena arena = Arena.ofShared(); // Use shared arena instead of confined
    MemorySegment pixelBuffer = arena.allocate(WIDTH * HEIGHT * 4);

    // Fill the buffer with a gradient pattern
    fillPixelBuffer(pixelBuffer, WIDTH, HEIGHT);

    // Convert to BufferedImage for display
    var imageIcon = new ImageIcon(createImageFromPixelBuffer(pixelBuffer, WIDTH, HEIGHT));
    var imageLabel = new JLabel(imageIcon);

    // Start schedule to update the image every second
    executor.scheduleAtFixedRate(() -> {
        try {
            timeOffset += 0.1f;
            fillPixelBuffer(pixelBuffer, WIDTH, HEIGHT);
            SwingUtilities.invokeLater(() -> {
                imageIcon.setImage(createImageFromPixelBuffer(pixelBuffer, WIDTH, HEIGHT));
                imageLabel.repaint();
            });
        } catch (Exception e) {
            System.err.println("Error in scheduled task: " + e.getMessage());
        }
    }, 2_000, 5, TimeUnit.MILLISECONDS);

    // Display in a JFrame
    JFrame frame = new JFrame("Foreign Memory API - Pixel Buffer Example");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(imageLabel);
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);

    // Close arena on window close
    frame.addWindowListener(new java.awt.event.WindowAdapter() {
        @Override
        public void windowClosing(java.awt.event.WindowEvent e) {
            if (executor != null) {
                executor.shutdown();
            }
            arena.close();
            IO.println("Closed arena");
        }
    });
}

/**
 * Fill the pixel buffer with a colorful gradient pattern
 * Demonstrates direct memory manipulation without array copies
 */
private static void fillPixelBuffer(MemorySegment memorySegment, int width, int height) {
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            long pixelOffset = ((long) y * width + x) * 4L;

            // Create a gradient effect
            int red = (int) (((Math.sin(x * 0.1f + timeOffset) + 1) * 127.5f));
            int green = (int) (((Math.cos(y * 0.1f + timeOffset) + 1) * 127.5f));
            int blue = (int) (((Math.sin((x + y) * 0.1f + timeOffset) + 1) * 127.5f));
            int alpha = 255; // Fully opaque

            // Write RGBA bytes directly to memory
            // This is where the magic happens - direct memory access!
            memorySegment.set(ValueLayout.JAVA_BYTE, pixelOffset, (byte) red);
            memorySegment.set(ValueLayout.JAVA_BYTE, pixelOffset + 1, (byte) green);
            memorySegment.set(ValueLayout.JAVA_BYTE, pixelOffset + 2, (byte) blue);
            memorySegment.set(ValueLayout.JAVA_BYTE, pixelOffset + 3, (byte) alpha);
        }
    }

    var now = System.currentTimeMillis();
    IO.println("Interval: " + (now - timestamp) + " - Generated " + (width * height) + " pixels");
    timestamp = now;
}

/**
 * Convert the raw pixel data from MemorySegment to BufferedImage
 * In a real application, this might interface with native graphics libraries
 */
private static BufferedImage createImageFromPixelBuffer(MemorySegment memorySegment, int width, int height) {
    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            long pixelOffset = ((long) y * width + x) * 4L;

            // Read RGBA bytes directly from memory
            int red = Byte.toUnsignedInt(memorySegment.get(ValueLayout.JAVA_BYTE, pixelOffset));
            int green = Byte.toUnsignedInt(memorySegment.get(ValueLayout.JAVA_BYTE, pixelOffset + 1));
            int blue = Byte.toUnsignedInt(memorySegment.get(ValueLayout.JAVA_BYTE, pixelOffset + 2));
            int alpha = Byte.toUnsignedInt(memorySegment.get(ValueLayout.JAVA_BYTE, pixelOffset + 3));

            // Pack into ARGB integer
            int argb = (alpha << 24) | (red << 16) | (green << 8) | blue;
            image.setRGB(x, y, argb);
        }
    }

    return image;
}

void main() {
    SwingUtilities.invokeLater(() -> {
        createAndShowGUI();
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            if (executor != null) {
                executor.shutdown();
            }
        }));
    });
}
