#!/usr/bin/env bash
# Desk OCR / capture API · full visual assist · error windows included
# Usage:
#   desk-ocr                  # full screen → OCR text
#   desk-ocr screen           # same
#   desk-ocr window CLAWS     # window name substring
#   desk-ocr window-id 0x…    # X window id
#   desk-ocr file path.png    # OCR existing image
#   desk-ocr claws            # claws-mail window if open
# Outputs: out/desk-ocr-*.{png,txt,json}
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
OUT="$ROOT/out"
mkdir -p "$OUT"
export DISPLAY="${DISPLAY:-:0}"
TS=$(date +%Y%m%d-%H%M%S)
PNG="$OUT/desk-ocr-$TS.png"
TXT="$OUT/desk-ocr-$TS.txt"
JSON="$OUT/desk-ocr-latest.json"
LATEST_PNG="$OUT/desk-ocr-latest.png"
LATEST_TXT="$OUT/desk-ocr-latest.txt"

mode="${1:-screen}"
arg="${2:-}"

capture_root() {
  if command -v scrot >/dev/null 2>&1; then
    scrot -o "$PNG" 2>/dev/null && return 0
  fi
  # xwd root → png via python/PIL
  local xwd=/tmp/desk-ocr-$$.xwd
  xwd -root -silent -out "$xwd" 2>/dev/null || return 1
  python3 - "$xwd" "$PNG" <<'PY'
import sys
from pathlib import Path
try:
    from PIL import Image
except ImportError:
    sys.exit(2)
# xwd via pillow may need convert; try ImageMagick
import subprocess
src, dst = sys.argv[1], sys.argv[2]
r = subprocess.run(["convert", src, dst], capture_output=True)
if r.returncode != 0:
    # pure python xwd is painful; try gdk
    try:
        import gi
        gi.require_version("Gdk", "3.0")
        from gi.repository import Gdk
        w = Gdk.get_default_root_window()
        h, ww = w.get_height(), w.get_width()
        pb = Gdk.pixbuf_get_from_window(w, 0, 0, ww, h)
        if pb:
            pb.savev(dst, "png", [], [])
            raise SystemExit(0)
    except Exception:
        pass
    sys.exit(1)
PY
}

capture_window_id() {
  local wid=$1
  if command -v scrot >/dev/null 2>&1 && command -v xdotool >/dev/null 2>&1; then
    # scrot -u needs focus; use import -window if available
    :
  fi
  if command -v import >/dev/null 2>&1; then
    import -window "$wid" "$PNG" 2>/dev/null && return 0
  fi
  local xwd=/tmp/desk-ocr-$$.xwd
  xwd -id "$wid" -silent -out "$xwd" 2>/dev/null || return 1
  if command -v convert >/dev/null 2>&1; then
    convert "$xwd" "$PNG" && return 0
  fi
  python3 - <<PY
import gi, sys
gi.require_version("Gdk", "3.0")
from gi.repository import Gdk, GdkX11
# fallback full screen
w = Gdk.get_default_root_window()
pb = Gdk.pixbuf_get_from_window(w, 0, 0, w.get_width(), w.get_height())
if not pb: sys.exit(1)
pb.savev("$PNG", "png", [], [])
PY
}

find_window() {
  local needle=$1
  # xwininfo -tree -root is heavy; use wmctrl if present
  if command -v wmctrl >/dev/null 2>&1; then
    wmctrl -l | awk -v n="$(echo "$needle" | tr '[:upper:]' '[:lower:]')" 'tolower($0) ~ n {print $1; exit}'
    return 0
  fi
  # xdotool
  if command -v xdotool >/dev/null 2>&1; then
    xdotool search --name "$needle" 2>/dev/null | head -1
    return 0
  fi
  # python Xlib-free: parse xwininfo
  xwininfo -root -tree 2>/dev/null | awk -v n="$(echo "$needle" | tr '[:upper:]' '[:lower:]')" '
    tolower($0) ~ n && /\(has no name\)/ {next}
    tolower($0) ~ n {
      if (match($0, /0x[0-9a-fA-F]+/)) { print substr($0, RSTART, RLENGTH); exit }
    }'
}

do_ocr() {
  if ! command -v tesseract >/dev/null 2>&1; then
    echo "WARN tesseract missing · image only: $PNG" >&2
    echo "(no OCR binary)" >"$TXT"
    return 1
  fi
  tesseract "$PNG" "${TXT%.txt}" -l eng --psm 6 2>/dev/null || \
    tesseract "$PNG" "${TXT%.txt}" -l eng 2>/dev/null || true
  [[ -f "$TXT" ]]
}

case "$mode" in
  screen|root|full|"")
    capture_root || { echo "capture failed" >&2; exit 1; }
    ;;
  claws|mail)
    wid=$(find_window "Claws" || true)
    [[ -z "${wid:-}" ]] && wid=$(find_window "claws" || true)
    if [[ -n "${wid:-}" ]]; then
      capture_window_id "$wid" || capture_root
    else
      echo "WARN claws window not found · full screen" >&2
      capture_root
    fi
    ;;
  window)
    [[ -n "$arg" ]] || { echo "usage: desk-ocr window NAME" >&2; exit 2; }
    wid=$(find_window "$arg" || true)
    if [[ -n "${wid:-}" ]]; then
      capture_window_id "$wid" || capture_root
    else
      echo "WARN window '$arg' not found · full screen" >&2
      capture_root
    fi
    ;;
  window-id|id)
    capture_window_id "$arg"
    ;;
  file)
    [[ -f "$arg" ]] || { echo "no file $arg" >&2; exit 2; }
    cp -f "$arg" "$PNG"
    ;;
  camera|cam|usb)
    # USB camera frame → OCR (Hostess vision)
    dev="${arg:-/dev/video0}"
    [[ -e "$dev" ]] || dev=/dev/video0
    if command -v ffmpeg >/dev/null 2>&1 && [[ -e "$dev" ]]; then
      timeout 10 ffmpeg -y -f v4l2 -video_size 640x480 -i "$dev" -frames:v 1 -q:v 3 "$PNG" >/dev/null 2>&1 \
        || capture_root
    else
      echo "WARN camera grab unavailable · full screen" >&2
      capture_root
    fi
    ;;
  *)
    echo "usage: desk-ocr [screen|claws|window NAME|file PATH|camera]" >&2
    exit 2
    ;;
esac

do_ocr || true
cp -f "$PNG" "$LATEST_PNG"
[[ -f "$TXT" ]] && cp -f "$TXT" "$LATEST_TXT" || true

python3 - <<PY
import json, time
from pathlib import Path
png, txt = Path("$PNG"), Path("$TXT")
text = txt.read_text(errors="replace") if txt.is_file() else ""
obj = {
  "ts": time.strftime("%Y-%m-%dT%H:%M:%S%z"),
  "mode": "$mode",
  "png": str(png),
  "txt": str(txt) if txt.is_file() else None,
  "latest_png": "$LATEST_PNG",
  "latest_txt": "$LATEST_TXT",
  "chars": len(text),
  "lines": text.count("\n")+1 if text else 0,
  "preview": text[:2000],
  "api": "Build/desk-ocr.sh · visual assist · error windows included",
}
Path("$JSON").write_text(json.dumps(obj, indent=2)+"\n")
print(json.dumps({"ok": True, "png": obj["png"], "chars": obj["chars"], "preview": obj["preview"][:500]}, indent=2))
PY

# also echo full text for agent consumption
if [[ -f "$TXT" ]]; then
  echo "----- OCR TEXT -----"
  cat "$TXT"
  echo "----- END OCR -----"
fi
