# rbterm shell integration (bash).
#
# Emits OSC 133 prompt marks so rbterm can paint gutter badges
# (green = exit 0, red = nonzero) next to every command in the
# scrollback. Source this once from ~/.bashrc:
#
#     source /path/to/rbterm-shell-integration.bash
#
# Idempotent: re-sourcing the file is safe.

# Skip non-interactive shells / non-tty stdout.
case $- in *i*) ;; *) return 0;; esac
[[ -t 1 ]] || return 0

# Emit OSC 133;D (with $? from the last command) and 133;A right
# before each prompt is drawn. PROMPT_COMMAND is bash's pre-prompt
# hook; we prepend so the marks land before the user's own prompt
# customisation runs.
_rbterm_precmd() {
    local ec=$?
    printf '\e]133;D;%d\e\\\e]133;A\e\\' "$ec"
}

# Emit OSC 133;C right after the user hits Enter (DEBUG trap fires
# before each command). Skip when the trap is firing for the
# PROMPT_COMMAND chain itself — we only want it for user commands.
#
# Also skip for wrapper commands (ssh/mosh/telnet/container exec)
# that hand the terminal to a child program with its own prompt
# loop — local 133;C would otherwise light the rbterm "command
# running" stripe for the whole remote session even when the user
# is sitting at the *remote* prompt doing nothing. If the remote
# shell has its own rbterm-shell-integration installed, its 133;C/D
# will mark commands accurately.
_rbterm_preexec() {
    [[ "$BASH_COMMAND" == "_rbterm_precmd"* ]] && return
    local cmd=${BASH_COMMAND%% *}
    cmd=${cmd##*/}
    case $cmd in
        ssh|mosh|telnet|docker|kubectl|podman|nsenter|lxc|incus) return ;;
    esac
    printf '\e]133;C\e\\'
}

# Wire up the hooks idempotently.
case ";${PROMPT_COMMAND};" in
    *";_rbterm_precmd;"*) ;;
    *) PROMPT_COMMAND="_rbterm_precmd${PROMPT_COMMAND:+;$PROMPT_COMMAND}" ;;
esac
trap '_rbterm_preexec' DEBUG

# Inline media: `cat photo.jpg` / `cat song.mp3` shows the image or a
# player card in the rbterm pane — locally or on a host you ssh'd into
# (source this file there too; rbterm exports LC_TERMINAL=rbterm, which
# ssh forwards). Text and pipes are untouched. RBTERM_CAT_PLAIN=1 turns
# it off. `function` keeps an existing `cat` alias from mangling this.
# >>> rbterm-inline-media (keep identical in rbterm-shell-integration.{bash,zsh})
_rbterm_inline_max=50000000
_rbterm_in_rbterm() {
    if [ "${LC_TERMINAL:-}" = rbterm ] || [ "${TERM_PROGRAM:-}" = rbterm ] ||
       [ "${RBTERM_INLINE:-}" = 1 ]; then
        return 0
    fi
    # Over ssh LC_TERMINAL only survives if sshd's AcceptEnv allows it
    # (Debian/Ubuntu: LC_*; RHEL/CentOS: a fixed list without it). So ask
    # the terminal itself — XTVERSION, answered as "rbterm(<ver>)" — once
    # per shell. Not inside tmux (tmux answers for itself; set
    # RBTERM_INLINE=1 there).
    case ${_rbterm_probed:-} in yes) return 0 ;; no) return 1 ;; esac
    _rbterm_probed=no
    [ -t 0 ] && [ -t 1 ] && [ -z "${TMUX:-}" ] || return 1
    _rb_stty=$(stty -g 2>/dev/null) || return 1
    stty -echo -icanon min 0 time 10 2>/dev/null
    printf '\033[>q'
    _rb_reply=$(dd bs=256 count=1 2>/dev/null)
    stty "$_rb_stty" 2>/dev/null
    case $_rb_reply in *rbterm*) _rbterm_probed=yes; return 0 ;; esac
    return 1
}
_rbterm_is_media() {
    if command -v file >/dev/null 2>&1; then
        case $(file -b --mime-type -- "$1" 2>/dev/null) in
            image/png|image/jpeg|image/gif|image/bmp|image/x-ms-bmp) return 0 ;;
            audio/mpeg|audio/mp3|audio/x-wav|audio/wav|audio/vnd.wave) return 0 ;;
            audio/ogg|audio/x-vorbis+ogg|application/ogg) return 0 ;;
        esac
        return 1
    fi
    case $1 in
        *.png|*.PNG|*.jpg|*.JPG|*.jpeg|*.JPEG|*.gif|*.GIF|*.bmp|*.BMP) return 0 ;;
        *.mp3|*.MP3|*.wav|*.WAV|*.ogg|*.OGG) return 0 ;;
    esac
    return 1
}
# Sent as iTerm2's multipart form: a header, 3 KB base64 chunks each in its
# own complete escape, then FileEnd. One escape per small write matters on
# Windows: the console host closes an unfinished escape at every write
# boundary, so a single File= escape split across printf/base64/printf
# arrived with an empty body. Chunks also suit tmux passthrough.
_rbterm_send_file() {
    _rb_size=$(wc -c < "$1" | tr -d ' ')
    if [ "$_rb_size" -gt "$_rbterm_inline_max" ]; then
        printf 'rbterm: %s is too large to show inline (%s bytes, limit %s)\n' \
            "$1" "$_rb_size" "$_rbterm_inline_max" >&2
        return 1
    fi
    _rb_name=$(printf '%s' "${1##*/}" | base64 | tr -d '\n')
    if [ -n "${TMUX:-}" ]; then
        _rb_pre='\033Ptmux;\033\033]'; _rb_post='\a\033\\'
    else
        _rb_pre='\033]'; _rb_post='\a'
    fi
    printf "${_rb_pre}1337;MultipartFile=name=%s;size=%s;inline=1${_rb_post}" "$_rb_name" "$_rb_size"
    # awk, not a shell read loop (~40x faster); fflush per chunk keeps
    # each escape in its own write. Delimiters go via ENVIRON so awk
    # doesn't reinterpret their backslashes.
    base64 < "$1" | tr -d '\n' | fold -w 3000 |
        _RB_P=$(printf "$_rb_pre") _RB_Q=$(printf "$_rb_post") awk \
            '{ printf "%s1337;FilePart=%s%s", ENVIRON["_RB_P"], $0, ENVIRON["_RB_Q"]; fflush() }'
    printf "${_rb_pre}1337;FileEnd${_rb_post}"
}
# The wrapper: media files go to the pane inline, anything else — text,
# pipes (`cat x | grep`), options, stdin — goes through the real cat. The
# terminal is only probed when a media file is actually involved.
_rbterm_cat() {
    if [ -t 1 ] && [ $# -gt 0 ] && [ -z "${RBTERM_CAT_PLAIN:-}" ]; then
        _rb_any=
        for _rb_a in "$@"; do
            case $_rb_a in -*) command cat "$@"; return ;; esac
            [ -f "$_rb_a" ] || { command cat "$@"; return; }
            _rbterm_is_media "$_rb_a" && _rb_any=1
        done
        if [ -n "$_rb_any" ] && _rbterm_in_rbterm; then
            _rb_rc=0
            for _rb_a in "$@"; do
                if _rbterm_is_media "$_rb_a"; then
                    _rbterm_send_file "$_rb_a" || _rb_rc=1
                else
                    command cat -- "$_rb_a" || _rb_rc=1
                fi
            done
            return $_rb_rc
        fi
    fi
    command cat "$@"
}
# <<< rbterm-inline-media
function cat { _rbterm_cat "$@"; }
