#@ File (label="Input folder",               style="directory")                   inputDir
#@ File (label="Output folder",              style="directory")                   outputDir
#@ File (label="Cellpose env python",        style="file")                        envPython
#@ String (label="Model", choices={"cpsam","custom"}, value="custom")             modelChoice
#@ File (label="Custom model (if 'custom')", style="file", required=false)        customModel
#@ String (label="p63+ threshold variant",   choices={"T14","T1","T2","T7"}, value="T14") variant
#@ String (label="Pooling mode",             choices={"per_image","pool_all","pool_replicate","reference_replicate"}, value="per_image") poolingMode
#@ String (label="Reference replicate (e.g. R1)", value="R1")                    referenceReplicate
#@ Double (label="Attenuation cap", style="slider", min=0.0, max=1.0, stepSize=0.05, value=0.7) attenCap
#@ Boolean (label="Use GPU",                 value=false)                         useGpu
#@ Integer (label="Batch size (images per eval)", value=8)                        batchSize
#@ Integer (label="Segmentation channel (blue)", value=3)                         segChannel
#@ Integer (label="Quantification channel (red)", value=1)                        quantChannel

import os
import sys

# Fiji does not add the script's directory to sys.path automatically;
# insert it so that sidecar_runner, display, and review are importable.
#
# Three execution contexts require different path resolution:
#   1. Plugins menu: __file__ is a relative path from the Scripts root
#      (e.g. "p63_quant/p63_Cellpose_Batch.py") -- resolve via IJ.getDirectory
#   2. Script Editor: __file__ is not set at all -- use co_filename (always absolute)
#   3. Absolute __file__: use it directly
try:
    _script_path = __file__
except NameError:
    import inspect as _inspect
    _script_path = _inspect.currentframe().f_code.co_filename

if os.path.isabs(_script_path):
    _here = os.path.dirname(_script_path)
else:
    # Relative path (e.g. "p63_quant/p63_Cellpose_Batch.py" from plugins menu).
    # Build a prioritised list of base directories to search; the first one
    # that contains sidecar_runner.py wins.
    from ij import IJ as _IJ
    _rel_dir = os.path.dirname(_script_path)   # e.g. "p63_quant"
    _p   = _IJ.getDirectory("plugins") or ""
    _ij  = _IJ.getDirectory("imagej")  or ""
    _cwd = os.getcwd()
    _IJ.log("[p63] plugins={} imagej={} cwd={} rel={}".format(_p, _ij, _cwd, _rel_dir))

    _search_bases = []
    if _p:
        _search_bases.append(os.path.join(_p, "Scripts"))
        _search_bases.append(_p)
    if _ij:
        _search_bases.append(os.path.join(_ij, "plugins", "Scripts"))
        _search_bases.append(_ij)
    _search_bases.append(_cwd)
    # Jython may have the Scripts root already in sys.path
    for _sp in sys.path:
        if _sp:
            _search_bases.append(str(_sp))

    _here = None
    for _base in _search_bases:
        _c = os.path.join(_base, _rel_dir)
        if os.path.isfile(os.path.join(_c, "sidecar_runner.py")):
            _here = _c
            break

    if _here is None:
        _here = os.path.dirname(os.path.abspath(_script_path))

    _IJ.log("[p63] script dir resolved to: " + str(_here))

if _here not in sys.path:
    sys.path.insert(0, _here)
# Jython's import machinery doesn't follow directory symlinks; add the
# real (dereferenced) path as well so modules are importable either way.
_real_here = os.path.realpath(_here)
if _real_here != _here and _real_here not in sys.path:
    sys.path.insert(0, _real_here)

