#!/usr/bin/env bash
export GUILE_AUTO_COMPILE=0

if [ -z "$srcdir" ]; then
    echo >&2 "the \`srcdir' environment variable needs to be set to this script's location"
    exit 1
fi

source ${srcdir}/test-util

# run `scribbu popm ...'
# usage: run_popm var_to_rcv_stdout var_to_rcv_stderr var_to_rcv_status args...
# for instance:
#     run_popm out err stat -t 1 --playcount-only
# will execute `scribbu popm -t 1 --playcount-only'; on return the process'
# stdout will be in `out', stderr in `err', and exit status in `stat'. Note
# that due to quoting issues, any `'' or ``' characters will have been
# replaced with `:'.
function run_popm()
{
    local __stdout=$1
    local __stderr=$2
    local __status=$3
    shift 3

    verbose `pwd`
    verbose ${popm_scribbu_dir}/scribbu -L ${srcdir}/../scheme popm $@
    # Cf. https://stackoverflow.com/questions/11027679/store-capture-stdout-and-stderr-in-different-variables-bash
    local t_std
    local t_err
    local t_stat
    eval "$(${popm_scribbu_dir}/scribbu -L ${srcdir}/../scheme popm $@ \
    2> >(t_err=$(cat); typeset -p t_err) \
     > >(t_std=$(cat); typeset -p t_std) ); t_stat=$?"

    eval "$__stdout='$(echo $t_std|tr \' :|tr \` :)'"
    eval "$__stderr='$(echo $t_err|tr \' :|tr \` :)'"
    eval "$__status='$t_stat'"

    verbose $t_stat
    printf '=%.0s' $(seq 1 $popm_cols) >&3
    verbose "$t_std"
    printf '=%.0s' $(seq 1 $popm_cols) >&3
	echo "$t_err"
    printf '=%.0s' $(seq 1 $popm_cols) >&3
}

###########################################################################
#                                   tests                                 #
###########################################################################

