#!/usr/bin/env bash

# Copyright (c) 2022 Tigera, Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -e

num_unfiltered_lines=2000
log_monitor_regexps=(
  "(?<!Decode)Failure"
  "FAIL"
  "SUCCESS"
  "Test batch"
)

echo "Current dir: $(pwd)"
my_dir="$(dirname $0)"
repo_dir="${my_dir}/.."
artifacts_dir="$repo_dir/artifacts"
log_file_name="$1"
log_file="${artifacts_dir}/$1"

mkdir -p "${artifacts_dir}"

shift

cmd_escaped="$(printf "%q " "$@")"
echo "==== Running command: $cmd_escaped"

# Many of the commands that we run expect to be run in an interactive terminal,
# even though they don't actually interact with the user.  If we just redirect
# such a command's output to file then it will fail with a variant of "stdout
# is not a TTY".  Work around that by using a trivial "expect" script that:
#
# * spawns the program in a TTY
# * runs it to completion (by waiting for EOF)
# * exits, copying the command's return code.
expect <<END >& "$log_file" &
spawn -noecho $cmd_escaped
set timeout -1
expect eof

lassign [wait] pid spawnid os_error_flag value
exit \$value
END

# Capture the PID of the expect process so we can wait on it below.
pid=$!

grep_args=("--line-buffered" "-B2" "-A15" "--perl")
# Combine the regexps; in Perl mode, grep only supports one
# pattern so we combine them with '|'.
pattern=""
for r in "${log_monitor_regexps[@]}"; do
  pattern="${pattern}|$r"
done
grep_args+=( "-e" "${pattern:1}" )

tail --pid $pid -n 1000000 -F "$log_file" | {
  # Always output the first n lines.
  head -n $num_unfiltered_lines;
  echo "---- Output to screen filtered from here ----";
  grep "${grep_args[@]}";
} &
mon_pid=$!

final_result=1
final_msg="==== Command ${cmd_escaped}INTERRUPTED (PID=$pid).  Full command output in artifacts/${log_file_name}."
cmd_done=false
function cleanup()
{
  kill $mon_pid || true
  if ! $cmd_done; then
    echo "==== Stopping command PID=${pid}..."
    kill $pid || true
  fi

  sleep 1 # So tail and grep can finish after stopping the main process.
  output-tail
  echo "$final_msg; exit with RC=$final_result"
  exit $final_result
}
trap cleanup EXIT

function output-tail()
{
  # Output the last num_unfiltered_lines lines of the log, unless the log is short, in which case
  # don't log lines that were already logged by head, above.
  num_lines=$(wc "$log_file" | cut -d ' ' -f1)
  if (( num_lines > num_unfiltered_lines )); then
    num_lines_to_tail=$(( num_lines - num_unfiltered_lines ))
    if (( num_lines_to_tail > num_unfiltered_lines )); then
      num_lines_to_tail=$num_unfiltered_lines
    fi
    echo "---- Tail of output (may duplicate some lines that hit the log filter above) ----";
    tail -n $num_lines_to_tail "$log_file"
  fi
}

echo "==== Filtered output to screen, full output in artifacts/${log_file_name} (main PID=$pid, filter PID=$mon_pid)"
if wait "$pid"; then
  final_msg="==== Command ${cmd_escaped}SUCCEEDED (PID=$pid).  Full command output in artifacts/${log_file_name}."
  final_result=0
else
  final_msg="==== Command ${cmd_escaped}FAILED (PID=$pid).  Full command output in artifacts/${log_file_name}."
fi

cmd_done=true

