#!/bin/bash
#
# headless_pianobar
#
# Copyright (c) 2012 Daniel Thau
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
### Description/instructions:
#
# This script to allow pianobar to run headlessly as well as re-connect to
# pianobar's UI.
#
# When this script is launched, if pianobar isn't running, it will launch
# pianobar (in the background) and then immediately connect to pianobar's UI to
# allow the user to do things such as log in.  If pianobar is running, this
# script will simply connect to pianobar's UI.
#
# If you close this script with ctrl-c or by closing the terminal, pianobar
# should continue running in the background.
#
# To close pianobar, press the key mapped to close pianobar ('q' by default).
# This script should detect that pianobar died and close as well.
#
### Known issues/limitations:
#
# When the user is at a text input field - such as login or naming a station -
# mappings to quit pianobar ('q' by default) will not quit pianobar.  Moreover,
# techniques such as ctrl-c or closing the terminal will not quit pianobar
# either, as they only quit this script and leave pianobar running headlessly.
# To quit pianobar from this situation, one must either complete the text entry
# and get pianobar to a state where it will accept the quit mapping, or one
# must kill pianobar from outside of this script through commands such as
# `kill`.
#
# This script was initially intended to be bourne-shell compatible and
# portable, however one major limitation was found: the bourne shell does not
# seem to have a good way to read a single character at a time such as can be
# done with bash's "-n" flag.  Thus this script is dependent on less portable
# techniques and currently requires bash.


# This script regularly checks whether or not pianobar is running in the
# background in order to detect when it closes.  This variable sets how long of
# a delay there will be between checks (in seconds).
CHECK_PERIOD="1"

# This variable will set how many lines of pianobar's output to print when
# re-connecting to pianobar.
OUTPUT_LINES="30"

# Ensure the ctrl fifo exists.
if [ ! -p $HOME/.config/pianobar/ctl ]
then
	echo "pianobar ctl fifo not present at $HOME/.config/pianobar/ctl"
	echo -n "Create? (y/N) "
	read YN
	if [ "$YN" = "y" ] || [ "$YN" = "yes" ] || [ "$YN" = "Y" ] || [ "$YN" = "YES" ]
	then
		mkdir -p $HOME/.config/pianobar/
		mkfifo  $HOME/.config/pianobar/ctl
		if [ ! -p $HOME/.config/pianobar/ctl ]
		then
			echo "Failed to create fifo, aborting"
			exit 1
		fi
	else
		exit 1
	fi
fi

# Launch pianobar if it is not already running.
if ! ps -u $(id -u) -o comm | grep -q "^pianobar$"
then
	echo "pianobar not running, launching pianobar"
	nohup pianobar &>$HOME/.config/pianobar/out &disown
	sleep 1
fi

# Sanity check: ensure pianobar's output can be read.
if [ ! -f $HOME/.config/pianobar/out ]
then
	echo "pianobar does not seem to be outputting to $HOME/.config/pianobar/out, try killing it and starting $0 again"
	exit 2
fi

# Function to cleanly quit.  Ensures that the two backgrounded processes (the
# output, tail, and the check, running()) both exit along with the parent (this
# script itself)
quit(){
	# tail and running() might both die when the parent dies as there was no
	# nohup, but double-check just to make sure.  Don't want to leave a mess
	# behind.
	if [ ! -z $TAILPID ]
	then
		kill $TAILPID 2>/dev/null
	fi
	if [ ! -z $RUNNINGPID ]
	then
		kill $RUNNINGPID 2>/dev/null
	fi
	trap - HUP INT TERM
	kill $$
	exit 0
}

# Check on a regular basis that pianobar is still running.
# If pianobar stops running, stop this script as well.
running(){
	while ps -u $(id -u) -o comm | grep -q "^pianobar$"
	do
		sleep $CHECK_PERIOD
	done
	echo "pianobar died, quitting"
	quit
}

# Ensure quit() is called to clean up when exiting
trap quit HUP INT TERM

# Print pianobar's output.
tail -n$OUTPUT_LINES -f $HOME/.config/pianobar/out &
TAILPID=$!

# Run running() in the background to detect when pianobar closes.
running &
RUNNINGPID=$!

# Get input from user, character by character, and feed it to pianobar's ctl
# fifo.  Note that no newline character is given with `read`.  Rather, one
# simply gets an empty variable back.  Detect this situation and pass a newline
# along to pianobar.  Otherwise, send the character read from input to
# pianobar.
IFS=""
while /bin/true
do
	read -n1 -s INPUT
	if [ "$INPUT" == "" ]
	then
		echo "" > $HOME/.config/pianobar/ctl
	else
		echo -n $INPUT > $HOME/.config/pianobar/ctl
	fi
done