# "smoke tests"-- if these don't pass, something's very wrong
function smoke_tests()
{
    local out
    local err
    local stat

    # can we get a help message?
    run_popm out err stat -h

    [ $stat -eq 0 ] || fail "smoke_tests: non-zero status for -h"
    [ -z "$err" ] || fail "smoke_tests: non-nil stderr for -h"
    [ -n "$out" ] || fail "smoke_tests: empty stdout for -h"

    here=`pwd`
    set_testdir ${srcdir} $popm_testdir smoke_tests id3v2.3.tag

    run_popm out err stat id3v2.3.tag
    [ $stat -eq 0 ] || fail "smoke_tests: non-zero status on create"

    # should be a nop, since this tag has no PCNT frame to begin with
    ${popm_scribbu_dir}/scribbu dump id3v2.3.tag|grep -F PCNT
    stat=$?
    [ $stat -ne 0 ] || fail "smoke_tests: should not have PCNT frame"

    run_popm out err stat --dry-run --create-frame id3v2.3.tag
    [ $stat -eq 0 ] || fail "smoke_tests: non-zero status on dry-run"
    echo $out|grep -F "Creating a new PCNT frame with a play count of 1.">/dev/null
    stat=$?
    [ $stat -eq 0 ] || fail "smoke_tests: failed to find PCNT (dry-run)"

    # run it again-- should be there now
    run_popm out err stat --create-frame id3v2.3.tag
    [ $stat -eq 0 ] || fail "smoke_tests: non-zero exit status on create"
    ${popm_scribbu_dir}/scribbu dump id3v2.3.tag|grep -F PCNT
    stat=$?
    [ $stat -eq 0 ] || fail "smoke_tests: failed to find PCNT"

    run_popm out err stat id3v2.3.tag
    pcnt=$(${popm_scribbu_dir}/scribbu dump id3v2.3.tag|grep -F PCNT|awk '{print $2}')
    [ $pcnt = "2" ] || fail "smoke_tests: PCNT is $pcnt; should be 2"

    run_popm out err stat --popularimeter-only -f -o "sp1ff@pobox.com" -r 192 id3v2.3.tag
    popm=$(${popm_scribbu_dir}/scribbu dump id3v2.3.tag|grep -F POPM|awk '{print $2}')
    [ "$popm" = "sp1ff@pobox.com" ] || fail "smoke_tests: no POPM"
    rating=$(${popm_scribbu_dir}/scribbu dump id3v2.3.tag|grep -F rating|awk '{print $2}')
    [ "$rating" = "192" ] || fail "smoke_tests: rating is $rating; should be 192"
    pcnt=$(${popm_scribbu_dir}/scribbu dump id3v2.3.tag|grep -F counter|awk '{print $2}')
    [ "$pcnt" = "02" ] || fail "smoke_tests: PCNT is $pcnt; should be 02 (2)"

    run_popm out err stat --count=11 id3v2.3.tag
    [ $stat -eq 0 ] || fail "smoke_tests: non-zero status on pc => 11"
    pcnt=$(${popm_scribbu_dir}/scribbu dump id3v2.3.tag|grep -F PCNT|awk '{print $2}')
    [ $pcnt = "11" ] || fail "smoke_tests: PCNT is $pcnt; should be 11"
    cnt=$(${popm_scribbu_dir}/scribbu dump id3v2.3.tag|grep -F counter|awk '{print $2}')
    [ $cnt = "0b" ] || fail "smoke_tests: POPM counter is $cnt; should be 0x0b"

    run_popm out err stat --rating=200 id3v2.3.tag
    [ $stat -eq 0 ] || fail "smoke_tests: non-zero status on rating"
    rating=$(${popm_scribbu_dir}/scribbu dump id3v2.3.tag|grep -F rating|awk '{print $2}')
    [ "$rating" = "200" ] || fail "smoke_tests: rating is $rating; should be 200"

    run_popm out err stat --rating=x id3v2.3.tag
    [ $stat -eq 0 ] || fail "smoke_tests: non-zero status on star rating"
    rating=$(${popm_scribbu_dir}/scribbu dump id3v2.3.tag|grep -F rating|awk '{print $2}')
    [ "$rating" = "1" ] || fail "smoke_tests: rating is $rating; should be 1"

    # Let's test recursion
    mkdir -p sub1/sub2 || fail "smoke_tests: failed to make test sub-directories"
    cp  id3v2.3.tag sub1/sub2 || fail "smoke_tests: failed to copy test file"

    run_popm out err stat --rating=##### id3v2.3.tag sub1
    test $stat -eq 0 || fail "smoke_tests: non-zero status on star rating (2)"
    rating=$(${popm_scribbu_dir}/scribbu dump id3v2.3.tag|grep -F rating|awk '{print $2}')
    test "$rating" = "255" || fail "smoke_tests: rating is $rating; should be 255"
    rating=$(${popm_scribbu_dir}/scribbu dump sub1/sub2/id3v2.3.tag|grep -F rating|awk '{print $2}')
    test "$rating" = "255" || fail "smoke_tests: rating is $rating; should be 255 (2)"

    clean_testdir $here $popm_testdir smoke_tests

}

