package com.elphel.imagej.gpu.jna;

import com.elphel.imagej.gpu.GpuQuad;
import com.elphel.imagej.tileprocessor.QuadCLT;
import com.sun.jna.Native;
import com.sun.jna.Pointer;

/**
 * Native (JNA) GPU backend — architecture B: subclass {@link GpuQuad}, but allocate NO JCuda memory and
 * create NO JCuda context. The constructor uses GpuQuad's protected no-alloc constructor (config fields
 * only) and stands up the native tile-processor library ({@code libtileproc.so}) via {@link TpJna}:
 * a {@code TpModule} (NVRTC-compiled kernels) and a persistent {@code TpProc} instance that owns all GPU
 * memory. Each GPU-touching method of GpuQuad is overridden to delegate to {@code TpProc}; methods not yet
 * ported throw {@link UnsupportedOperationException} (so anything off the validated path fails loudly
 * rather than silently using the null base JCuda buffers).
 *
 * Selected once at startup (default backend stays JCUDA); JNA mode never initializes JCuda.
 * Validated kernel path (Stages 1–5 + StageProc): geometry, convert_direct, imclt, correlations.
 * By Claude on 2026-06-25.
 */
public class GpuQuadJna extends GpuQuad {
    private final TpJna   lib;
    private final Pointer module;   // TpModule* (NVRTC kernels)
    private final Pointer proc;      // TpProc*   (persistent device buffers)
    private boolean closed = false;

    /**
     * @param quadCLT   the per-scene QuadCLT (geometry, kernels, sensor config)
     * @param srcdir    directory with the kernel .cu/.h sources for NVRTC (e.g. tile_processor_gpu/src)
     * @param devrt     path to libcudadevrt.a (for CDP linking)
     * @param debugLevel gpu debug level
     */
    public GpuQuadJna(QuadCLT quadCLT, String srcdir, String devrt, int debugLevel) {
        super(quadCLT, debugLevel, true); // no-alloc: config fields only, no JCuda context
        this.lib = Native.load("tileproc", TpJna.class);
        this.module = lib.tp_create_module(srcdir, devrt);
        if (module == null) {
            throw new IllegalStateException("GpuQuadJna: tp_create_module failed: " + lib.tp_last_error());
        }
        this.proc = lib.tp_proc_create(module);
        if (proc == null) {
            throw new IllegalStateException("GpuQuadJna: tp_proc_create failed: " + lib.tp_last_error());
        }
        int rc = lib.tp_proc_setup(proc, num_cams, num_colors, img_width, img_height,
                                   kernels_hor, kern_tiles);
        if (rc != 0) {
            throw new IllegalStateException("GpuQuadJna: tp_proc_setup rc=" + rc + ": " + lib.tp_last_error());
        }
        // tp_proc_setup_rbg_corr (imclt + correlation buffers) is deferred until the correlation config
        // (num_pairs / sel_pairs / color_weights / corr_out_rad) is known at first use.
    }

    /** Native handles for the override implementations (added incrementally). */
    protected TpJna   lib()    { return lib; }
    protected Pointer module() { return module; }
    protected Pointer proc()   { return proc; }

    /** Release native GPU memory + module (deterministic — does not rely on GC/finalize). */
    public synchronized void close() {
        if (closed) return;
        closed = true;
        if (proc != null)   lib.tp_proc_destroy(proc);
        if (module != null) lib.tp_destroy_module(module);
    }

    // NOTE: GPU-touching methods (setGeometryCorrection / setConvolutionKernels / setBayerImages /
    // setTasks / execSetTilesOffsets / execConvertDirect / execImcltRbgAll / getRBG / execCorr2D_* /
    // getCorr2D / getCorr2DCombo / handleWH) are overridden incrementally to delegate to TpProc.
    // Until overridden, the inherited base methods would touch null JCuda buffers — the selector must
    // only route the validated CUAS path here.
}