# Resolve the sidecar script. Four layouts are supported:
#   1. Repo / symlink layout: sidecar/ is a sibling of the src/ folder
#      (real path of _here has a ../sidecar/ next to it)
#   2. Subdirectory layout: sidecar/ lives inside the scripts folder
#      (e.g. plugins/Scripts/Plugins/p63/sidecar/run_p63.py)
#   3. Fiji update-site layout: sidecar lives in plugins/p63_sidecar/
#      Standard script path is plugins/Scripts/Plugins/<submenu>/ so
#      walking three levels up from _here reaches plugins/.
#   4. Flat install: run_p63.py is copied into the same folder as the
#      Jython scripts.
_real_here = os.path.realpath(_here)
_sidecar_script = os.path.join(os.path.dirname(_real_here), "sidecar", "run_p63.py")
if not os.path.isfile(_sidecar_script):
    # Subdirectory layout: sidecar/ inside the scripts folder
    _sidecar_script = os.path.join(_here, "sidecar", "run_p63.py")
if not os.path.isfile(_sidecar_script):
    # Update-site layout: plugins/p63_sidecar/run_p63.py
    _plugins_root = os.path.dirname(os.path.dirname(os.path.dirname(_here)))
    _sidecar_script = os.path.join(_plugins_root, "p63_sidecar", "run_p63.py")
if not os.path.isfile(_sidecar_script):
    # Flat install: sidecar lives alongside the src files
    _sidecar_script = os.path.join(_here, "run_p63.py")

from ij import IJ

import sidecar_runner
import display
import navigator


def run():
    """Validate inputs, launch the sidecar once over the whole folder, then display and review results per image."""
    input_path  = str(inputDir)
    output_path = str(outputDir)
    python_path = str(envPython)

    # --- Input validation ---
    if not os.path.isdir(input_path):
        IJ.error("p63 Batch", "Input folder not found:\n" + input_path)
        return

    if not os.path.isfile(python_path):
        IJ.error("p63 Batch", "Cellpose env Python not found:\n" + python_path)
        return

    if str(modelChoice) == "custom":
        custom_path = str(customModel) if customModel else ""
        if not custom_path or not os.path.isfile(custom_path):
            IJ.error("p63 Batch", "Custom model file not found:\n" + custom_path)
            return

    if not os.path.isdir(output_path):
        os.makedirs(output_path)

    # --- Launch sidecar (blocks until the whole batch is done) ---
    IJ.log("=== p63 Cellpose Batch ===")
    IJ.log("Input:   " + input_path)
    IJ.log("Output:  " + output_path)
    IJ.log("Variant: " + str(variant))
    IJ.log("Pooling: " + str(poolingMode))

    args = sidecar_runner.build_args(
        inputDir, outputDir, envPython,
        modelChoice, customModel,
        variant, attenCap, useGpu,
        segChannel, quantChannel,
        poolingMode, referenceReplicate,
        _sidecar_script,
        batchSize,
    )

    try:
        sidecar_runner.run_sidecar(args)
    except RuntimeError as exc:
        IJ.error("p63 Batch - Sidecar Error", str(exc))
        return

    # --- Read CSV outputs ---
    summary_rows = sidecar_runner.read_summary_csv(outputDir)
    cells_data   = sidecar_runner.read_cells_csv(outputDir)

    if not summary_rows:
        IJ.log("[WARN] summary.csv is empty -- nothing to display.")
        return

    # Group cells by image for O(1) lookup in the loop below
    cells_by_image = {}
    for row in cells_data:
        cells_by_image.setdefault(row["image"], []).append(row)

    # Map basename -> original image path (for opening in Fiji)
    exts = {".tif", ".tiff", ".png", ".jpg", ".jpeg"}
    image_map = {}
    for fname in os.listdir(input_path):
        base, ext = os.path.splitext(fname)
        if ext.lower() in exts:
            image_map[base] = os.path.join(input_path, fname)

    variant_str = str(variant)
    threshold_col = variant_str.lower() + "_threshold"

    # Build a single ResultsTable for the whole batch
    rt = display.populate_results_table(cells_data)

    # --- Batch navigator: replaces the per-image sequential review loop ---
    navigator.show_navigator(
        summary_rows, cells_by_image, image_map, output_path,
        variant_str, threshold_col, int(segChannel), int(quantChannel), rt,
    )

    IJ.log("=== p63 Cellpose Batch complete ===")


run()