# Exercise https://github.com/sp1ff/scribbu/issues/1
function test_issue_1
{
    local out
    local err
    local stat

    case `uname` in
        Linux)
            sed_regex="-r";;
        Darwin)
            sed_regex="-E";;
        *)
            echo "Unknown system" 2>&1;
            exit 1;;
    esac

    here=`pwd`
    set_testdir ${srcdir} $popm_testdir test_issue_1 id3v2.3.tag

    run_popm out err stat --create-frame --owner=sp1ff@pobox.com --rating=255 id3v2.3.tag
    [ $stat -eq 0 ] || fail "test_issue_1: non-zero exit status on create"

    ${popm_scribbu_dir}/scribbu dump id3v2.3.tag|grep -F PCNT
    [ $stat -eq 0 ] || fail "test_issue_1: should have PCNT frame"

    ${popm_scribbu_dir}/scribbu dump id3v2.3.tag|grep -F POPM
    [ $stat -eq 0 ] || fail "test_issue_1: should have POPM frame"

    run_popm out err stat id3v2.3.tag
    [ $stat -eq 0 ] || fail "test_issue_1: non-zero exit status on update"

    pcnt=`${popm_scribbu_dir}/scribbu dump id3v2.3.tag|grep -F PCNT|sed ${sed_regex} 's/PCNT: ([0-9]+)/\1/'`
    test "$pcnt" == "2" || fail "test_issue_1: PCNT should be two, got $pcnt"

    pcnt=`${popm_scribbu_dir}/scribbu dump id3v2.3.tag|grep -F counter|sed ${sed_regex} 's/counter: ([0-9]+)/\1/'`
    test "$pcnt" == "02" || fail "test_issue_1: POPM counter should be two, got $pcnt"

    clean_testdir $here $popm_testdir test_issue_1
}

# Make sure that when creating a PCNT or a POPM frame, when a such a frame is
# already present, picks up the play count from the extant frame.
function test_extant_frames()
{
    local out
    local err
    local stat

    case `uname` in
        Linux)
            sed_regex="-r";;
        Darwin)
            sed_regex="-E";;
        *)
            echo "Unknown system" 2>&1;
            exit 1;;
    esac

    here=`pwd`
    set_testdir ${srcdir} $popm_testdir test_extant_frames cerulean.mp3

    # This file has a POPM frame with a play count of zero-- bump that.
    run_popm out err stat cerulean.mp3
    test $stat -eq 0 || fail "test_extant_frames: failed to increment counter"
    pcnt=`${popm_scribbu_dir}/scribbu dump cerulean.mp3|grep -F counter|sed ${sed_regex} 's/counter: ([0-9]+)/\1/'`
    test "$pcnt" == "01" || fail "test_extant_frames: POPM counter should be one, got $pcnt"

    # Now create a PCNT frame, see if it picks up the counter from the existing
    # POPM
    run_popm out err stat --create-frame --owner=sp1ff@pobox.com --rating=255 cerulean.mp3
    test $stat -eq 0 || fail "test_extant_frames: failed to create POPM"
    pcnt=`${popm_scribbu_dir}/scribbu dump cerulean.mp3|grep -F PCNT|sed ${sed_regex} 's/PCNT: ([0-9]+)/\1/'`
    test "$pcnt" == "1" || fail "test_extant_frames: PCNT should be one, got $pcnt"

    clean_testdir $here $popm_testdir test_extant_frames
}

# Test my fix to a bug I found found while running `mppopmd' & a file with just
# an ID3v1 tag
function test_id3v1_only
{
    local out
    local err
    local stat

    case `uname` in
        Linux)
            sed_regex="-r";;
        Darwin)
            sed_regex="-E";;
        *)
            echo "Unknown system" 2>&1;
            exit 1;;
    esac

    here=`pwd`
    set_testdir ${srcdir} $popm_testdir test_id3v1_only v1-only.mp3

    run_popm out err stat -a -f -b -o sp1ff@pobox.com -r 196 v1-only.mp3
    test $stat -eq 0 || fail "test_id3v1_only: failed to set rating"

    rating=$(${popm_scribbu_dir}/scribbu dump v1-only.mp3|grep -F rating|awk '{print $2}')
    [ "$rating" = "196" ] || fail "test_id3v1_only: rating is $rating; should be 196"

    clean_testdir $here $popm_testdir test_id3v1_only
}

