#!/bin/bash
# This script will install pianod2 r232 or later on a fresh Raspian
# Jessie image, using gstreamer as the media engine.
# Prior to this script, use raspi-config to expand the filesystem
# (4GB won't be enough, but 8 seems fine) and reboot.
#
# This is a very unforgiving and fragile script.  It may overwrite
# existing data or configurations.  The uninstall option may remove
# files without warning.  Use with care.

arg0=$(basename "$0")
install_failures=""

function get_core_count {
	typeset TAB="	"
	typeset count=$(grep -c "^processor$TAB:" /proc/cpuinfo)
	[ $count -eq 0 ] && count=1
	echo $count
}

function adjust_packages {
	if $UNINSTALL
	then
		apt-get -y remove "$@"
	else
		apt-get -y install "$@"
	fi
	status=$?
	install_failures="$install_failures $*"
}

function add_remove_compiled {
	typeset project="$1" url="$2" version file
	shift 2
	file=$(basename "$url")

	# raspbian tar's "z" option doesn't understand .xz format.
	typeset uncompress
	case "$file" in
		*.gz)	uncompress="gunzip -c" ;;
		*.xz)	uncompress="unxz -c" ;;
		*.bz2)	uncompress="bunzip2 -c" ;;
		*.lzma)	uncompress="unlzma -c" ;;
		*.tar)	uncompress="cat" ;;
		*)
			echo "Unknown compression format: $file"
			exit 1 ;;
	esac

	if $UNINSTALL
	then
		echo "Uninstalling $project"
		version=$($uncompress $TARGETHOME/"$file" | tar t | head -1) || return 0
		cd $TARGETHOME/$version || return 0
		make uninstall
		return
	fi
# Run the download and compilation as an ordinary user, not as root
	echo "Downloading and building $project"
	typeset core_count=$(get_core_count)
	create_file $TARGETHOME/install_$project $TARGET.pi 744 << EOF
		mkdir Music bin
		wget -O "$file" "$url" || exit 1
		version=\$($uncompress "$file" | tar t | head -1) || exit 1
		$uncompress "$file" | tar x || exit 1
		cd "\$version" || exit 1
		./configure $@ || exit 1
		make -j$core_count
EOF

	if su - $TARGET -c $TARGETHOME/install_$project
	then
		echo "Installing $project"
		version=$($uncompress $TARGETHOME/"$file" | tar t | head -1) || exit 1
		cd $TARGETHOME/"$version" || exit 1
		make install
	fi
}

function add_remove_packages {
	typeset status=0
	# Audio engine & output
	adjust_packages \
		'libgstreamer1.0-.*' \
		'gstreamer1.0-alsa.*' \
		'gstreamer1.0-plugins-base.*' \
		'gstreamer1.0-plugins-good.*' \
		'gstreamer1.0-plugins-ugly.*' \
		'libgstreamer-plugins-base1.0.*' \
		'gstreamer1.0-nice' || status=$?

	# Secure connections & Pandora
	if false
	then
		adjust_packages \
			'libssl-dev' \
			'libcurl4-openssl-dev' \
			'libgcrypt[23].*' || status=$?
	else
		adjust_packages \
			'libgnutls-deb0.*' \
			'libgnutls.*-dbg' \
			'libgnutls.*-dev' \
			'libcurl4-gnutls-dev' \
			'libgcrypt[23].*' || status=$?
	fi
	# at(1), needed for runmix
	adjust_packages \
		'at' || status=$?

	# Pandora support
	adjust_packages libjson-c.* || status=$?
	# Filesystem support
	adjust_packages 'libtag1c.*' 'libtag1-d.*' || status=$?

	# pianod itself
	add_remove_compiled pianod "http://deviousfish.com/Downloads/pianod2/${DEVELOPMENT}pianod2-latest.tar.gz" --enable-debug --with-pandora --with-filesystem --with-tonegenerator --with-compression || status=$?
	return $status
}


