#!/bin/bash

program="$( basename "$1" )"
scriptdir="$( dirname "$1" )"
exe="./tools/$2"
old="$3"
shift 3

# Arguments
justrun=
save=
verbose=
pattern=*
folderpattern=*

# Calculated values change in each iteration
current=
results=

# Don't change per iteration
tmpfile="test/check"
tmperr="test/err"
testdir="test/tests"
maxlength=0
oneline=0
files=()
folders=()
temp=()
errored=0
dropadj=1

# Formatting values
bold=`tput bold`
normal=`tput sgr0`
uline=`tput smul`
green=`tput setaf 2`
red=`tput setaf 1`
blue=`tput setaf 4`
backblue=`tput setab 4`

function errWith {
  echo "$1" >&2
  exit 1
}

function execerror {
  echo "${bold}${uline}${red}ERROR${normal} $1"
  errored=1
}

function dots {
  local len=`echo "$current" | wc -c`
  for i in `seq $len $maxlength` ; do
    echo -n '.'
  done
  echo -n ' '
}

function contains {
  local elem
  for elem in "${@:2}" ; do
    test "$elem" = "$1" && return 0
  done
  return 1
}

function dropdirprefix {
  echo "$1" | cut -c $(( ${#2} + $dropadj ))-
}

function setdropadj {
  local result=$( dropdirprefix "/dev/null" "/dev/" )
  local null="null"
  dropadj=$(( dropadj + (${#null} - ${#result}) ))
}

function show_standard {
  echo "${red}Standard -- START${normal}"
  cat "$results"
  echo "${red}Stadard -- END${normal}"
}

function testit {
  local testing="${bold}Testing:${normal} ${uline}${current}${normal}"
  test "$oneline" -eq 0 && echo "$testing"
  test "$oneline" -ne 0 && echo -n "$testing"
  test "$oneline" -ne 0 && dots
  test -n "$verbose" && cat "$1"
  if [ -n "$justrun" ] ; then
    cat "$1" | "$exe"
    return 0
  fi
  cat "$1" | "$exe" 1> "$tmpfile" 2> "$tmperr"
  if [ $? -ne 0 ] ; then
    execerror "Error testing $program with $current"
    cat "$tmperr"
  elif [ -n "$save" ] ; then
    echo "${bold}Saving${normal} $current"
    mkdir -p $( dirname "$results" )
    mv "$tmpfile" "$results"
  elif [ ! -e "$results" ] ; then
    execerror "Cannot check results -- standard does not exist"
  else
    if [ -n "$verbose" ] ; then
      echo -n "${bold}Output:${normal} "
      cat "$tmpfile"
    fi
    test "$oneline" -eq 0 && echo -n "${bold}Results:${normal} "
    diff -q "$tmpfile" "$results" &> /dev/null
    if [ $? -eq 0 ] ; then
      echo "${bold}${green}PASS${normal}"
    else
      echo "${bold}${red}MISMATCH${normal}"
      test -n "$verbose" && show_standard
    fi
  fi

  test -e "$tmpfile" && rm "$tmpfile"  # Sometimes happens
  test -e "$tmperr" && rm "$tmperr"    # Always happens

  test "$oneline" -eq 0 && echo ""
}

function listandexit {
  for afile in $( find "$testdir" -type f -name "$pattern" ) ; do
    current=$( dropdirprefix "$afile" "$testdir" )
    echo "$current"
  done
  exit 0
}

function usage {
cat <<USAGE
$program -[chlpsv]
  -f pattern
     Filter meta-folders by pattern

  -h
     Display this help

  -l
     Display the name of all tests; note that pattern can be used

  -p pattern
     Filter tests to be used based on pattern (as in find -name)

  -R
     merely run the driving exe and output the result to stdout (no checking anything)

  -s
     save results

  -v
     verbose output
USAGE
  exit 0
}

setdropadj

while getopts "f:hlRsvp:" OPTION ; do
  case "$OPTION" in
    f) folderpattern=$OPTARG ;;
    h) usage ;;
    R) justrun=1 ;;
    s) save=1 ;;
    v) verbose=1 ;;
    p) pattern=$OPTARG ;;
    l) list=1;;
    ?) errWith "Unknown option; aborting" ;;
  esac
done
shift $(($OPTIND - 1))

test -n "$list" && listandexit

test -e "$exe" || errWith "Testing $program but $exe unavailable"
test -f "$exe" || errWith "Testing $program but $exe is not a file"
test -x "$exe" || errWith "Testing $program but $exe unexecutable"

test -z "$verbose" && oneline=1

for adir in $( find "$testdir" -mindepth 1 -maxdepth 1 -type d -name "$folderpattern" ) ; do
  adir=$( dropdirprefix "$adir" "$testdir/" )
  folders+=( "$adir" )
done
test "${#folders[@]}" -eq 0 && errWith "No folders in test directory. Good-bye."

for afolder in "${folders[@]}" ; do
  test -d "$testdir/$afolder" || errWith "$afolder is not a directory ($testdir)"
done

for afile in $( find "$testdir/${folders[0]}" -type f -name "$pattern" ) ; do
  test "README" = $( basename "$afile" ) || files+=( $( dropdirprefix "$afile" "$testdir/${folders[0]}/" ) )
done

for afolder in "${folders[@]}" ; do
  temp=()
  for afile in $( find "$testdir/$afolder" -type f -name "$pattern" ) ; do
    test "README" = $( basename "$afile" ) || temp+=( $( dropdirprefix "$afile" "$testdir/$afolder/" ) )
  done

  for afile in "${files[@]}" ; do
    contains "$afile" "${temp[@]}" || errWith "$afolder does not contain $afile but ${folders[0]} does"
  done
  for bfile in "${temp[@]}" ; do
    contains "$bfile" "${files[@]}" || errWith "$afolder contains $bfile but ${folders[0]} does not"
  done
done
test "${#files[@]}" -eq 0 && errWith "No files match the given pattern. Good-bye."

# All the test directories have the same structure.
for current in "${files[@]}" ; do
  len=`echo "$current" | wc -c`
  test $len -gt $maxlength && maxlength="$len"
done
maxlength=$(( maxlength + 5 ))

for afolder in "${folders[@]}" ; do
  echo "${bold}${blue}Testing:${normal} $afolder"
  for current in "${files[@]}" ; do
    results="test/$old/$afolder/$current"
    testit "$testdir/$afolder/$current"
  done
done

test $errored -eq 1 && exit 1
test -n "$justrun" && exit 0

# Ensure that all the results are the same.
for current in "${files[@]}" ; do
  master="test/$old/${folders[0]}/$current"
  matched=1

  for afolder in "${folders[@]}" ; do
    target="test/$old/$afolder/$current"
    diff -q "$master" "$target" &> /dev/null
    if [ $? -ne 0 ] ; then
      echo "$current ${bold}${red}DIFFERS${normal} between ${folders[0]} (reference) and $afolder"
      matched=0
    fi
  done
  test $matched -eq 1 && echo "$current ${bold}${green}MATCHES${normal} across all folders"
done
