#!/usr/bin/env sh

# output is to stdout unless explicitly set through -o or env var
OUTPUTF="$SXHKD_OUTPUTF"

# set fifo input file, according (somewhat) to xdg
if [ -n "$SXHKD_FIFO" ]; then
    FIFO="$SXHKD_FIFO"
elif [ -p "${XDG_RUNTIME_DIR}"/sxhkd_fifo ]; then
    FIFO="${XDG_RUNTIME_DIR}"/sxhkd_fifo
elif [ -p "${XDG_CACHE_HOME:-$HOME/.cache}"/sxhkd_fifo ]; then
    FIFO="${XDG_CACHE_HOME:-$HOME/.cache}"/sxhkd_fifo
elif [ -p "$HOME/.sxhkd_fifo" ]; then
    FIFO="$HOME/.sxhkd_fifo"
fi

# set label config file, according (somewhat) to xdg
if [ -n "$SXHKD_LABELCONFIG" ]; then
    LABELCONFIG="$SXHKD_LABELCONFIG"
elif [ -f "${XDG_CONFIG_HOME:-$HOME/.config}"/sxhkd/chain-labels.conf ]; then
    LABELCONFIG="${XDG_CONFIG_HOME:-$HOME/.config}"/sxhkd/chain-labels.conf
elif [ -f "$HOME/.chain-labels.conf" ]; then
    LABELCONFIG="$HOME/.chain-labels.conf"
fi

SXHKDRC_FILE="$XDG_CONFIG_HOME"/sxhkd/sxhkdrc

main() {
    while read -r event; do
        detect_event "$event"
    done <"$FIFO"
}

detect_event() {
    ev="$(echo "$1" | sed -e 's/^H.*$/hotkey/;s/^C.*$/command/;s/^BBegin chain.*$/chainstart/;s/^EEnd chain.*$/chainend/;')"

    case $ev in
    hotkey) ev_hotkey "$(echo "$1" | sed -e 's/^H//')" ;;
    # command) ev_command "$(echo "$1" | sed -e 's/^C//')" ;;
    chainstart) ev_chainstart "$(echo "$1" | sed -e 's/^B//')" ;;
    chainend) ev_chainend "$(echo "$1" | sed -e 's/^E//')" ;;
    *) ;;
    esac
}

send_msg() {
    if [ -n "$OUTPUTF" ]; then
        echo "$1" >"$OUTPUTF"
    else
        echo "$1"
    fi
}

ev_hotkey() {
    LAST_HOTKEY="$1"
}

# compare labels to last hotkey, return mode name
ev_chainstart() {
    [ -z "$LAST_HOTKEY" ] && return 1
    found=$(echo "$LABELS" | sed -e "/$LAST_HOTKEY/!d;s/^\(.\+\):.*$/\1/")
    send_msg "$found"
}

ev_chainend() {
    send_msg ""
}

# TODO add option to also display last command done in chain
# ev_command() {
#   send_msg "command: $1"
# }

# read config from file, remove comments (lines starting with #) and empty lines
read_config() {
    [ ! -f "$1" ] && return 1

    parse_labels "$(cat "$1")"
}

# parse sxhkdrc for mode compatible comments
read_sxhkdrc() {
    [ ! -f "$1" ] && return 1

    _sxhkdrc_content="$(sed -e '/^# mode:/!d;s/^# mode://' <"$1")"
    parse_labels "$_sxhkdrc_content"
}

# append
parse_labels() {
    LABELS="${LABELS}$(echo "$1" | sed -e '/^#/d;/^[[:blank:]]*$/d')"
}

get_help() {
    printf \
        "Usage: sxhkd-chain-labels [-c config file][-o output file][-s input pipe]
    
 By default will take the input from the input pipe (at XDG_RUNTIME_DIR/sxhkd_fifo) and 
 print the current sxhkd chain mode to stdout. That means, sxhkd needs to be started
 with a fifo pipe running, ideally to the XDG_RUNTIME_DIR:

  mkfifo \$XDG_RUNTIME_DIR/sxhkd_fifo && sxhkd -s \$XDG_RUNTIME_DIR/sxhkd_fifo

 When given a key combination which maps to a specific mode, it will print out the name
 of the mode instead. These maps can be passed with -c flag, specifying an options file.
 By default it will look in \$XDG_CONFIG_HOME/sxhkd/sxhkd-chain-labels.conf

 When passed the -o flag it will replace the contents of the file passed in with the 
 current sxhkd chain mode, emptying the file when no mode is active.

 Default lookup places for files, in descending order:
   :input fifo
     \$SXHKD_FIFO (env variable)
     \$XDG_RUNTIME_DIR/sxhkd_fifo
     \$XDG_CACHE_HOME/sxhkd_fifo
     ~/.cache/sxhkd_fifo
     ~/.sxhkd_fifo

   :label configuration
     \$SXHKD_LABELCONFIG (env variable)
     \$XDG_CONFIG_HOME/sxhkd/chain-labels.conf
     ~/.config/sxhkd/chain-labels.conf
     ~/.chain-labels.conf 
     \$XDG_CONFIG_HOME/sxhkd/sxhkdrc (parsing)

 The label configuration file uses the following format:
   mode name:key chain

 Lines beginning with a # will be ignored. Whitespace is important, sxhkd will, by 
 default, put a single space between any component of the key combination.

 An example file chain-labels.conf:
   media:super + alt + m
   system:super + backspace

 Instead of using an explicit configuration file, you can put the chain mode 
 information into the regular sxhkdrc as comments. They need to follow this exact format:
   # mode:mode-name:key-chain 

 They can occur anywhere in the file. The space between # and mode is necessary, and # 
 needs to be the first character on the line. 
 The above example file as written into the sxhkdrc:
   # mode:media:super + alt + m
   # mode:system:super + backspace

 If an explicit configuration file exists, it will supersede any mode information in the
 sxhkdrc file.
  \n"
}

get_version() {
    printf \
        "%s: 0.3

  fifo input pipe:
    %s %s
  label configuration file:
    %s %s
  output:
    %s \n" \
        "$0" \
        "$FIFO" \
        "$([ -p "$FIFO" ] && echo "(found)" || echo "(NOT FOUND)")" \
        "${LABELCONFIG:-"$SXHKDRC_FILE"}" \
        "$([ -f "${LABELCONFIG:-"$SXHKDRC_FILE"}" ] && echo "(found)" || echo "(NOT FOUND)")" \
        "${OUTPUTF:-stdout}"
}

while getopts "vho:s:c:" opt; do
    case "$opt" in
    \?)
        printf "Usage: sxhkd-chain-labels [-c config file][-o output file][-s input pipe]\n"
        exit 0
        ;;
    h)
        get_help
        exit 0
        ;;
    v)
        get_version
        exit 0
        ;;
    o) OUTPUTF="$OPTARG" ;;
    s) FIFO="$OPTARG" ;;
    c) LABELCONFIG="$OPTARG" ;;
    esac
done
shift $((OPTIND - 1))

[ "${1:-}" = "--" ] && shift

# look for default label config, prefer config file to parsing sxhkdrc
[ -z "$LABELS" ] && read_config "$LABELCONFIG"
[ -z "$LABELS" ] && read_sxhkdrc "$SXHKDRC_FILE"

main "$@"