#!/bin/bash
# aur-search - search for AUR packages
[[ -v AUR_DEBUG ]] && set -o xtrace
argv0=search
AUR_LOCATION=${AUR_LOCATION:-'https://aur.archlinux.org'}
PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'

# default options
multiple=section output=hrm search_by=name-desc sort_key=Name type=search

tabulate() {
    # It is important to have a default value for every field, or
    # parsing of the @tsv result might break.
    # See: https://lists.gnu.org/archive/html/help-bash/2016-05/msg00041.html
    jq -r --arg key "$1" '
        def sel_join:
            select (length > 0) // ["-"] | join(" ");

        [.results[]] | sort_by(.[$key])[] | [
            .Name        // "-",
            .PackageBase // "-",
            .Version     // "-",
            .Description // "-",
            .URL         // "-",

            (.Keywords | sel_join),
            (.License  | sel_join),

            .Maintainer  // "-",
            .NumVotes    // "-",
            .Popularity  // "-",
            .OutOfDate   // "-",

            (.FirstSubmitted | todate),
            (.LastModified   | todate),
            (.Depends        | sel_join),
            (.MakeDepends    | sel_join),
            (.CheckDepends   | sel_join)
        ] | @tsv'
}


info_long() {
    local -a desc=(
        'Name'
        'Base'
        'Version'
        'Description'
        'URL'
        'Keywords'
        'License'
        'Maintainer'
        'Votes'
        'Popularity'
        'Out Of Date'
        'Submitted'
        'Last Modified'
        'Depends On'
        'Makedepends'
        'Checkdepends'
    )
    local -a info

    while IFS=$'\t' read -r -a info; do
        # XXX: AUR_LOCATION only used here
        printf "$BOLD%s:$ALL_OFF\\t%s\\n" "AUR URL" "$AUR_LOCATION/packages/${info[0]}"

        for i in "${!info[@]}"; do
            printf "$BOLD%s:$ALL_OFF\\t%s\\n" "${desc[i]}" "${info[i]}"
        done

        # column(1) ignores empty lines (package delimitation)
        printf '%s\n' '-'
    done
}

info_short() {
    local Name Version NumVotes Popularity Maintainer OutOfDate Description

    while IFS=$'\t' read -r Name _ Version Description _ _ _ Maintainer NumVotes Popularity OutOfDate _; do
        case $OutOfDate in
            -) unset OutOfDate ;;
            *) # FIXME move date command to jq (must only be run if OutOfDate is set)
               OutOfDate="(Out-of-date: $(date -d @"$OutOfDate" '+%d %B %Y'))" ;;
        esac

        case $Maintainer in
            -) Maintainer='(Orphaned) ' ;;
            *) unset Maintainer ;;
        esac

        # Unset LC_ALL to ensure it doesn't override LC_NUMERIC.
        LC_ALL='' LC_NUMERIC=C printf -v Popularity '%.2f' "$Popularity"

        printf "${BLUE}aur/${ALL_OFF}${BOLD}%s ${GREEN}%s ${ALL_OFF}(+%s %s%%) ${RED}%s%s${ALL_OFF}\\n    %s\\n" \
               "$Name" "$Version" "$NumVotes" "$Popularity" "$Maintainer" "$OutOfDate" "$Description"
    done
}

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 [-adimnqrsv] [-k key] pkgname...' "$argv0"
    exit 1
}

source /usr/share/makepkg/util/message.sh
source /usr/share/makepkg/util/parseopts.sh

if [[ ! -v NO_COLOR ]] && [[ ! -v AUR_DEBUG ]]; then
    # We use colored messages on both stdout and stderr, and these may
    # be desired even if stdout is not connected to a terminal. (in
    # particular, aur search foo | less -R, #585)
    colorize
fi

opt_short='k:adimnqrsv'
opt_long=('any' 'info' 'search' 'desc' 'maintainer' 'name' 'depends' 'verbose'
          'makedepends' 'optdepends' 'checkdepends' 'key:' 'raw' 'short')
opt_hidden=('dump-options')

if ! parseopts "$opt_short" "${opt_long[@]}" "${opt_hidden[@]}" -- "$@"; then
    usage
fi
set -- "${OPTRET[@]}"

unset format
while true; do
    case "$1" in
        -a|--any)
            multiple=union ;;
        -i|--info)
            type=info ;;
        -s|--search)
            type=search ;;
        -d|--desc)
            search_by=name-desc ;;
        -m|--maintainer)
            search_by=maintainer ;;
        -n|--name)
            search_by=name ;;
        --depends)
            search_by=depends ;;
        --makedepends)
            search_by=makedepends ;;
        --optdepends)
            search_by=optdepends ;;
        --checkdepends)
            search_by=checkdepends ;;
        -q|--short)
            format=short ;;
        -v|--verbose)
            format=long ;;
        -r|--raw)
            output=raw ;;
        -k|--key)
            shift; sort_key=$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 ! (( $# )); then
    usage
fi

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

# XXX union is done by the AUR for info, but not search
if (( $# < 2 )) || [[ $type == 'info' ]]; then
    multiple=none
fi

# set format depending on query type (#319)
case $type in
      info) format=${format-long}  ;;
    search) format=${format-short} ;;
esac

# set filters (1)
case $format in
     long) info() { info_long | column -ts $'\t' | sed -E 's/^-//; $d'; } ;;
    short) info() { info_short; } ;;
esac

# set filters (2)
case $multiple in
    section) combine() { sort | uniq -d; } ;;
      union) combine() { sort | uniq -u; } ;;
esac

# check results
printf '%s\n' "$@" | aur query -t "$type" -b "$search_by" > "$tmp" || exit

# exit early on raw output (#187)
case $output in
    raw) cat "$tmp"
         exit ;;
esac

# check results (#257)
count=$(jq -r '.resultcount' "$tmp" | awk '{s += $1} END {print s}')
error=$(jq -r '.error' "$tmp")

case $count in
    0) if [[ $error != 'null' ]]; then
           error '%s' "$error"
           exit 2
       else
           # no results found
           exit 1
       fi ;;
    *) if [[ $multiple != 'none' ]]; then
           tabulate "$sort_key" < "$tmp" | combine | info
       else
           tabulate "$sort_key" < "$tmp" | info
       fi ;;
esac

# vim: set et sw=4 sts=4 ft=sh:
