#!/bin/bash
# shebang for syntax highlighting purposes

# Colors
_NON="\[\033[0m\]"
_BLD="\[\033[1m\]"
_BLK="\[\033[30m\]"
_RED="\[\033[31m\]"
_GRN="\[\033[32m\]"
_YLW="\[\033[33m\]"
_BLU="\[\033[34m\]"
_PRP="\[\033[35m\]"
_CYN="\[\033[36m\]"
_WHT="\[\033[37m\]"

__debug_trap() {
        # Set necessary pre-command variables (PROMPT_COMMAND is a
        # command so its excluded here)
        if [[ "$BASH_COMMAND" != "$PROMPT_COMMAND"
                && "$LAST_BASH_COMMAND" == "$PROMPT_COMMAND" ]]; then
                INC_TIME=1
                LAST_RT="$EPOCHREALTIME" # This should be the last thing done in this function
        fi
        LAST_BASH_COMMAND="$BASH_COMMAND"
}

trap '__debug_trap' DEBUG

__get_cmd_time() {
        # Set hours minutes seconds and remove preceding zeros
        local YEAR
        local DAYS
        local HOUR
        local MINS
        local SECS
        ((YEAR=10#$(($(TZ=UTC printf '%(%Y)T' "$CMD_TIME")))-1970))
        ((DAYS=10#$(TZ=UTC printf '%(%j)T' "$CMD_TIME")))
        ((HOUR=10#$(TZ=UTC printf '%(%H)T' "$CMD_TIME")))
        ((MINS=10#$(TZ=UTC printf '%(%M)T' "$CMD_TIME")))
        ((SECS=10#$(TZ=UTC printf '%(%S)T' "$CMD_TIME")))

        # Choose whether or not to print hours minutes and seconds
        [[ $CMD_TIME -ge 31536000 ]] && printf '%sy ' "${YEAR}"
        [[ $CMD_TIME -ge 86400    ]] && printf '%sd ' "${DAYS}"
        [[ $CMD_TIME -ge 3600     ]] && printf '%sh ' "${HOUR}"
        [[ $CMD_TIME -ge 60       ]] && printf '%sm ' "${MINS}"
        [[ $CMD_TIME -ge 1        ]] && printf '%ss ' "${SECS}"
        # If you want to have a limit uncomment the next line and replace somenum with
        # the minimum microseconds
#        [[ $CMD_US   -ge somenum  ]] &&
                                        printf '%sμs' "${CMD_US:-0}"
}

__sig() {
        # Giant switch case for getting the name of the signal (`kill -l`)
        local ITR=0
        local RET
        for RET in "$@"; do
                if [[ $ITR != 0 ]]; then
                        printf '%s|%s' "$_WHT" "$_RED"
                fi
                ((ITR++))
                case $RET in
                        126): "ACCES"  ;;
                        127): "NOENT"  ;;
                        129): "HUP"    ;;
                        130): "INT"    ;;
                        131): "QUIT"   ;;
                        132): "ILL"    ;;
                        133): "TRAP"   ;;
                        134): "ABRT"   ;;
                        135): "BUS"    ;;
                        136): "FPE"    ;;
                        137): "KILL"   ;;
                        138): "USR"1   ;;
                        139): "SEGV"   ;;
                        140): "USR"2   ;;
                        141): "PIPE"   ;;
                        142): "ALRM"   ;;
                        143): "TERM"   ;;
                        144): "STKFLT" ;;
                        145): "CHLD"   ;;
                        146): "CONT"   ;;
                        147): "STOP"   ;;
                        148): "TSTP"   ;;
                        149): "TTIN"   ;;
                        150): "TTOU"   ;;
                        151): "URG"    ;;
                        152): "XCPU"   ;;
                        153): "XFSZ"   ;;
                        154): "VTALRM" ;;
                        155): "PROF"   ;;
                        156): "WINCH"  ;;
                        157): "IO"     ;;
                        158): "PWR"    ;;
                        159): "SYS"    ;;
                        16[3-9]|1[7-8][0-9]|19[0-2]): "RT$((RET-128))" ;; # Savagery
                        *):   "$RET"   ;; # Print exit code if not in list
                esac
                printf '%s' "$_"
        done
}

