#!/usr/bin/bash
#
#
# Notification handling: create, check, hide, remove. Here is
# also a 'hide' mechanism hiding current state for user (affects
# all notifications) and a locking tool aimed for the GUI.
#

scriptdir=$( dirname $(readlink -fn $0))
source $scriptdir/lpf-defs.bash


function get_user() { echo "${SUDO_USER:-$USER}"; }
function get_uid() { echo "${SUDO_UID:-$UID}"; }


function check_pkgbuild_user()
# Check that we run as pkg-build user, exit if not
{
    [ "$USER" != 'pkg-build' ] && {
        echo >&2 "Error: must be run as pkg-build user."
        exit 1
    }
    return 0
}


function get_lockdir()
# Return lock directory for current user and DISPLAY. Requires $DISPLAY.
{
    [ "$DISPLAY" ] || { echo; return 1; }
    display="$DISPLAY"
    [[ "$display" == :* ]] && display="0${display}"
    local lockdir="/var/run/user/$( get_uid )/lpf/locks/$display"
    [ -d $lockdir ]  || mkdir -p $lockdir
    echo "$lockdir"
}


function get_lockfile()
# Return lockfile for current user, requires DISPLAY.
{
    echo $( get_lockdir )/$( get_user ).$$
}


function clean_locks()
# Remove all locks referring to dead processes.
{
    for lock in $( ls $( get_lockdir )/$( get_user ).* 2>/dev/null ); do
        local pid=${lock#*.}
        ps --pid $pid &> /dev/null || rm -f $lock
    done
}


function lock()
# Create a lock for current user and DISPLAY (no-op without DISPLAY)
{
    [ "$DISPLAY" ] || return 0 && clean_locks && touch $( get_lockfile )
}


function unlock()
# Unlock, no-op if not locked.
{
    [ "$DISPLAY" ] || return 0 && clean_locks && rm -f $( get_lockfile ) || :
}


function is_locked()
# Silent test if there is a lock for current user and DISPLAY.
{
    [ "$DISPLAY" ] ||  return 1 && \
        test -f $( get_lockdir )/$( get_user).* &>/dev/null
}


function get_hidedir()
# Return directory for user for hide (mute) definitions.
{
    local uid=$( get_uid )
    local hidedir="/var/run/user/$uid/lpf/hide"
    echo "$hidedir"
}


function hide()
# Hide message about given package for invoking user.
{
    local pkg=$1
    local hide_dir=$( get_hidedir )
    [ -d "$hide_dir" ] || mkdir -p "$hide_dir"
    echo $( get_state $pkg ) > $hide_dir/$pkg
}


function unhide()
#  Make message about given package visible for invoking user.
#  No-op if not hidden.
{
    local pkg=$1
    local hide_dir=$( get_hidedir )
    rm -f $hide_dir/$pkg
}


function is_hidden()
# Return 0 if messages for given package are muted for current user.
{
    local pkg=$1
    local hide_dir=$( get_hidedir )
    test -e  $hide_dir/$pkg
}


function create()
# Create or update a message for a given package and state.
# Invoke as pkg-build user.
{
    check_pkgbuild_user
    local pkg=$1
    local state=$2
    if [ -f $LPF_VAR/notify/$pkg ]; then
        echo $state > $LPF_VAR/notify/$pkg
        return 0
    fi
    test -d $LPF_VAR/notify || mkdir -p $LPF_VAR/notify
    new_dir=$( mktemp -d /var/tmp/XXXX )
    echo $state > $new_dir/state
    chmod 755 $new_dir
    rm -rf  $LPF_VAR/notify/$pkg
    mv -Tf $new_dir $LPF_VAR/notify/$pkg
}


function remove()
# Remove message for given package. Invoke as pkg-build user.
{
    check_pkgbuild_user
    local pkg=$1
    rm -rf $LPF_VAR/notify/$pkg
}


function is_pkg_message_pending()
# Return true if a message is pending for given package.
{
    local pkg=$1
    test -d $LPF_VAR/notify/$pkg  || return 1
    is_hidden $pkg && return 1
    return 0
}


function is_message_pending()
# Return true if there is any message pending.
{
    dirs=( $( find $LPF_VAR/notify -maxdepth 1 -mindepth 1 -type d ) )
    (( ${#dirs} == 0 )) && return 1
    for dir in  ${dirs[@]}; do
        is_pkg_message_pending ${dir##*/} && return 0
    done
    return 1
}


function cli_notify()
# Print message if there are lpf packages needing rebuild.
{
   local msg="lpf packages need rebuild (lpf update to fix)."
   is_message_pending && echo ${1:-$msg}
}


function notify_watch
# Invoke  $1 if there is package(s) needing rebuild. Kills other instances
# running for current user. Does not return.
{
    trap "" sighup sigchld sigquit sigterm
    trap 'exit 0' sigint

    local pids=$( pgrep -fu $USER 'notify-watch' )
    pids=${pids/$$/}
    [ "${pids/ /}" ] && kill -INT $pids
    sleep 1
    local trigger_func="$*"
    is_message_pending && eval $trigger_func &
    while :; do
        inotifywait -q -q -e moved_to $( find $LPF_VAR/notify -type d ) || \
            continue
        is_locked && continue
        sync
        is_message_pending && eval $trigger_func &
    done
}


cmd=$1; shift

case $cmd in
    'create')   create "$@"
                ;;
    'hide'|'mute')
                hide "$@"
                ;;
    'unhide'|'unmute')
                unhide "$@"
                ;;
    'remove')   remove "$@"
                ;;
    'is-message-pending')
                is_message_pending "$@"
                ;;
    'cli-notify')
                cli_notify "$@"
                ;;
    'notify-watch')
                notify_watch "$@"
                ;;
    'is-hidden') is_hidden "$@"
                ;;
    'lock')     lock "$@"
                ;;
    'unlock')   unlock "$@"
                ;;
    'is-locked')
                is_locked "$@"
                ;;
esac


# vim: set expandtab ts=4 sw=4:
