#!/bin/bash
# aur-view - inspect package files
[[ -v AUR_DEBUG ]] && set -o xtrace
set -o errexit
argv0=view
XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-$HOME/.config}
XDG_DATA_HOME=${XDG_DATA_HOME:-$HOME/.local/share}
AUR_VIEW_DB=${AUR_VIEW_DB:-$XDG_DATA_HOME/aurutils/$argv0}
PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'

# default options
log_fmt='diff' log_args=() excludes=() patch=1 prefix=0

args_csv() {
    # shellcheck disable=SC2155
    local str=$(printf '%s,' "$@")
    printf '%s' "${str%,}"
}

trap_exit() {
    if [[ ! -v AUR_DEBUG ]]; then
        rm -rf -- "$tmp"
    else
        printf >&2 'AUR_DEBUG: %s: temporary files at %s\n' "$argv0" "$tmp"
    fi
}

usage() {
    plain >&2 'usage: %s [--format] <package...>' "$argv0"
    exit 1
}

opt_short='a:'
opt_long=('format:' 'arg-file:' 'revision:' 'no-patch' 'confirm' 'exclude:' 'prefix')
opt_hidden=('dump-options')

if opts=$(getopt -o "$opt_short" -l "$(args_csv "${opt_long[@]}" "${opt_hidden[@]}")" -n "$argv0" -- "$@"); then
    eval set -- "$opts"
else
    usage
fi

unset queue revision prefix
while true; do
    case "$1" in
        -a|--arg-file)
            shift; queue=$1 ;;
        --format)
            shift
            case $1 in
                diff|log)
                    log_fmt=$1 ;;
                *)
                    printf >&2 '%s: invalid --format option: %s' "$argv0" "$1"
                    usage ;;
            esac ;;
        --confirm)
            AUR_CONFIRM_PAGER=1 ;;
        --no-patch)
            patch=0 ;;
        --prefix)
            prefix=1 ;;
        --revision)
            shift; revision=$1 ;;
        --exclude)
            shift; excludes+=("$1") ;;
        --dump-options)
            printf -- '--%s\n' "${opt_long[@]}" ${AUR_DEBUG+"${opt_hidden[@]}"}
            printf -- '%s' "${opt_short}" | sed 's/.:\?/-&\n/g'
            exit ;;
        --) shift; break ;;
    esac
    shift
done

if (( patch )); then
    log_args+=(--patch)
fi

# shellcheck disable=SC2174
mkdir -pm 0700 -- "${TMPDIR:-/tmp}/aurutils-$UID"
tmp=$(mktemp -d --tmpdir "aurutils-$UID/$argv0.XXXXXXX")
trap 'trap_exit' EXIT

# Directory for git checksums (revisions viewed by the user, #379)
mkdir -p -- "$AUR_VIEW_DB"

# Default to showing PKGBUILD first in patch. (#399)
orderfile=$XDG_CONFIG_HOME/aurutils/$argv0/orderfile
mkdir -p -- "${orderfile%/*}"

if [[ ! -s $orderfile ]]; then
    printf 'PKGBUILD\n' > "$orderfile"
fi

# Take input from a file instead of redirecting stdin (#871)
packages=()
if [[ -v queue ]]; then
    exec {fd}< "$queue"

    while read -ru "$fd"; do
        [[ $REPLY ]] && packages+=("$REPLY")
    done
else
    packages=("$@")
    set --
fi

if (( ! ${#packages[@]} )); then
    printf >&2 "there is nothing to do"
    exit
fi

# Link build files in the queue (absolute links)
for pkg in "${packages[@]}"; do
    [[ $pkg ]] && printf '%s\0' "$(pwd -P)/$pkg"
done | xargs -0 ln -t "$tmp" -s --

# Retrieve last viewed revisions
declare -A heads

for pkg in "${packages[@]}"; do
    git() { command git -C "$pkg" "$@"; }

    # Point git to current orderFile path (#1167)
    git config diff.orderFile "$orderfile"

    # Ensure every directory is a git repository (--continue)
    if head=$(git rev-parse "${revision:-HEAD}"); then
        heads[$pkg]=$head
    else
        printf >&2 '%s: %s: revision %s not found\n' "$argv0" "$pkg" "${revision:-HEAD}"
        exit 2
    fi

    unset path_args
    if (( prefix )); then
        path_args=(--src-prefix="$pkg/" --dst-prefix="$pkg/")
    fi

    if [[ -f $AUR_VIEW_DB/$pkg ]] && read -r view < "$AUR_VIEW_DB/$pkg"; then
        # Check if hash points to an existing object
        if ! git cat-file -e "$view"; then
            printf >&2 '%s: %s: invalid git revision\n' "$argv0" "$AUR_VIEW_DB/$pkg"
            printf >&2 '%s: %s: falling back to empty tree\n' "$argv0" "$AUR_VIEW_DB/$pkg" 
            view=$(git hash-object -t tree /dev/null)
        fi

        if [[ $view != "$head" ]]; then
            git --no-pager "$log_fmt" --stat "${log_args[@]}" "${path_args[@]}" \
                "$view..$head" -- "${excludes[@]}" > "$tmp/$pkg.$log_fmt"
        fi

    elif [[ -f $AUR_VIEW_DB/$pkg ]]; then
        printf >&2 '%s: %s: failed to read revision\n' "$argv0" "$pkg"
        exit 1
    fi
done
unset -f git

# Begin file inspection
if [[ -v AUR_PAGER ]]; then
    # shellcheck disable=SC2086
    command -- $AUR_PAGER "$tmp"

    # Use an additional prompt for file managers with no support
    # for exit codes greater 0 (#673)
    if [[ -v AUR_CONFIRM_PAGER ]] && (( AUR_CONFIRM_PAGER )); then
        read -rp $'Press Return to continue or Ctrl+d to abort\n'
    fi

elif type -P vifm >/dev/null; then
    # Show hidden directories (.git)
    shopt -s dotglob

    { # Print patch files
      for f in "$tmp"/*; do
          [[ -f $f ]] && printf '%s\0' "$f"
      done

      # Print build directories (non-empty) in dependency order
      for d in "$tmp"/*/; do
          printf '%s\0' "$d" "$d"/*
      done
    # Avoid directory prefix in printed paths (#452)
    } | env -C "$tmp" vifm -c 'set vifminfo=' -c 'view!' -c '0' -

else
    printf >&2 '%s: no viewer found, please install vifm or set AUR_PAGER\n' "$argv0"
    exit 1
fi

# Update gitsums (viewer exited successfully)
for pkg in "${packages[@]}"; do
    printf '%s\n' "${heads[$pkg]}" > "$AUR_VIEW_DB/$pkg"
done