__ssh() {
        local CON=($SSH_CONNECTION)
        local SRV_IP="${CON[2]}"
        [[ -z "$SRV_IP" ]] && return
        local SRV_PORT="${CON[3]}"
        # 4 chars from startand 4 chars from end
        local SRV_IP_CUT="${_CYN}${SRV_IP}"
        [[ ${#SRV_IP} -gt 8 ]] && local SRV_IP_CUT="${_CYN}${SRV_IP:0:4}${_WHT}*${_CYN}${SRV_IP: -4}"

        printf '%s' "${_GRN}${_BLU}[${SRV_IP_CUT}${_PRP}${_BLD}:${_NON}${_CYN}${SRV_PORT}${_BLU}]${_NON}"
}

__guix_env() {
        local re='[\(]?\("([^"]*)"'
        if [[ "$GUIX_ENVIRONMENT" ]]; then
                local PACKAGES=($(while read -r line; do
                                [[ "$line" =~ $re ]] && printf '%s ' "${BASH_REMATCH[1]}"
                        done < $GUIX_ENVIRONMENT/manifest))
                if [[ ${#PACKAGES[@]} -eq 2 ]]; then
                        printf "${_BLD}${_BLU}[${_NON}${_YLW}Guix${_NON}: ${_WHT}%s ${_NON}+ ${_PRP}%d ${_NON}pkg${_BLD}${_BLU}] " "${PACKAGES[0]}" "$((${#PACKAGES[@]}-1))"
                elif [[ ${#PACKAGES[@]} -gt 2 ]]; then
                        printf "${_BLD}${_BLU}[${_NON}${_YLW}Guix${_NON}: ${_WHT}%s ${_NON}+ ${_PRP}%d ${_NON}pkgs${_BLD}${_BLU}] " "${PACKAGES[0]}" "$((${#PACKAGES[@]}-1))"
                else
                        printf "${_BLD}${_BLU}[${_NON}${_YLW}Guix${_NON}: ${_WHT}%s${_BLD}${_BLU}] " "${PACKAGES[@]}"
                fi
        fi
}

__prompt() {
        # Get exit code (must be first)
        local PLC=("${PIPESTATUS[@]}")

        # Reset time when prompt was first displayed after command
        # this contributes to the ~40 microsecond difference in $CMD_US and the actual time it took
        if [[ "$INC_TIME" != 0 ]]; then 
                PROMPT_RT="$EPOCHREALTIME"
                INC_TIME=0
        fi

        # *_RT may not be set
        LAST_RT="${LAST_RT:-$EPOCHREALTIME}"
        PROMPT_RT="${PROMPT_RT:-$EPOCHREALTIME}"

        # Get relative times

        # Remove decimal point, simulating multiplying by 1 million
        PROMPT_RT1M="${PROMPT_RT/.}"
        PROMPT_RT1M="${PROMPT_RT1M/,}"
        LAST_RT1M="${LAST_RT/.}"
        LAST_RT1M="${LAST_RT1M/,}"

        ((CMD_US=PROMPT_RT1M-LAST_RT1M))

        CMD_TIME=0

        # Divide by 1 million to get a more accurate difference
        ((CMD_TIME=CMD_US/1000000))

        [[ ${#CMD_US} -lt 6 ]] || CMD_US="${CMD_US: -6}"
        ((CMD_US=10#$CMD_US))

        # Set prompt sections

        local SIG
        local COL
        local UHD
        local TIME
        local IND
        local GUIX

        # Text

        # ssh detection and indicator
        [[ "$SSH_CONNECTION" ]] && local SSH="$(__ssh) "

        # [INT], [4], etc.
        local RET
        for RET in "${PLC[@]}"; do
                if [[ $RET -gt 0 ]]; then
                        SIG="$(printf '%s[%s%s%s] ' "$_BLU" "$_RED" "$(__sig "${PLC[@]}")" "$_BLU")"
                        break
                fi
        done

        # Guix shell environment
        GUIX="$(__guix_env)"

        # [user@homeserver:~]
        COL="$([[ $UID == 0 ]] && printf '%s' "$_RED" || printf '%s' "$_YLW")"
        UHD="${_BLD}${_BLU}[${COL}\u${_PRP}@${_CYN}\h${_PRP}:${_GRN}\w${_BLU}]"

        # 2y 351d 12m 43s 382969μs
        TIME="${_YLW}$(__get_cmd_time)"

        # Random colored $ or #
        _RAND256="\[\033[38;2;$((RANDOM%256));$((RANDOM%256));$((RANDOM%256))m\]"
        IND="${_RAND256}\\$"

        # Set the prompt
        PS1="${_NON}${SSH}${GUIX}${SIG}${UHD} ${TIME} ${IND} ${_NON}"
}
        
PROMPT_COMMAND=__prompt