#!/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
