#!/bin/sh
# shellcheck source=/dev/null
#
# pfetch - Simple POSIX sh fetch script.

die() {
    printf '\033[31;1merror\033[m: %s.\n' "$@" >&2
    exit 1
}

log() {
    printf '\033[3%s;1m%s\033[m%s%s\n' "$1" "$2" "$3" "${4:-unknown}"
}

get_os() {
    case $kernel_name in
        Linux|GNU*) os=linux ;;

        *)
            die "Unknown OS detected '$kernel_name'" \
                "Open an issue on GitHub to add support for your OS"
        ;;
    esac
}

get_title() {
    case $os in
        linux)
            read -r hostname < /proc/sys/kernel/hostname
        ;;
    esac

    # Username is retrieved by first checking '$USER' with a fallback
    # to the 'whoami' command.
    #
    # Hostname is retrieved by first checking '$HOSTNAME' with a fallback
    # to the OS specific detection above and finally an additional fallback
    # to the 'hostname' command.
    #
    # Disable the warning about '$HOSTNAME' being undefined
    # in POSIX sh as it is intended for allowing the user to
    # overwrite the value on invocation.
    # shellcheck disable=SC2039
    log 1 "${USER:-$(whoami)}" @ "${HOSTNAME:-${hostname:-$(hostname)}}"
}

get_distro() {
    case $os in
        linux)
            . /etc/os-release && distro=$PRETTY_NAME
        ;;
    esac

    log 1 os " " "$distro"
}

get_kernel() {
    log 1 kernel " " "$kernel_version"
}

get_uptime() {
    # Uptime works by retrieving the data in total seconds and then
    # converting that data into days, hours and minutes using simple
    # math.
    case $os in
        linux)
            IFS=. read -r s _ < /proc/uptime
        ;;
    esac

    # Convert the uptime from seconds into days, hours and minutes.
    d=$((s / 60 / 60 / 24))
    h=$((s / 60 / 60 % 24))
    m=$((s / 60 % 60))

    # Only append days, hours and minutes if they're non-zero.
    [ "$d" = 0 ] || uptime="${uptime}${d}d "
    [ "$h" = 0 ] || uptime="${uptime}${h}h "
    [ "$m" = 0 ] || uptime="${uptime}${m}m "

    log 1 uptime " " "${uptime:-0m}"
}

get_memory() {
    case $os in
        # Used memory is calculated using the following "formula" (Linux):
        # MemUsed = MemTotal + Shmem - MemFree - Buffers - Cached - SReclaimable
        # Source: https://github.com/KittyKatt/screenFetch/issues/386
        linux)
            # Parse the '/proc/meminfo' file splitting on ':' and 'k'.
            # The format of the file is 'key:   000kB' and an additional
            # split is used on 'k' to filter out 'kB'.
            while IFS=:k read -r key val _; do
                case $key in
                    MemTotal)
                        mem_used=$((mem_used + val))
                        mem_total=$val
                    ;;

                    Shmem)
                        mem_used=$((mem_used + val))
                    ;;

                    MemFree|Buffers|Cached|SReclaimable)
                        mem_used=$((mem_used - val))
                    ;;
                esac
            done < /proc/meminfo

            mem_used=$((mem_used / 1024))
            mem_total=$((mem_total / 1024))
        ;;
    esac

    log 1 memory " " "${mem_used}MiB / ${mem_total}MiB"
}

main() {
    # Hide 'stderr' unless the first argument is '-v'. This saves
    # polluting the script with '2>/dev/null'.
    [ "$1" = -v ] || exec 2>/dev/null

    # Store the output of 'uname' to avoid calling it multiple times
    # throughout the script. 'read <<EOF' is the simplest way of reading
    # a command into a list of variables.
    read -r kernel_name kernel_version kernel_machine <<EOF
        $(uname -srm)
EOF

    get_os
    get_title
    get_distro
    get_kernel
    get_uptime
    get_memory
}

main "$@"
