#!/usr/bin/env bash
# tinyradio: play Tiny Radio in your terminal.
#
#   install:  curl -fsSL https://tinyradio.net/tinyradio -o /usr/local/bin/tinyradio && chmod +x /usr/local/bin/tinyradio
#   listen:   tinyradio
#   keys:     q quit    + / - volume (mpv only)    s schedule
#
# Needs one audio player: mpv (best), ffplay (comes with ffmpeg) or vlc.
# Mac: brew install mpv      Debian/Ubuntu: sudo apt install mpv
#
# tinyradio --check   prints the station status and exits (no audio).

set -u

STREAM="https://stream.tinyradio.net/radio.mp3"
SITE="https://www.tinyradio.net"
STATS_URL="$SITE/api/stats"
SCHEDULE_URL="$SITE/api/schedule"
TZ_STATION="Europe/London"
POLL=10

# ---------------------------------------------------------------- helpers
have() { command -v "$1" >/dev/null 2>&1; }
json() { python3 -c "import sys,json; d=json.load(sys.stdin); print($1)" 2>/dev/null; }

PLAYER=""
for p in mpv ffplay vlc cvlc; do have "$p" && { PLAYER=$p; break; }; done

station_time() { TZ=$TZ_STATION date "$@"; }

# seven-segment style digits, three rows high
digit_row() {  # $1 digit, $2 row (0..2)
  case "$1" in
    0) r=("▄▀▀▄" "█  █" "▀▄▄▀");; 1) r=("  ▄█" "   █" "   █");;
    2) r=("▀▀▀▄" "▄▀▀▀" "▀▀▀▀");; 3) r=("▀▀▀▄" " ▀▀█" "▀▀▀▀");;
    4) r=("█  █" "▀▀▀█" "   █");; 5) r=("▄▀▀▀" "▀▀▀▄" "▀▀▀▀");;
    6) r=("▄▀▀▀" "█▀▀▄" "▀▄▄▀");; 7) r=("▀▀▀█" "   █" "   █");;
    8) r=("▄▀▀▄" "█▀▀█" "▀▄▄▀");; 9) r=("▄▀▀▄" "▀▀▀█" "▄▄▄▀");;
    :) r=(" " "▀" "▄");; *) r=("    " "    " "    ");;
  esac
  printf '%s' "${r[$2]}"
}
big_clock() {  # $1 HH:MM
  for row in 0 1 2; do
    line=""
    for ((i=0; i<${#1}; i++)); do line+="$(digit_row "${1:$i:1}" $row) "; done
    printf '      \033[31m%s\033[0m\n' "$line"
  done
}

fetch_stats() {
  curl -fsS -m 6 "$STATS_URL" 2>/dev/null
}
fetch_schedule() {
  curl -fsS -m 6 "$SCHEDULE_URL" 2>/dev/null
}

# ---------------------------------------------------------------- player
MPV_SOCK="${TMPDIR:-/tmp}/tinyradio-$$.sock"
PLAYER_PID=""
VOLUME=80

start_player() {
  [ -n "$PLAYER_PID" ] && kill -0 "$PLAYER_PID" 2>/dev/null && return
  case "$PLAYER" in
    mpv)    mpv --no-video --really-quiet --volume="$VOLUME" --input-ipc-server="$MPV_SOCK" "$STREAM" >/dev/null 2>&1 & ;;
    ffplay) ffplay -nodisp -autoexit -loglevel quiet -volume "$VOLUME" "$STREAM" >/dev/null 2>&1 & ;;
    vlc|cvlc) "$PLAYER" -I dummy --quiet "$STREAM" >/dev/null 2>&1 & ;;
  esac
  PLAYER_PID=$!
}
stop_player() {
  if [ -n "$PLAYER_PID" ]; then kill "$PLAYER_PID" 2>/dev/null; wait "$PLAYER_PID" 2>/dev/null; PLAYER_PID=""; fi
  rm -f "$MPV_SOCK"
}
set_volume() {  # mpv only
  [ "$PLAYER" = mpv ] || return
  python3 - "$MPV_SOCK" "$VOLUME" <<'EOF' 2>/dev/null
import socket, sys, json
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM); s.settimeout(1)
s.connect(sys.argv[1]); s.send((json.dumps({"command": ["set_property", "volume", int(sys.argv[2])]}) + "\n").encode()); s.close()
EOF
}

