#!/bin/sh
#---help---
# Usage:
#   $0 [options] <command> [<value>[%]]
#   $0 [options] (+|-)<value>%
#   $0 [options] [=]<value>%
#   $0 -h
#
# Adjust the sound or mic volume using 'pamixer' and show an on-screen
# notification using 'avizo-client'.
#
# Commands:
#   + | up           Increase the volume by <value> percents (defaults to 5).
#   - | down         Decrease the volume by <value> percents (defaults to 5).
#   = | set          Set volume to <value> percents.
#   x | mute         Set mute.
#   u | unmute       Unset mute.
#   % | toggle-mute  Switch between mute and unmute.
#
# Options:
#   -a               Use with mute, unmute or toggle-mute command to sync the
#                      mute state of all sinks or sources with <device-id> or
#                      the default device.
#   -b               Allow volume to go above 100% (boost).
#   -D <device-id>   Choose a different sink (output) or source (input) than
#                      the default (see pamixer --list-sinks and --list-sources).
#   -p               Choose the currently playing sink (or source) instead of
#                      the default. In case of multiple playing sinks/sources,
#                      it will choose the first one as reported by pactl/pamixer.
#   -g <gamma>       Increase/decrease using gamma correction (e.g. 2.2).
#   -m               Control the source (mic) instead of sink (output).
#   -M <monitornr>   Set monitor where notification will be shown.
#   -u               Unmute when changing the volume (+|-|=).
#   -d               Show dark theme friendly variant of the icon
#   -h               Show this message and exit.
#
# This script is part of the avizo package.
#---help---
# This script is POSIX Shell Command Language compliant.
set -eu

if ( set -o pipefail 2>/dev/null ); then
	set -o pipefail
fi

PROGNAME='volumectl'

help() {
	local tag='---help---'
	sed -n "/^#$tag/,/^#$tag/{/^#$tag/d; s/^# \\?//; s|\$0|$0|; p}" "$0"
}

die() {
	printf "$PROGNAME: %s\n" "$1" >&2
	exit 1
}

is_integer() {
	expr "$1" : '[0-9]\+$' >/dev/null
}

playing_dev_id() {
	if command -v pactl >/dev/null; then
		pactl list short ${1}s | grep RUNNING | grep -m1 -o '^[0-9]\+'
	else  # this works since pamixer 1.6
		pamixer --list-${1}s | grep '"Running"' | grep -m1 -o '^[0-9]\+'
	fi
}


all_flag=false
dev=
dev_type='sink'
use_playing=false
opts=
unmute_opt=
optind=1
dark=0
monitor=-1
while getopts ':adbD:pg:mM:uh' OPT; do
	case "$OPT" in
		a) all_flag=true;;
		b) opts="$opts --allow-boost";;
		D) dev=$OPTARG;;
		p) use_playing=true;;
		g) opts="$opts --gamma=$OPTARG";;
		m) dev_type='source';;
		M) monitor="$OPTARG";;
		u) unmute_opt='--unmute';;
		d) dark=1;;
		h) help; exit 0;;
		\?) case "$OPTARG" in
		    	[0-9]*) break;;
		    	*) die "unknown option: -$OPTARG";;
		    esac
		;;
	esac
	optind=$OPTIND  # hack to make -<value>% work correctly
done
shift $((optind -1))

if [ $# -lt 1 ] || [ $# -gt 2 ]; then
	die "invalid number of arguments (see '$0 -h')"
fi

case "$1" in
	[=+-][0-9]*%) cmd=${1%%[0-9]*}; value=${1#[=+-]};;
	[0-9]*%) cmd='='; value=$1;;
	*) cmd=$1; value=${2:-5};;
esac
value=${value%\%}

if ! is_integer "$value"; then
	die "invalid value: '$value'"
fi

# Note: 'raise' and 'lower' are only for backward compatibility with
#  the previous version of this script.
case "$cmd" in
	+ | up | raise) cmd_opt="--increase=$value";;
	- | down | lower) cmd_opt="--decrease=$value";;
	= | set) cmd_opt="--set-volume=$value";;
	x | mute) cmd_opt='--mute';;
	u | unmute) cmd_opt='--unmute';;
	% | toggle-mute) cmd_opt='--toggle-mute';;
	*) die "invalid command: $cmd (see '$0 -h')";;
esac
case "$cmd_opt" in
	*mute*) unmute_opt='';;
	*) all_flag=false;;
esac

dev_opt=
if [ "$dev_type" = 'source' ] && [ -z "$dev" ]; then
	dev_opt='--default-source'
elif [ "$dev" ]; then
	dev_opt="--$dev_type=$dev"
elif $use_playing && id=$(playing_dev_id "$dev_type"); then
	dev_opt="--$dev_type=$id"
fi


pamixer $dev_opt $opts $unmute_opt $cmd_opt

# Note: pamixer returns 1 when muted or volume is 0
muted=$(pamixer $dev_opt --get-mute || :)
volume=$(pamixer $dev_opt --get-volume || :)

if ! is_integer "$volume"; then
	die "pamixer returned invalid volume level: '$volume'"
fi

if $all_flag; then
	[ "$muted" = 'true' ] && mute_opt='--mute' || mute_opt='--unmute'

	for id in $(pamixer --list-${dev_type}s | grep -o '^[0-9]\+'); do
		pamixer --$dev_type=$id $mute_opt || :
	done
fi

case "$dev_type" in
	sink)
		if [ "$muted" = 'true' ]; then
			image='volume_muted'
		elif [ "$volume" -le 33 ]; then
			image='volume_low'
		elif [ "$volume" -le 66 ]; then
			image='volume_medium'
		else
			image='volume_high'
		fi
	;;
	source)
		if [ "$muted" = 'true' ]; then
			image='mic_muted'
		else
			image='mic_unmuted'
		fi
	;;
esac

if [ "$dark" -eq 1 ]; then
    image="${image}_dark"
fi

progress=$(echo "$volume" | awk '{ printf "%.2f", ($1 > 100 ? 1 : $1 / 100) }')
avizo-client --image-resource="$image" --progress="$progress" --monitor="$monitor"
