package de.ladkau.questshock;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.SystemClock;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import java.nio.ByteBuffer;

/**
 * An off-screen, never-attached-to-a-window View tree for questshock's
 * OpenXR menu quad (see android/app/src/main/cpp/xr_menu.c, which owns the
 * quad's swapchain and drives this class entirely via JNI - construction,
 * touch input, and pixel readback). Rendering to a Bitmap and dispatching
 * synthetic MotionEvents into an unattached hierarchy both work the same
 * way they would for an attached View - draw(Canvas)/dispatchTouchEvent()
 * don't require a ViewRootImpl/window, just a measured+laid-out tree.
 */
public class MenuOverlay {
    private static final String TAG = "QuestShock";

    // Matches xr_menu.c's MENU_WIDTH/MENU_HEIGHT swapchain size - fixed,
    // not tied to any real display metric, since this tree is never shown
    // in a real window.
    static final int WIDTH = 1024;
    static final int HEIGHT = 768;

    private static MenuOverlay instance;

    private final Activity activity;
    private final FrameLayout root;
    private final Button keyboardButton;
    private final Bitmap bitmap;
    private final Canvas canvas;

    private final Object pixelLock = new Object();
    private byte[] pendingPixels;

    private MenuOverlay(Activity activity) {
        this.activity = activity;

        root = new FrameLayout(activity);
        root.setBackgroundColor(0xFF202020);

        keyboardButton = new Button(activity);
        keyboardButton.setText("Keyboard");
        keyboardButton.setOnClickListener(v -> Log.i(TAG, "MenuOverlay: Keyboard button clicked"));
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(420, 140);
        lp.gravity = Gravity.CENTER;
        root.addView(keyboardButton, lp);

        bitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888);
        canvas = new Canvas(bitmap);

        int widthSpec = View.MeasureSpec.makeMeasureSpec(WIDTH, View.MeasureSpec.EXACTLY);
        int heightSpec = View.MeasureSpec.makeMeasureSpec(HEIGHT, View.MeasureSpec.EXACTLY);
        root.measure(widthSpec, heightSpec);
        root.layout(0, 0, WIDTH, HEIGHT);

        forceRedraw();
    }

    // Called from xr_menu.c's xr_menu_init(), on the render thread, right
    // after it resolves this class via the activity's own ClassLoader
    // (plain FindClass() can't see app classes from a thread that was
    // attached to the JVM rather than spawned from Java - see that file's
    // xr_menu_find_class()).
    public static void nativeInit(Activity activity) {
        if (instance == null) {
            instance = new MenuOverlay(activity);
        }
    }

    // Polled once per frame while the menu quad is visible (see
    // xr_menu_render_if_visible()) - returns the current pixels only once
    // per redraw (null otherwise), so the native side knows when it can
    // skip re-uploading a texture that hasn't actually changed.
    public static byte[] nativeTakePixelsIfDirty() {
        if (instance == null) {
            return null;
        }
        synchronized (instance.pixelLock) {
            byte[] pixels = instance.pendingPixels;
            instance.pendingPixels = null;
            return pixels;
        }
    }

    // u/v are the menu quad's own hit-test coordinates (0..1, top-left
    // origin) - computed by xr_input.c's ray/quad intersection against
    // xr_menu.c's reported quad geometry, forwarded here as a synthetic
    // tap. Runs on the UI thread since the View/Bitmap/Canvas objects here
    // are otherwise only ever touched from there.
    public static void nativeDispatchTouch(final float u, final float v, final boolean down) {
        if (instance == null) {
            return;
        }
        instance.activity.runOnUiThread(() -> instance.handleTouch(u, v, down));
    }

    private void handleTouch(float u, float v, boolean down) {
        float x = u * WIDTH;
        float y = v * HEIGHT;
        long time = SystemClock.uptimeMillis();
        MotionEvent event = MotionEvent.obtain(
            time, time, down ? MotionEvent.ACTION_DOWN : MotionEvent.ACTION_UP, x, y, 0);
        try {
            root.dispatchTouchEvent(event);
        } finally {
            event.recycle();
        }
        forceRedraw();
    }

    private void forceRedraw() {
        canvas.drawColor(0xFF202020);
        root.draw(canvas);

        byte[] pixels = new byte[WIDTH * HEIGHT * 4];
        // ARGB_8888's actual in-memory byte order is R,G,B,A - matches
        // GL_RGBA/GL_UNSIGNED_BYTE on the native side with no swizzling.
        bitmap.copyPixelsToBuffer(ByteBuffer.wrap(pixels));
        synchronized (pixelLock) {
            pendingPixels = pixels;
        }
    }
}
