#!/usr/bin/bash

# heos-menu  Copyright 2025-2026  Norman Carver

# Script program to loop on a menu of HEOS actions for a HEOS player/group.
#
# A player is specified with:
# (1) a (sub)string of the player's HEOS name, or
# (2) its IP num (i.e., host portion of its dotted quad IP address).
#
# With the -g option, a group is specified:
# (1) a (sub)string of the group's HEOS name
#
# Sends commands to the default HEOS master device, but can select alternative
# using the MASTER_IPNUM argument option.


function print_usage()
{
    echo -e "Menu-based control of a HEOS Player/Group.\n" >&2
    echo    "Usage: heos-menu [-g] PLAYER [MASTER_IPNUM]" >&2
    echo    "where:" >&2
    echo    "  PLAYER -- a HEOS Player's Name (substring) or its IPNUM;" >&2
    echo    "  MASTER_IPNUM -- use a non-default HEOS master device;" >&2
    echo -e "  (IPNUM means the last quad of an IP address only: i.e., 0--255.)\n" >&2
    echo    "Options:" >&2
    echo    "  -g -- group: PLAYER is a group Name;" >&2
}


if [[ "$1" == --help ]]; then
    print_usage
    exit 0
fi

# Process supplied options:
pgopt=--player
groupopt=""
debugopt=""
while [[ "$1" == -* ]]; do
    if [[ "$1" == -g ]]; then
        pgopt=--group
        groupopt="-g"
    elif [[ "$1" == -- ]]; then
        shift;break
    elif [[ "$1" == --debug ]]; then
        debugopt=--debug
    else
        echo "Error: invalid option '$1'" >&2
        echo "(use '--' to signal end of options, with -* arguments)" >&2
        exit 1
    fi
    shift
done

# Process command arguments:
if [[ $# == 0 || $# -gt 2 ]]; then
    print_usage
    exit 1
fi

player=$1
if [[ $# == 2 ]]; then
    master=${2}
else
    master=""
fi
heosdir=$(dirname "$0")


# heosutil-run-command error checking function (one json output):
# args: 1) command result variable; 2) error message; [3) error action]
# (default action on error is 'exit 1', use $3 of 'return 1' to continue execution)
function error_check_run_command()
{
    cmdresult=${!1}
    if [[ ($? != 0) || ("$cmdresult" != *'"result": "success"'*) ]]; then
        if [[ "$cmdresult" == Error:* || ($debugopt == --debug && "$cmdresult" == ERROR:*) ]]; then
            echo "$cmdresult" >&2
        fi
        if [[ -n "$2" ]]; then echo "$2" >&2; fi
        if [[ $# == 3 ]]; then $3; else exit 1; fi
    else
        return 0
    fi
}


# Validate player:
result=$("$heosdir"/heosutils/heosutil-run-command ${pgopt}="$player" "heos://player/get_play_state?pid=\${pid}" $master)
error_check_run_command result ""  #error message comes from heosutil-run-command

# Harvest PID from result and change player to PID:
pid=$(grep -Eo 'pid=-?[0-9]+' <<<"$result")
pidopt=--"$pid"

# Determine width to use for select and set up:
# (Bash does single column with wide screens, want 4x3 menu instead which 60 width will cause.)
width=$(tput cols)
if [[ "$width" -gt 60 ]]; then
    width=60
fi
COLUMNS=$width  #COLUMNS is what select uses to determine menu display width

# Execute menu for action options:
options=(Play Pause Next Prev Volume "Mute Toggle" "Now Playing" "Status" "Player Queue" "Favorites" "Playlists" "Local Server")
PS3='Enter choice (^-d to Quit): '
select opt in "${options[@]}"; do

    case "$opt" in
        Play)
            "$heosdir"/heos-play $groupopt $pidopt "$player" $master
            if [[ $? == 0 ]]; then echo "Playing..."; fi ;;

        Pause)
            "$heosdir"/heos-pause $groupopt $pidopt "$player" $master
            if [[ $? == 0 ]]; then echo "Paused!"; fi ;;

        Next)
            "$heosdir"/heos-next $groupopt $pidopt "$player" $master ;;

        Prev)
            "$heosdir"/heos-prev $groupopt $pidopt "$player" $master ;;

        Volume)
            read -p "Enter volume level or Return for current: " vol
            if [[ -n "$vol" ]]; then
                "$heosdir"/heos-volume $groupopt $pidopt "$player" "$vol" $master
            else
                "$heosdir"/heos-volume $groupopt $pidopt "$player" ? $master
            fi ;;

        "Mute Toggle")
            "$heosdir"/heos-mute $groupopt $pidopt "$player" tog $master ;;

        "Now Playing")
            "$heosdir"/heos-nowplaying $groupopt $pidopt "$player" $master ;;

        "Status")
            "$heosdir"/heos-status $groupopt $pidopt "$player" $master ;;

        "Player Queue")
            read -p  "Enter Queue item (>=1) or Return to list: " item
            if [[ -n "$item" ]]; then
                "$heosdir"/heos-qplay $groupopt -v $pidopt "$player" "$item" $master
            else
                "$heosdir"/heos-qlist $groupopt $pidopt "$player" $master
            fi ;;

        "Favorites")
            read -p "Enter Favorites item (>=1) or Return to list: " item
            if [[ -n "$item" ]]; then
                "$heosdir"/heos-favplay $groupopt -v $pidopt "$player" "$item" $master
            else
                "$heosdir"/heos-favlist $master
            fi ;;

        "Playlists")
            read -p "Enter Playlists item (>=1) or Return to list: " item
            if [[ -n "$item" ]]; then
                "$heosdir"/heos-plplay $groupopt -v $pidopt "$player" "$item" $master
            else
                "$heosdir"/heos-pllist $master
            fi ;;

        "Local Server")
            read -p "Enter Server artist search substring: " artiststr
            if [[ -n "$artiststr" ]]; then
                "$heosdir"/heos-servplay $groupopt $pidopt "$player" "$artiststr" $master
            fi ;;
        *) echo "Error: invalid option '$REPLY'" ;;
    esac

    COLUMNS=$width  #COLUMNS reset by actions if any external commands, so reset
done

#EOF
