#!/bin/bash
# User setup script.
# (C) Mark Blakeney, Aug 2016.

PROG="$(basename $0)"
NAME=${PROG%-*}

BINDIR="/usr/bin"
SYSDIR="/usr/lib/systemd/user"
APPDIR="/usr/share/applications"
ICOBAS="/usr/share/icons/hicolor"
ICODIR="$ICOBAS/128x128/apps"
OCODIR="/usr/share/pixmaps"
DOCDIR="/usr/share/doc/$NAME"
CNFDIR="/etc"
HCFDIR="${XDG_CONFIG_HOME:-$HOME/.config}"
AUTDIR="$HCFDIR/autostart"
SVCFLG="$HCFDIR/.$NAME-is-service"
CHECKSECS=6

usage() {
    echo "Usage:"
    echo
    echo "As root:"
    echo "  $ sudo $PROG install|uninstall"
    echo "  -d <dir> (option sets DESTDIR for install/uninstall)"
    echo
    echo "As user:"
    echo "  $ $PROG command [command ..]"
    echo "  where command is any of:"
    echo "  start|stop|restart|autostart|autostop|status|desktop|service"
    echo "  default command = status."
    echo
    exit 1
}

# Process command line options
DESTDIR=""
while getopts d: c; do
    case $c in
    d) DESTDIR="$OPTARG";;
    \?) usage;;
    esac
done

shift $((OPTIND - 1))

# Test if given systemd property is set for given unit
sysd_prop() {
    if systemctl --user show -p "$2" "$1" 2>/dev/null | grep -q "=$3$"; then
	echo 1
    else
	echo 0
    fi
}

# Launch given desktop app. First work out most suitable launcher.
# Pretty crude at present but should work for at least GNOME and KDE.
de_start() {
    local app="$1"
    local fullpath="$APPDIR/$app.desktop"
    local binpath="$BINDIR/$app"

    # All the commands we will potentially try ..
    local cmds=(
	"kde kioclient5 exec $fullpath"
	"kde kioclient exec $fullpath"
	"all gtk-launch $app"
	"all i3-msg exec $binpath"
	"all exo-open $fullpath"
	"all dex $fullpath"
    )

    local cmdline
    for cmdline in "${cmds[@]}" ; do
	IFS=' ' read de cmd args <<< "$cmdline"

        # Skip if the command does not exist
	if ! hash $cmd &>/dev/null; then
	    continue
	fi

	# Only try KDE commands on KDE
	if ! echo $XDG_CURRENT_DESKTOP | grep -q KDE; then
	    if [[ $de == kde ]]; then
		continue
	    fi
	fi

	# Execute this command
	$cmd $args &>/dev/null
	return $?
    done

    echo "Don't know how to invoke $app.desktop" >&2
    return 1
}

# Set up desktop entry link for auto start of app, if it doesn't already
# exist
de_auto_start() {
    if [[ ! -f $APPDIR/$NAME.desktop ]]; then
	if [[ -e $AUTDIR/$NAME.desktop ]]; then
	    echo "Removed old $AUTDIR/$NAME.desktop"
	    rm -f $AUTDIR/$NAME.desktop
	fi
	return 1
    fi

    if ! cmp -s $APPDIR/$NAME.desktop $AUTDIR/$NAME.desktop; then
	if mkdir -p $AUTDIR && cp $APPDIR/$NAME.desktop $AUTDIR; then
	    echo "installed or updated $AUTDIR/$NAME.desktop"
	fi
    fi
    return 0
}

