#!/bin/sh
set -e

footer=true
header=true
legend=false
os_list="."
suite_list=""
suite_overview=false

dashdash=
previous_option=
for argument do
  if test -n "$previous_option"; then
    eval $previous_option=\$argument
    previous_option=
    continue
  fi

  case $argument in
  *=?*) parameter=$(expr "X$argument" : '[^=]*=\(.*\)' || true) ;;
  *=)   parameter= ;;
  *)    parameter=yes ;;
  esac

  case $dashdash$argument in
  --) dashdash=yes ;;
  --disable-footer) footer=true ;;
  --disable-header) header=false ;;
  --enable-legend) legend=true ;;
  --enable-suites-overview) suite_overview=true ;;
  --os-list=*) os_list=$parameter ;;
  --os-list) previous_option=os_list ;;
  --suite-list=*) suite_list=$parameter ;;
  --suite-list) previous_option=suite_list ;;
  -*) echo "$0: unrecognized option $argument" >&2
      exit 1 ;;
  *)
    if [ $operand = 1 ]; then
      input="$argument"
      operand=2
    elif [ $operand = 2 ]; then
      directory="$argument"
      operand=3
    else
      echo "$0: unexpected extra operand $argument" >&2
      exit 1
    fi
    ;;
  esac
done

if test -n "$previous_option"; then
  echo "$0: option '$argument' requires an argument" >&2
  exit 1
fi

export LC_COLLATE=C

check_result() {
  for expectation in "$1.expect/$2.unknown."*; do
    if [ -e "$expectation" ]; then
      if cmp -s "$expectation" "$3"; then
        rm -f "unused.$expectation"
        echo unknown
        return
      fi
    fi
  done
  for expectation in "$1.expect/$2."*; do
    if [ -e "$expectation" ]; then
      if cmp -s "$expectation" "$3"; then
        rm -f "unused.$expectation"
        echo good
        return
      fi
    fi
  done
  if [ "$(echo "$1.expect/$2."*)" = "$1.expect/$2.*" ]; then
    echo unrated
  else
    echo bad
  fi
}

if which sha256sum > /dev/null 2>&1; then
  checksum=sha256sum
elif which sha256 > /dev/null 2>&1; then
  checksum=sha256
elif which sha1 > /dev/null 2>&1; then
  checksum=sha1
elif which md5 > /dev/null 2>&1; then
  checksum=md5
else
  checksum=sha256sum
fi

if $header; then
  cat << \EOF
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style>
table.big-comparison {
	background-color: #EEEEEE;
	margin-left: auto;
	margin-right: auto;
	font-size: 90%;
}

table.big-comparison tr th {
	padding-left: 0.1em;
	padding-right: 0.1em;
	padding-top: 0.5em;
	padding-bottom: 0.5em;
	text-align: center;
}

table.big-comparison tr td {
	padding-left: 0.1em;
	padding-right: 0.1em;
	padding-top: 0.5em;
	padding-bottom: 0.5em;
	text-align: center;
}

.good {
	background-color: #80FF80;
}

.good-1 {
	background-color: #40BF80;
}

.good-2 {
	background-color: #80BF80;
}

.good-3 {
	background-color: #80BF40;
}

.good-4 {
	background-color: #40BF40;
}

.good-5 {
	background-color: #409F40;
}

.bad {
	background-color: #FF8080;
}

.bad-1 {
	background-color: #FF8080;
}

.bad-2 {
	background-color: #FF6080;
}

.bad-3 {
	background-color: #FF8060;
}

.bad-4 {
	background-color: #FF6060;
}

.bad-5 {
	background-color: #BF6060;
}

.unknown {
	background-color: #8080FF;
}

.unknown-1 {
	background-color: #8080BF;
}

.unknown-2 {
	background-color: #6080BF;
}

.unknown-3 {
	background-color: #8060BF;
}

.unknown-4 {
	background-color: #6060BF;
}

.unknown-5 {
	background-color: #60609F;
}

.unrated {
	background-color: #D0D0D0;
}

.unrated-1 {
	background-color: #C0D0D0;
}

.unrated-2 {
	background-color: #D0C0D0;
}

.unrated-3 {
	background-color: #D0D0C0;
}

.unrated-4 {
	background-color: #C0C0D0;
}

.unrated-5 {
	background-color: #D0C0C0;
}
    </style>
    <title>os-test</title>
  </head/>
  <body>
    <article>
      <h1>os-test</h1>
EOF
fi

