#! /usr/bin/env bash
#
# Executes a command and formats the command and its stdout in reST.
#

trap cleanup INT TERM EXIT

function usage() {
    echo
    echo "$(basename "$0") [options] <command line>"
    echo
    echo "    -d          Do not actually execute command; just format the command line."
    echo "    -h          Show this help."
    echo "    -r <file>   Insert <file> into output, rather than stdout."
    echo "    -o          Do not include command into output."
    echo "    -c <cmd>    Show <cmd> in output instead of the one actually executed."
    echo "    -f <filter> Run <filter> command on command output (or file) before including."
    echo "    -n <n>      Include only n lines of output, adding a [...] marker if there's more."
    echo
    exit 1
}

function apply_filter() {
    eval "$filter_env" | eval "$filter_opt"
}

# Strip leading white-space and then indent to 6 space.
function indent() {
    python3 -c "
from __future__ import print_function
import sys
input = sys.stdin.readlines()
n = 1e10
for i in input:
    n = min(n, len(i) - len(i.lstrip()))

for i in input:
    print('      ' + i[n:], end='')
"
}

function cleanup() {
    # shellcheck disable=SC2086
    rm -f $tmps
    exit
}

stdout=$(mktemp -t "$(basename "$0")".XXXXXX)
cmd_out=$(mktemp -t "$(basename "$0")".XXXXXX)
filter_out=$(mktemp -t "$(basename "$0")".XXXXXX)
tmps="$stdout $cmd_out $filter_out"

include=$cmd_out
show_command=1
cmd_display=""
dry=0
lines=0

filter_env=${BTEST_RST_FILTER}

while getopts "odhr:f:c:n:" opt; do
    case $opt in
        h) usage ;;
        o) show_command=0 ;;
        r) include=$OPTARG ;;
        d)
            dry=1
            include=""
            ;;
        c) cmd_display=$OPTARG ;;
        f) filter_opt=$OPTARG ;;
        n) lines=$OPTARG ;;
        *) exit 1 ;;
    esac
done

shift $((OPTIND - 1))

cmd=$*
test "$cmd_display" == "" && cmd_display=$cmd
test "$filter_opt" == "" && filter_opt="cat"
test "$filter_env" == "" && filter_env="cat"

test "$cmd" == "" && usage

if [ "$dry" != "1" ]; then
    if ! eval "$cmd" >"$cmd_out"; then
        exit 1
    fi
fi

# Generate reST output.

if [ "$show_command" == "1" ]; then
    {
        echo ".. rst-class:: btest-cmd"
        echo
        echo "    .. code-block:: none"
        echo "      :linenos:"
        echo "      :emphasize-lines: 1,1"
        echo
        echo "      # $cmd_display" | apply_filter
    } >>"$stdout"
else
    {
        echo ".. rst-class:: btest-include"
        echo
        echo "    .. code-block:: guess"
        echo "      :linenos:"
        echo
    } >>"$stdout"
fi

for i in $include; do
    echo "    $(basename "$i")" >>"$filter_out"
    echo "" >>"$filter_out"
    cat "$i" | apply_filter | indent >"$filter_out"

    if [ "$lines" = 0 ]; then
        cat "$filter_out" >>"$stdout"
    else
        cat "$filter_out" | head -n "$lines" >>"$stdout"
        if [ "$(wc -l <"$filter_out")" -gt "$lines" ]; then
            echo '      [...]' >>"$stdout"
        fi
    fi

    rm -f "$filter_out"
done

echo >>"$stdout"

# Branch depending on where this script was started from.

if [ "$BTEST_RST_OUTPUT" != "" ]; then
    # Running from inside Sphinx, just output to where it tells us.
    cat "$stdout" >>"${BTEST_RST_OUTPUT}#${TEST_PART}"

elif [ "$TEST_NAME" ]; then
    # Running from inside BTest, output into file that btest-diff-rst will pickup.
    cat "$stdout" >>"btest-${TEST_NAME}#${TEST_PART}"

else
    # Running from command line, just print out.
    cat "$stdout"
fi