# Action given user command
user_action() {
    local cmd="$1"
    local pidfile=/tmp/$NAME-$USER.pid

    # Test if systemd is enabled/running
    if [[ $HAS_SYSD -eq 1 ]]; then
	svc_enabled=$(sysd_prop $NAME.service UnitFileState enabled)
	svc_running=$(sysd_prop $NAME.service SubState running)
    else
	svc_enabled=0
	svc_running=0
    fi

    if [[ $cmd == service ]]; then
	if [[ $HAS_SYSD -eq 0 ]]; then
	    echo "Systemd not available, can not run as service."
	    exit 1
	fi
	mkdir -p "$(dirname $SVCFLG)"
	echo "# This file created by \"$NAME-setup $cmd\" command." >$SVCFLG
	rm -fv $AUTDIR/$NAME.desktop
    elif [[ $cmd == desktop ]]; then
	rm -f $SVCFLG
	if [[ $HAS_SYSD -eq 1 ]]; then
	    systemctl --user disable $NAME.service &>/dev/null
	fi
    elif [[ $cmd == start ]]; then
	STARTAS=""
	if [[ -f $SVCFLG ]]; then
	    if [[ $HAS_SYSD -eq 0 ]]; then
		echo "Systemd service is not available."
		exit 1
	    fi
	    if systemctl --user start $NAME.service; then
		STARTAS="user service"
	    fi
	else
	    if [[ ! -f $APPDIR/$NAME.desktop ]]; then
		echo "$NAME is not installed."
		exit 1
	    fi
	    if de_start "$NAME"; then
		STARTAS="desktop application"
	    fi
	fi

	if [[ -n $STARTAS ]]; then
	    # Wait some time to start ..
	    done=0
	    for _ in $(seq $CHECKSECS); do
		sleep 1
		if ps "$(head -1 $pidfile 2>/dev/null)" &>/dev/null; then
		    done=1
		    break
		fi
	    done

	    if [[ $done -eq 1 ]]; then
		echo "$NAME started as a $STARTAS".
	    else
		echo "$NAME failed to start as a $STARTAS". >&2
	    fi
	fi
    elif [[ $cmd == stop ]]; then
	STARTAS=""
	if [[ $svc_running -eq 1 ]]; then
	    systemctl --user stop $NAME.service
	    STARTAS="user service"
	else
	    if [[ -f $pidfile ]]; then
		local killed=0
		while read pid; do
		    if kill $pid &>/dev/null; then
			killed=1
		    fi
		done <$pidfile
		if [[ $killed -ne 0 ]]; then
		    STARTAS="desktop application"
		fi
	    fi
	fi

	if [[ -n $STARTAS ]]; then
	    # Wait some time to stop ..
	    done=0
	    for _ in $(seq $CHECKSECS); do
		sleep 1
		if ! ps "$(head -1 $pidfile 2>/dev/null)" &>/dev/null; then
		    done=1
		    break
		fi
	    done

	    if [[ $done -eq 1 ]]; then
		echo "$NAME stopped $STARTAS".
	    else
		echo "$NAME failed to stop $STARTAS". >&2
	    fi
	fi
    elif [[ $cmd == autostart ]]; then
	if [[ $HAS_SYSD -eq 1 ]]; then
	    # Be sure to remove any old systemd links ..
	    systemctl --user disable $NAME.service &>/dev/null
	fi
	if [[ -f $SVCFLG ]]; then
	    if [[ $HAS_SYSD -eq 0 ]]; then
		echo "Systemd service is not available."
		exit 1
	    fi

	    if systemctl --user enable $NAME.service; then
		echo "$NAME enabled as a user service."
	    fi
	    rm -fv $AUTDIR/$NAME.desktop
	elif ! de_auto_start; then
	    echo "$NAME is not installed."
	    exit 1
	fi
    elif [[ $cmd == autostop ]]; then
	if [[ $HAS_SYSD -eq 1 ]]; then
	    systemctl --user disable $NAME.service &>/dev/null
	fi
	rm -fv $AUTDIR/$NAME.desktop
    elif [[ $cmd == status ]]; then
	if [[ -f $BINDIR/$NAME ]]; then
	    echo "$NAME is installed."
	else
	    echo "$NAME is not installed."
	fi

	if [[ -f $SVCFLG ]]; then
	    echo "$NAME is set up as a user service."
	else
	    echo "$NAME is set up as a desktop application."
	fi

	if [[ $svc_running -eq 1 ]]; then
	    echo "$NAME is currently running as a user service."
	elif ps "$(head -1 $pidfile 2>/dev/null)" &>/dev/null; then
	    echo "$NAME is currently running as a desktop application."
	else
	    echo "$NAME is not currently running."
	fi

	if [[ $svc_enabled -eq 1 ]]; then
	    echo "$NAME is set to autostart as a user service."
	    rm -fv $AUTDIR/$NAME.desktop
	else
	    if [[ -f $AUTDIR/$NAME.desktop ]]; then
		echo "$NAME is set to autostart as a desktop application."
	    else
		echo "$NAME is not set to autostart."
	    fi
	fi

	if [[ -f $HCFDIR/$NAME.conf ]]; then
	    echo "$NAME is using custom configuration file."
	else
	    echo "$NAME is using default configuration file."
	fi
    else
	echo "ERROR: \"$cmd\" is not a valid user command" >&2
    fi
}