if $legend; then
  cat << EOF
      <h2 id="legend"><a href="#legend">Legend</a></h2>
      <table class="big-comparison">
        <tr>
          <th></th>
          <th>Unanimous</th>
          <th>Result kind 1</th>
          <th>Result kind 2</th>
          <th>Result kind 3</th>
          <th>Result kind 4</th>
          <th>Result kind 5</th>
        </tr>
        <tr>
          <th>Good</th>
          <td class="good">Good unanimous</td>
          <td class="good-1">Good result kind 1</td>
          <td class="good-2">Good result kind 2</td>
          <td class="good-3">Good result kind 3</td>
          <td class="good-4">Good result kind 4</td>
          <td class="good-5">Good result kind 5</td>
        </tr>
        <tr>
          <th>Unknown</th>
          <td class="unknown">Unknown unanimous</td>
          <td class="unknown-1">Unknown result kind 1</td>
          <td class="unknown-2">Unknown result kind 2</td>
          <td class="unknown-3">Unknown result kind 3</td>
          <td class="unknown-4">Unknown result kind 4</td>
          <td class="unknown-5">Unknown result kind 5</td>
        </tr>
        <tr>
          <th>Bad</th>
          <td class="bad">Bad unanimous</td>
          <td class="bad-1">Bad result kind 1</td>
          <td class="bad-2">Bad result kind 2</td>
          <td class="bad-3">Bad result kind 3</td>
          <td class="bad-4">Bad result kind 4</td>
          <td class="bad-5">Bad result kind 5</td>
        </tr>
      </table>
      <p><b>Good.</b> A cell is good if its output belongs to the set of
         expected valid outputs for that test, as determined by the applicable
         standards, specifications, expected behavior, or the interpretation of
         the os-test authors. If all the good results in a row have the same
         output, all the good result cells are colored in the unanimous color.
         Otherwise, each different kind of good outcome is colored in an unique
         color.</p>
      <p><b>Unknown.</b> A cell is unknown if the output is known, but it has
         not yet been determined if the output is a good result. If all the
         unknown results in a row have the same output, all the unknown result
         cells are colored in the unanimous color. Otherwise, each different
         kind of unknown outcome is colored in an unique color.</p>
      <p><b>Bad.</b> A cell is bad if its output is neither good nor unknown. If
         all the bad results in a row have the same output, all the bad result
         cells are colored in the unanimous color. Otherwise, each different
         kind of bad outcome is colored in an unique color.</p>
      <p><b>§.</b> The § link on the left of each row links to that row.</p>
EOF
fi

if $suite_overview; then
  cat << EOF
      <h2 id="suites"><a href="#suites">Suites</a></h2>
      <p>os-test currently contains these suites:</p>
      <ul>
EOF
  for suite in $suite_list; do
    cat << EOF
        <li><a href="#$suite">$suite</a> - $(head -1 -- "$suite/README")</li>
EOF
  done
  cat << EOF
      </ul>
EOF
fi

for suite in $suite_list; do
  cat << EOF
      <h2 id="$suite"><a href="#$suite">$suite</a></h2>
EOF
  if which markdown >/dev/null 2>&1; then
    markdown $suite/README
  fi
  cat << EOF
      <table class="big-comparison">
        <tr>
          <th></th>
EOF
  for os in $os_list; do
    if [ "$os" = . ]; then
      os_dir="."
      os=$(uname -s | tr '[:upper:]' '[:lower:]')
    else
      os_dir="out/$os"
    fi
    cat << EOF
          <th>$os<br />$(cat -- "$os_dir/uname.out")</th>
EOF
  done
  cat << EOF
        </tr>
EOF
  test_list=$(cd "$suite" && ls -- *.c | sed -E 's/\.c$//' | sort)
  test_list=$(echo "$test_list" | sed -E -e "s,$suite/,,g" -e 's,\.c,,g')
  for test in $test_list; do
    cat << EOF
        <tr>
          <th id="$suite-$test"><a href="#$suite-$test">§</a> <a href="$suite/$test.c">$test</a></th>
EOF
    tmpdir=$(mktemp -dt results.XXXXXX)
    mkdir -- "$tmpdir/good"
    mkdir -- "$tmpdir/bad"
    mkdir -- "$tmpdir/unknown"
    mkdir -- "$tmpdir/unrated"
    for os in $os_list; do
      if [ "$os" = . ]; then
        os_dir="."
      else
        os_dir="out/$os"
      fi
      if [ -e "$os_dir/$suite/$test.out" ]; then
        result=$(check_result "$suite" "$test" "$os_dir/$suite/$test.out")
        hash=$(cat "$os_dir/$suite/$test.out" | $checksum | tr -d ' -')
        if [ ! -e "$tmpdir/$result/$hash" ]; then
          variants=$(expr $(ls -- "$tmpdir/$result" | wc -l) + 1)
          echo "$variants" > "$tmpdir/$result/$hash"
        fi
      fi
    done
    for os in $os_list; do
      if [ "$os" = . ]; then
        os_dir="."
      os=$(uname -s | tr '[:upper:]' '[:lower:]')
      else
        os_dir="out/$os"
      fi
      if [ -e "$os_dir/$suite/$test.out" ]; then
        result=$(check_result "$suite" "$test" "$os_dir/$suite/$test.out")
        variants=$(ls -- "$tmpdir/$result" | wc -l)
        if [ $variants -le 1 ]; then
          class=$result
        else
          hash=$(cat "$os_dir/$suite/$test.out" | $checksum | tr -d ' -')
          variant=$(cat "$tmpdir/$result/$hash")
          class=$result-$variant
        fi
        cat << EOF
          <td class="$class">$os: $result$(cat "$os_dir/$suite/$test.out" | sed 's,^,<br />,g' | tr -d '\n')</td>
EOF
      else
        cat << EOF
          <td class="unrated">$os: no data</td>
EOF
      fi
    done
    rm -rf -- "$tmpdir"
    cat << EOF
        </tr>
EOF
  done
  cat << EOF
      </table>
EOF
done

if $footer; then
 cat << \EOF
    </article>
  </body>
</html>
EOF
fi