function create_file {
	typeset filename="$1" owner="$2" mode="$3"
	typeset user=$(echo "$owner" | cut -d. -f1)
	typeset group=$(echo "$owner" | cut -d. -f1 -s)
	cat > "$filename"
	chown $owner "$filename"
	[ "$group" != "" ] && chgrp $group "$filename"
	chmod $mode "$filename"
}


function usage {
	echo "$arg0: Usage: $arg0 [-d] [-u] [-n username]"
	echo "  -u : Uninstall and delete pianod user"
	echo "  -d : Use the latest development instead of stable release."
	echo "  -n : Specify name of target install user (may be new user)"
	echo "This is an unforgiving and fragile script.  Use with care."
	exit 1
}


SKIPUPDATE=false
UNINSTALL=false
DEVELOPMENT=""
TARGET=$(id -un)
rootforced=false

while getopts "udn:s" option
do
	case "$option" in
		s)	# Secret option:
			# Out-of-date package lists causes problems, but
			# updating repeatedly during testing is bad.
			SKIPUPDATE=true ;;
		u)
			UNINSTALL=true ;;
		d)
			DEVELOPMENT="Devel/" ;;
		n)
			rootforced=true
			TARGET="$OPTARG" ;;
		?)
			usage ;;
	esac
done

# Check for root privileges
uid=$(id -u)
if [ $uid -ne 0 ]
then
	echo "$arg0: Reinvoking self as root."
	echo sudo "$0" "-n$TARGET" "$@"
	exec sudo "$0" "-n$TARGET" "$@"
fi

if [ "$TARGET" = "root" -a "$rootforced" = "false" ]
then
	echo "$arg0: Invoked as root or with sudo."
	echo "Use $arg0 without sudo, as 'pi' or another user the pianod daemon will run as."
	echo "If you really intend to run as root, use $0 -nroot.  This increases security risks."
	usage
fi

# Shift away any processed options
shift $((OPTIND - 1))
TARGETHOME="$(eval echo "~$TARGET")"

if $UNINSTALL
then
	UNINSTALL=true
	systemctl stop mixerprompt
	systemctl disable mixerprompt
	systemctl stop pianod
	systemctl disable pianod
	mv /etc/lightdm/lightdm.conf.pianod /etc/lightdm/lightdm.conf
	add_remove_packages
	rm -f /usr/local/bin/gzstream.h
	rm -f /etc/init.d/pianod
	rm -f /etc/systemd/system/mixerprompt.service
	rm -f /etc/systemd/system/pianod.service
	apt-get autoremove
	if [ "$TARGET" = "$(id -un)" ]
	then
		echo "Not deleting current user."
		mv "$TARGETHOME/.bashrc.pianod" "$TARGETHOME/.bashrc"
		rm "$TARGETHOME/.kshrc"
		rm "$TARGETHOME/pianod2-latest.tar.gz"
		rm -rf $TARGETHOME/pianod2-[0-9][0-9][0-9]
	else
		deluser --remove-home pianod
	fi
	exit 1
fi