cmd="$1"
if [[ $cmd == install || $cmd == uninstall ]]; then
    DESTDIR="${DESTDIR%%+(/)}"
    if [[ -z $DESTDIR && $(id -un) != root ]]; then
	echo "Install or uninstall must be run as sudo/root."
	exit 1
    fi

    if [[ $# -ne 1 ]]; then
	usage
    fi

    # Remove any old files from earlier versions of program
    rm -f $DESTDIR$OCODIR/$NAME.png
    rm -f $DESTDIR$ICODIR/$NAME.png

    if [[ $cmd == install ]]; then
	install -CDv -m 755 -t $DESTDIR$BINDIR $NAME-setup
	install -CDv -m 755 -t $DESTDIR$BINDIR $NAME
	install -CDv -m 644 -t $DESTDIR$SYSDIR $NAME.service
	install -CDv -m 644 -t $DESTDIR$APPDIR $NAME.desktop
	install -CDv -m 644 -t $DESTDIR$ICODIR $NAME.svg
	install -CDv -m 644 -t $DESTDIR$CNFDIR $NAME.conf
	install -CDv -m 644 -t $DESTDIR$DOCDIR README.md
    else
	rm -rfv $DESTDIR$BINDIR/$NAME
	rm -rfv $DESTDIR$SYSDIR/$NAME.service
	rm -rfv $DESTDIR$APPDIR/$NAME.desktop
	rm -rfv $DESTDIR$ICODIR/$NAME.svg
	rm -rfv $DESTDIR$CNFDIR/$NAME.conf
	rm -rfv $DESTDIR$DOCDIR
	rm -rfv $DESTDIR$BINDIR/$NAME-setup
    fi

    if [[ -z $DESTDIR ]]; then
	if [[ -x /usr/bin/update-desktop-database ]]; then
	    /usr/bin/update-desktop-database -q
	fi
	if [[ -x /usr/bin/gtk-update-icon-cache ]]; then
	    /usr/bin/gtk-update-icon-cache $ICOBAS
	fi
    fi
else
    if [[ $(id -un) == root ]]; then
	echo "Non-installation commands must be run as your own user."
	exit 1
    fi

    # Test if systemd is installed
    if type systemctl &>/dev/null; then
	HAS_SYSD=$(sysd_prop graphical-session.target ActiveState active)
    else
	HAS_SYSD=0
    fi

    if [[ $HAS_SYSD -eq 1 ]]; then
	# Reload systemd if service file has been updated
	if systemctl --user status $NAME.service 2>&1 |
	    grep -q ' changed on disk'; then
	    echo "$NAME.service has changed, reloading systemd user daemon .."
	    systemctl --user daemon-reload
	    sleep 2
	fi
    fi

    # Remove any old configuration from earlier versions of program
    rm -fv ~/bin/$NAME 2>/dev/null
    rm -fv ~/.local/bin/$NAME 2>/dev/null
    rm -fv ~/.local/share/applications/$NAME.desktop 2>/dev/null
    rm -fv ~/.local/share/icons/$NAME.png 2>/dev/null

    # Look for and update any autostart file if it is a link or not
    # pointing to the latest desktop entry. Apparently user autostart
    # files should not be symlinks to system dir files.
    if [[ -e $AUTDIR/$NAME.desktop ]]; then
	if [[ -L $AUTDIR/$NAME.desktop ]]; then
	    echo "Removed old $AUTDIR/$NAME.desktop link"
	    rm -f $AUTDIR/$NAME.desktop
	fi
	de_auto_start
    fi

    # Execute each command given on command line ..
    if [[ $# -lt 1 ]]; then
	set -- status
    fi
    for cmd in "$@"; do
	if [[ $cmd == restart ]]; then
	    user_action "stop"
	    cmd=start
	fi

	user_action "$cmd"
    done
fi

exit 0