# Test another bug I found with `mppopmd'
function test_rate_then_count
{
    local out
    local err
    local stat

    case `uname` in
        Linux)
            sed_regex="-r";;
        Darwin)
            sed_regex="-E";;
        *)
            echo "Unknown system" 2>&1;
            exit 1;;
    esac

    here=`pwd`
    set_testdir ${srcdir} $popm_testdir test_rate_then_count life.mp3

    run_popm out err stat -a -f -b -o sp1ff@pobox.com -r 196 life.mp3
    test $stat -eq 0 || fail "test_rate_then_count: failed to set rating"
    rating=$(${popm_scribbu_dir}/scribbu dump life.mp3|grep -F rating|awk '{print $2}')
    [ "$rating" = "196" ] || fail "test_rate_then_count: rating is $rating; should be 196"

    # Now set the playcount to 1
    run_popm out err stat -a -f -b -o sp1ff@pobox.com -C 1 life.mp3
    rating=$(${popm_scribbu_dir}/scribbu dump life.mp3|grep -F rating|awk '{print $2}')
    [ "$rating" = "196" ] || fail "test_rate_then_count: rating is $rating; should be 196 (2)"

    clean_testdir $here $popm_testdir test_rate_then_count
}

# Exercise https://github.com/sp1ff/scribbu/issues/5
function test_issue_5
{
    local out
    local err
    local stat

    case `uname` in
        Linux)
            sed_regex="-r";;
        Darwin)
            sed_regex="-E";;
        *)
            echo "Unknown system" 2>&1;
            exit 1;;
    esac

    here=`pwd`
    set_testdir ${srcdir} $popm_testdir test_issue_5 id3v2.3.tag

    # id3v2.3.tag has no PCNT frame-- try to add one with a non-zero value
    run_popm out err stat -a -f -b -o sp1ff@pobox.com -C 11 id3v2.3.tag
    test $stat -eq 0 || fail "test_issue_5: non-zero exit status on create"

    pcnt=`${popm_scribbu_dir}/scribbu dump id3v2.3.tag|grep -F PCNT|sed ${sed_regex} 's/PCNT: ([0-9]+)/\1/'`
    test "$pcnt" == "11" || fail "test_issue_5: PCNT should be eleven, got $pcnt"

    clean_testdir $here $popm_testdir test_issue_1
}

###########################################################################
# parse options; `make check' will pass none, so be sure no options means
# execute test suite normally
###########################################################################

popm_builddir=../src
popm_cols=$(tput cols)
popm_debug=false
popm_help=false
popm_nargs=0
popm_scribbu_dir=../src
popm_testdir=./test-popm-tmp
popm_verbose=false
popm_prev_option=
for popm_option
do
    # If the previous option required an argument, assign it now.
    if test -n "$popm_prev_option"; then
        eval $popm_prev_option=\$popm_option
        popm_prev_option=
        popm_nargs=$(($popm_nargs+1))
        continue
    fi

    # Parse out the argument option in $popm_option, if present.
    case $popm_option in
        *=?*) popm_optarg=$(expr "X$popm_option" : '[^=]*=\(.*\)') ;;
        *=)   popm_optarg= ;;
        *)    popm_optarg=yes ;;
    esac

    # Options are handled here:
    case $popm_option in
        -d | --de | --deb | --debu | --debug)
            popm_debug=true;;
        -h | --he | --hel | --help)
            popm_help=yes;;
        -v | --ve | --ver | --verb | --verbo | --verbose)
            popm_verbose=true;;
        *)
            cat >&2 <<EOF
Unrecognized option \`$popm_option'.
Try \`$0 --help' for more information.
EOF
            exit 2;;
    esac

done

if test "$popm_help" == "true"; then
    cat <<EOF
\`$0' -- test the \`popm' sub-command

TODO(sp1ff): write me!
EOF
fi

if test "$popm_verbose" == "true"; then
    exec 3>&1
else
    exec 3> /dev/null
fi

if test "$popm_debug" == "true"; then
    set -x
fi

# Both must be absolute in order for `make-distcheck' to work
popm_scribbu_dir=`cd "$popm_scribbu_dir"; pwd`
[ -d "$popm_testdir" ] || mkdir -p "$popm_testdir"
popm_testdir=`cd "$popm_testdir"; pwd`
smoke_tests
test_issue_1
test_extant_frames
test_id3v1_only
test_rate_then_count
test_issue_5