if [ $# -ne 0 ]
then
	usage
fi

# Update the package list or installations may fail
$SKIPUPDATE || apt-get update

apt-get install 'git' || exit 1
adjust_packages 'ksh'

# Check if pianod user exists already
if id "$TARGET" >/dev/null 2>&1
then
	echo "Using existing $TARGET user."
else
	adduser --shell /bin/bash --ingroup pi --gecos "pianod user" $TARGET || exit 1
	usermod --append --groups audio,crontab $TARGET || exit 1
fi
TARGETHOME="$(eval echo "~$TARGET")"

# Install gzstream header-only version
if [ ! -f /usr/local/bin/gzstream.h ]
then
	git clone https://gist.github.com/1508048.git || exit 1
	mv 1508048/gzstream.h /usr/local/include || exit 1
	rm -rf 1508048
fi

if ! add_remove_packages
then
	echo "Something went wrong installing packages.  Please review output and try again."
	echo "$install_failures"
	exit 1
fi

# Set up the user's startscript
su - $TARGET -c "mkdir -p .config/pianod2"
create_file $TARGETHOME/.config/pianod2/startscript ${TARGET}.pi 644 << EOF
# Use ALSA driver for output
ROOM RECONFIGURE DRIVER alsa OPTIONS strict=experimental
# Allow listening visitors reasonable visibility
SET VISITOR RANK LISTENER
# Default mode: play requests if submitted
PLAY REQUEST
# Restore existing sources
# SOURCE USE name
EOF

# Create a .kshrc with environment variables so piano(1) works.
# It is not necessary to -I/usr/local/include
create_file $TARGETHOME/.kshrc ${TARGET}.pi 644 << 'EOF'
# Default admin user and password for pianod
# Change these after you have properly configured the music server
export PIANOD_USER=admin
export PIANOD_PASSWORD=admin

# Set output parameters for unit test using libavdevices
# export PIANOD_UNITTEST_AUDIO_DRIVER=alsa
# export PIANOD_UNITTEST_AUDIO_ID="strict=experimental"

# Add /usr/local/lib to search path (location of gstreamermm)
if [ "$LD_LIBRARY_PATH" = "" ]
then
	export LD_LIBRARY_PATH="/usr/local/lib"
else
	export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib"
fi
EOF

# Add the aforementioned stuff to the bashrc too
if [ ! -f "$TARGETHOME/.bashrc.pianod" ]
then
	touch "$TARGETHOME/.bashrc"
	cp -p "$TARGETHOME/.bashrc" "$TARGETHOME/.bashrc.pianod"
	echo ". $TARGETHOME/.kshrc" >> "$TARGETHOME/.bashrc"
fi

# Update startups to give console to pianod user
# Keep an original copy named <file>.pianod but don't overwrite if
# it already exists.
backup=.pianod
[ -f "/etc/lightdm/lightdm.conf.pianod" ] && backup=
sed /etc/lightdm/lightdm.conf -i$backup -e "s/^#?autologin-user=.*/autologin-user=$TARGET/"

# Establish some sane ALSA details
# These get saved by the Pi when it goes down/restored on boot
# Select the analog output
amixer cset numid=3 1
# Set system volume level
amixer set PCM -- -0.00

create_file /etc/systemd/system/pianod.service root 644 << EOF
[Unit]
Description=Pianod music daemon.
After=alsa-util

[Service]
Environment=LD_LIBRARY_PATH=/usr/local/lib
ExecStart=/usr/local/bin/pianod -n$TARGET
Restart=always

[Install]
WantedBy=default.target
EOF

# Create a runmix prompter on boot for user, but
# it is up to user to enable it.
# References:
# https://alan-mushi.github.io/2014/10/26/execute-an-interactive-script-at-boot-with-systemd.html
create_file /etc/systemd/system/mixerprompt.service root 644 << EOF
[Unit]
Description=Prompt user to select a music mix during boot sequence.
After=pianod.service
Before=getty@tty1.service lightdm.service
Requires=pianod.service

[Service]
Type=oneshot
ExecStart=/bin/su - $TARGET -c 'retries=5; while [ \$retries -gt 0 ] && ! piano status >/dev/null ; do let retries--; sleep 1; done; runmix -i'
StandardInput=tty
TTYPath=/dev/console
TTYReset=yes
TTYVHangup=yes

[Install]
WantedBy=default.target
EOF

systemctl enable pianod
systemctl start pianod

cat << EOF

To enable mix selection prompt on boot:
  sudo systemctl enable mixerprompt

To start pianod as this user, use a fresh login or re-read the shell's rc file.
If you're using an obscure shell, see .kshrc for the necessary environment
setup.

EOF

