#!/bin/sh
#-*-mode:  sh -*-

# Copyright (c) 2008-2015, Aleksey Cheusov <vle@gmx.net>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above
#       copyright notice, this list of conditions and the following
#       disclaimer in the documentation and/or other materials provided
#       with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

. pipestatus

export LC_ALL=C

LIBEXECDIR=${LIBEXECDIR-/usr/pkg/libexec/psu}
. ${LIBEXECDIR}/sig_handler.sh
on_exit () {
	# Stupid test for stupid Solaris
	if test -n "$tmp_dir"; then rm -rf $tmp_dir; fi
}

PKGDIG_SYSCONFDIR=${PKGDIG_SYSCONFDIR-/usr/pkg/etc}

systemwide_conf="$PKGDIG_SYSCONFDIR/pkg_digger.conf"
user_conf="$HOME/.pkg_digger"

if test -r "$user_conf"; then
    . "$user_conf"
elif test -r "$systemwide_conf"; then
    . "$systemwide_conf"
fi

set -e

usage () {
cat <<EOF
usage: pkg_digger -h
       pkg_digger -f
       pkg_digger -s
       pkg_digger [-1|-3-9|-i] [-r] [-q] query1 [query2 ...]
   where 'queryX' is either plain text query or in 'f:s:q' format,
   'f' here means field name, 's' - search strategy and
   'q' means plain text query
OPTIONS:
   -h         display this screen
   -s         display available search strategies
   -f         display available fields for search in
   -1         display 1-line information about packages (default)
   -3         display short information about packages
   -i|-9      display full information about packages
   -r         raw output in pkg_summary(5) format
   -q         quiet mode, do not print "No matches found" to stderr
EOF
}

info_type=-1 # the default
while getopts sfi931rqh f; do
    case "$f" in
        s)    strats=1;;
        f)    fields=1;;
        i|9)  info_type=-9;;
        3)    info_type=-3;;
        1)    info_type=-1;;
        r)    raw_out='-r';;
        q)    quiet_mode='-q';;
        h)    usage; exit 0;;
	?)    printf "Run pkg_digger -h for details\n"; exit 2;;
    esac
done
shift `expr $OPTIND - 1`

# temp dir
tmp_dir=`mktemp -d ${TMPDIR-/tmp}/pkg_digger.XXXXXX`

# highligting defaults
invert_def_loop (){
    while read def; do
	synonym=`printf '%s\n' "$def" | sed -e 's,^.*__,,' -e 's,=.*,,'`
	fullname=`printf '%s\n' "$def" | sed -e 's,^[^=]*=,,' -e 's,[.],dot,'`
	echo pkg_digger_definv_$fullname=$synonym
    done
}

invert_defaults (){
    # $1 - field or strat
    runpipe_re '0 [01] 0' \
	set '|' \
	grep ^pkg_digger_$1__ '|' \
	invert_def_loop
}

######################################################################
# -s
highligh_strat_def_loop (){
    while read strat comment; do
	cmd="printf '%7s   %10s   %s\\n' \"\$pkg_digger_definv_$strat\" \ '$strat' '$comment'"
	eval "$cmd"
    done
}

highligh_strat_def (){
    eval `invert_defaults strat`
    highligh_strat_def_loop
}

if test $strats; then
    echo 'synonym | full name  | description'
    echo '--------------------------------------'
    runpipe0 $PKG_DIGGER_BACKEND -s '|' highligh_strat_def
fi

######################################################################
# -f
highligh_field_def_loop (){
    while read field; do
	cmd="printf '%7s   %s\\n' \"\$pkg_digger_definv_$field\" '$field'"
	eval "$cmd"
    done
}

highligh_field_def (){
    eval `invert_defaults field`
    highligh_field_def_loop
}

if test $fields; then
    echo 'synonym | full name'
    echo '--------------------------'
    runpipe0 $PKG_DIGGER_BACKEND -f '|' highligh_field_def
fi

######################################################################
# -f|-s
if test -n "$fields$strats"; then
    exit 0
fi

######################################################################
#
if test $# -eq 0; then
    usage
    exit 1
fi

# the following code is for searching
#
grep_count (){
    # args: $1 - count
    awk '$1 == '"$1"' {print $2}'
}

