#!/bin/sh
# Copyright (c) 2026 Jiaxing Hu <gahing@gahingwoo.com>
# SPDX-License-Identifier: GPL-2.0
#
# charsiu: the front door.
#
# ONE RULE DECIDES WHO SEES WHAT, AND THERE IS NO FLAG FOR IT:
#
#     charsiu, nothing set up yet -> the guided screen
#     charsiu, already set up      -> a conversation
#     charsiu <anything else>      -> the command, and never a dialog
#
# Somebody who bought this board because it looked interesting types `charsiu`
# and is led through it. Somebody who lives in a terminal types `charsiu pull x`
# and never meets a dialog at all. Neither has to know the other mode exists.
#
# THE CLI SHAPE IS NOT INVENTED HERE. It follows ollama, whose users already
# know `run / pull / list / ls / rm / show / ps / serve`, and claude code, where
# a bare command is interactive, a quoted argument is one-shot, and -p prints
# and accepts a pipe. Copying a convention people already have is most of what
# "easy to use" means.
set -eu

VERSION=0.1
SELF=$(basename "$0")
CHARSIU_HERE=$(dirname "$0")
for _l in ${CHARSIU_LIB:+"$CHARSIU_LIB/charsiu-lib.sh"} \
	  "$CHARSIU_HERE/charsiu-lib.sh" \
	  "$CHARSIU_HERE/../scripts/charsiu-lib.sh" \
	  "$CHARSIU_HERE/../../opt/charsiu/charsiu-lib.sh" \
	  /opt/charsiu/charsiu-lib.sh; do
	[ -r "$_l" ] && { . "$_l"; break; }
done
command -v ini_get >/dev/null 2>&1 \
	|| { echo "$(basename "$0"): charsiu-lib.sh not found" >&2; exit 1; }

for t in ${CHARSIU_LIB:+"$CHARSIU_LIB/charsiu-tui.sh"} \
	 "$CHARSIU_HERE/charsiu-tui.sh" "$CHARSIU_HERE/../scripts/charsiu-tui.sh" \
	 "$CHARSIU_HERE/../../opt/charsiu/charsiu-tui.sh" /opt/charsiu/charsiu-tui.sh; do
	[ -r "$t" ] && { . "$t"; break; }
done
command -v ui_menu >/dev/null 2>&1 || { echo "$SELF: charsiu-tui.sh not found" >&2; exit 1; }
CTUI_TITLE="charsiu"

# ---------------------------------------------------------------------------
# WHERE THINGS LIVE
#
# USER FIRST, SYSTEM AS A FALLBACK. The old layout put the config in
# /etc/charsiu and the models in /opt/charsiu/models, so a plain user could
# neither configure nor download without root. ollama, codex and claude code all
# keep per-user state under $HOME and so does this. The system paths are still
# read, because the board image ships models there.
# ---------------------------------------------------------------------------
CONF=$(charsiu_conf)
MODELS="${CHARSIU_MODELS:-$CHARSIU_HOME/models}"
SYSMODELS=$CHARSIU_SYS_MODELS


have_model() { [ -n "$(all_models | head -1)" ]; }
configured() { m=$(ini_get model path); [ -n "$m" ] && [ -f "$m" ]; }

usage() {
cat <<USAGE
charsiu $VERSION, an open LLM runtime for the RK3576 NPU

  $SELF                       a conversation, or the guided setup on a new board
  $SELF "what is a bandgap"   ask once and exit
  $SELF -p "..."              print mode; reads a pipe:  cat f | $SELF -p "sum up"
  $SELF --image FILE "..."    ask about a picture; needs a model that sees,
                              which "$SELF pull" marks as SEES PICTURES

  $SELF run MODEL [PROMPT]    use a particular model
  $SELF pull [NAME]           fetch one; no NAME opens the picker
  $SELF list                  what is here                        (alias: ls)
  $SELF rm NAME               delete one
  $SELF show [MODEL]          what it is, and whether it can run here
  $SELF ps                    is anything running
  $SELF bench                 tokens per second on this board
  $SELF doctor [--paste]      what works and what does not
  $SELF serve [--port N]      an OpenAI and Anthropic compatible endpoint
  $SELF update [dev|stable]   fetch the latest charsiu and reinstall it
                              dev adds the hardware probes and tracks the
                              branch the work happens on
  $SELF update --auto         the same with no questions and no demo (-y);
                              auto = yes under [update] in config.ini keeps it
  $SELF setup                 the guided screen, on purpose

  Two things here are not conversations, so they are their own commands:

  charsiu_whisper MODEL --transcribe --audio clip.wav    a clip, in words
  charsiu_clip MODEL --image FILE --text "a cat" "a dog" which words fit a picture
  charsiu_vision MMPROJ                                  what a vision tower is

  models  $MODELS  (and $SYSMODELS)
  config  $CONF
USAGE
}

case "${1:-}" in
-h|--help|help)  usage; exit 0 ;;
-V|--version)    echo "charsiu $VERSION"; exit 0 ;;
setup)           _b=$(need_bin charsiu-config) || exit; exec "$_b" --setup ;;
update)          shift; _b=$(need_bin charsiu-update) || exit; exec "$_b" "$@" ;;
doctor)          shift; _b=$(need_bin charsiu-doctor) || exit; exec "$_b" "$@" ;;
pull)            shift
                 # ${1:+"$@"} ${1:-x} repeats the first argument. Set the
                 # default by replacing the list instead.
                 [ $# -gt 0 ] || set -- --wizard
                 _b=$(need_bin charsiu-get) || exit; exec env CHARSIU_MODELS_DIR="$MODELS" "$_b" "$@" ;;
list|ls)         shift; _b=$(need_bin charsiu-list) || exit; exec "$_b" "$@" ;;
rm)              shift; _b=$(need_bin charsiu-rm) || exit; exec "$_b" "$@" ;;
show)            shift; _b=$(need_bin charsiu-show) || exit; exec "$_b" "$@" ;;
ps)              shift; _b=$(need_bin charsiu-ps) || exit; exec "$_b" "$@" ;;
bench)           shift; _b=$(need_bin charsiu-runner) || exit; exec "$_b" bench "$@" ;;
serve)           shift; _b=$(need_bin charsiu-serve) || exit; exec "$_b" "$@" ;;
run)             shift
                 [ $# -ge 1 ] || { echo "$SELF run: which model?" >&2; exit 2; }
                 M="$1"; shift
                 _b=$(need_bin charsiu-runner) || exit; exec env CHARSIU_MODEL="$M" "$_b" "$@" ;;
esac

# ---------------------------------------------------------------------------
# No subcommand. Decide between the guided screen and a conversation.
# ---------------------------------------------------------------------------
if [ $# -eq 0 ] && { ! configured || ! have_model; }; then
	# Not an error and not a lecture. A board with nothing on it should walk
	# somebody through it, once, and never mention it again.
	_b=$(need_bin charsiu-config) || exit; exec "$_b" --setup
fi

_b=$(need_bin charsiu-runner) || exit; exec "$_b" "$@"