CURSOR_HIDDEN=0
cleanup() { stop_player; [ "$CURSOR_HIDDEN" = 1 ] && printf '\033[?25h\n'; }
trap cleanup EXIT INT TERM

# ---------------------------------------------------------------- draw
draw() {  # $1 on_air (true/false), $2 title, $3 listeners, $4 show_schedule
  local now hhmm hour onair="$1" title="$2" listeners="$3"
  hhmm=$(station_time +%H:%M); hour=$(station_time +%-H)
  printf '\033[H\033[2J'
  printf '  \033[1mTINY RADIO\033[0m  \033[2mthe home of good times\033[0m\n\n'
  big_clock "$hhmm"
  if [ "$onair" = true ]; then
    printf '\n      \033[31m●\033[0m ON AIR   \033[2m● UK time\033[0m   listeners %s\n' "$listeners"
    printf '\n      \033[33m▶ %s\033[0m\n' "${title:-tuning...}"
  else
    local back; if [ "$hour" -lt "$ON_START" ]; then back="today"; else back="tomorrow"; fi
    printf '\n      \033[2m● ON AIR\033[0m   \033[31m● OFF AIR\033[0m   back %s at %02d:00 UK\n' "$back" "$ON_START"
    printf '\n      \033[2mthe set is off; this will start playing when the station comes on\033[0m\n'
  fi
  printf '\n      \033[2mP R O G R A M M E   daily %02d–%02d UK\033[0m\n' "$ON_START" "$ON_END"
  while IFS=$'\t' read -r s e name; do
    [ -z "$name" ] && continue
    if [ "$hour" -ge "$s" ] && [ "$hour" -lt "$e" ]; then mark="\033[33m▶\033[0m"; else mark=" "; fi
    printf "      $mark %02d–%02d  %s\n" "$s" "$e" "$name"
  done <<< "$BLOCKS"
  printf '\n      \033[2m[q] quit'
  if [ "$PLAYER" = mpv ]; then printf '   [-] [+] volume %s%%' "$VOLUME"; else printf '   volume: system (mpv adds -/+)'; fi
  printf '   player: %s\033[0m\n' "$PLAYER"
}

# ---------------------------------------------------------------- main
sched=$(fetch_schedule)
ON_START=$(printf '%s' "$sched" | json "d['on_air']['start']"); ON_START=${ON_START:-8}
ON_END=$(printf '%s' "$sched" | json "d['on_air']['end']"); ON_END=${ON_END:-16}
BLOCKS=$(printf '%s' "$sched" | json "'\n'.join(f\"{b['start']}\t{b['end']}\t{b['name']}\" for b in d['blocks'])")
[ -z "$BLOCKS" ] && BLOCKS=$'8\t12\tMorning\n12\t16\tAfternoon'

if [ "${1:-}" = "--check" ]; then
  stats=$(fetch_stats)
  echo "station: $(printf '%s' "$stats" | json "'ON AIR' if d.get('on_air') else 'OFF AIR'")  now: $(printf '%s' "$stats" | json "d.get('title') or '-'")  listeners: $(printf '%s' "$stats" | json "d.get('listeners')")  player: ${PLAYER:-none found}"
  exit 0
fi

if [ -z "$PLAYER" ]; then
  echo "tinyradio needs an audio player. Install one of: mpv (brew install mpv / apt install mpv), ffmpeg (for ffplay), or vlc." >&2
  exit 1
fi

printf '\033[?25l'; CURSOR_HIDDEN=1
last_poll=0; onair=false; title=""; listeners="-"
while :; do
  now=$(date +%s)
  if (( now - last_poll >= POLL )); then
    stats=$(fetch_stats)
    if [ -n "$stats" ]; then
      onair=$(printf '%s' "$stats" | json "'true' if d.get('on_air') else 'false'")
      title=$(printf '%s' "$stats" | json "d.get('title') or ''")
      listeners=$(printf '%s' "$stats" | json "d.get('listeners', '-')")
    fi
    last_poll=$now
    if [ "$onair" = true ]; then start_player; else stop_player; fi
  fi
  draw "$onair" "$title" "$listeners"
  if read -r -s -n 1 -t 1 key; then
    case "$key" in
      q|Q) exit 0 ;;
      +|=) VOLUME=$(( VOLUME + 5 > 100 ? 100 : VOLUME + 5 )); set_volume ;;
      -|_) VOLUME=$(( VOLUME - 5 < 0 ? 0 : VOLUME - 5 )); set_volume ;;
    esac
  fi
done