#
intersect (){
    # args: $1 - count
    runpipe0 sort $tmp_dir/with_*.txt '|' uniq -c '|' \
	grep_count "$1"
}

#
subtract (){
    # args: $1 - minuend
    awk -v minuend="$1" '
FILENAME == minuend {
   if (!($0 in without)){
      print $0
   }
   next
}
{
   without [$0] = 1
}
' $tmp_dir/without.txt "$1"
}

# search images...
run_backend__fsq (){
    field=`printf '%s\n' "$1" | cut -d: -f1`
    strat=`printf '%s\n' "$1" | cut -d: -f2`
    query=`printf '%s\n' "$1" | cut -d: -f3`

    # short names for strategies
    if test -z "$strat"; then
	strat=empty
    fi

    synonym="pkg_digger_strat__$strat"
    set +e # workaround for buggy shells
    if eval test "\$$synonym"; then
	eval strat=\$$synonym
    fi
    set -e

    # short names for fields
    if test -z "$field"; then
	field=empty
    fi

    synonym="pkg_digger_field__$field"
    set +e # workaround for buggy shells
    if eval test "\$$synonym"; then
	eval field=\$$synonym
    fi
    set -e

    #
    $PKG_DIGGER_BACKEND $quiet_mode -- "$field:$strat:$query"
}

run_backend__fallbacks (){
    eval "$PKG_DIGGER_BACKEND $quiet_mode $query"
}

run_backend (){
    if printf '%s\n' "$1" | grep -q '^[^:]*:[^:]*:[^:]*$'; then
	run_backend__fsq "$1"
    else
	query=''
	for rule in $PKG_DIGGER_FALLBACK_RULES; do
	    query="$query '$rule:$1'"
	done

	run_backend__fallbacks
    fi
}

cnt_with=0
touch $tmp_dir/without.txt $tmp_dir/with_0.txt
for q in "$@"; do
    if printf '%s\n' "$q" | grep -q '^[!-]'; then
	res_fn=$tmp_dir/without.txt
    else
	res_fn=$tmp_dir/with_$cnt_with.txt
	cnt_with=$(($cnt_with+1))
    fi

    q=$(printf '%s\n' "$q" | sed 's/^[!-]//')

    if test "_$res_fn" = "_$tmp_dir/without.txt"; then
	# - pattern
	set +e
	run_backend "$q" >> $res_fn 2>/dev/null
	set -e
    else
	run_backend "$q" >> $res_fn
    fi
done

no_matches_found (){
    echo "No matches found" 1>&2
    exit 20
}

res_with_fn="$tmp_dir"/res_with
result_fn="$tmp_dir"/result

intersect "$cnt_with" > "$res_with_fn"
if ! test -s "$res_with_fn"; then
    no_matches_found
fi

subtract "$res_with_fn" > "$result_fn"
if ! test -s "$result_fn"; then
    no_matches_found
fi

wrapper (){
    awk -v cols="$COLUMNS" '
{
   if (length($0) >= cols)
      $0 = substr($0, 1, cols-1) ">"

   print $0
}
' "$@"
}

summary2onelineinfo (){
    runawk -v quiet_mode="$quiet_mode" -e '
        /^PKGNAME=/ {pkgbase = substr($0, 9); sub(/-[^-]*$/, "", pkgbase); next}
        /^PKGPATH=/ {pkgpath = substr($0, 9); next}
        /^COMMENT=/ {comment = substr($0, 9); next}
        NF == 0 {
            pos = index(pkgpath, "/")
            if (substr(pkgpath, pos+1) == pkgbase)
                printf "%-25s - %s\n", pkgpath, comment
            else
                printf "%-25s - %s\n", pkgpath "(" pkgbase ")", comment
        }' "$@"
}

run_pkg_digger_backend (){
    $PKG_DIGGER_BACKEND $quiet_mode $info_type -- `cat $result_fn` |
    if test -n "$raw_out"; then
	cat
    elif test "_$info_type" = _-1; then
	summary2onelineinfo
    else
	pkg_summary4view
    fi
}

if test -t 1 -a -n "$COLUMNS"; then
    run_pkg_digger_backend | wrapper
else
    run_pkg_digger_backend
fi
