#!/usr/bin/env bash
set -e

# shellcheck source=lib/bats-core/formatter.bash
source "$BATS_ROOT/lib/bats/formatter.bash"

BASE_PATH=.
BATS_ENABLE_TIMING=

while [[ "$#" -ne 0 ]]; do
  case "$1" in
  -T)
    BATS_ENABLE_TIMING="-T"
    ;;
  --base-path)
    shift
    normalize_base_path BASE_PATH "$1"
    ;;
  esac
  shift
done

update_count_column_width() {
  count_column_width=$((${#count} * 2 + 2))
  if [[ -n "$BATS_ENABLE_TIMING" ]]; then
    # additional space for ' in %s sec'
    count_column_width=$((count_column_width + ${#SECONDS} + 8))
  fi
  # also update dependent value
  update_count_column_left
}

update_screen_width() {
  screen_width="$(tput cols)"
  # also update dependent value
  update_count_column_left
}

update_count_column_left() {
  count_column_left=$((screen_width - count_column_width))
}

# avoid unset variables
count=0
screen_width=80
update_count_column_width
update_screen_width
test_result=

trap update_screen_width WINCH

begin() {
  test_result= # reset to avoid carrying over result state from previous test
  line_backoff_count=0
  go_to_column 0
  update_count_column_width
  buffer_with_truncation $((count_column_left - 1)) '   %s' "$name"
  clear_to_end_of_line
  go_to_column $count_column_left
  if [[ -n "$BATS_ENABLE_TIMING" ]]; then
    buffer "%${#count}s/${count} in %s sec" "$index" "$SECONDS"
  else
    buffer "%${#count}s/${count}" "$index"
  fi
  go_to_column 1
}

finish_test() {
  move_up $line_backoff_count
  go_to_column 0
  buffer "$@"
  if [[ -n "${TIMEOUT-}" ]]; then
    set_color 2
    if [[ -n "$BATS_ENABLE_TIMING" ]]; then
      buffer ' [%s (timeout: %s)]' "$TIMING" "$TIMEOUT"
    else
      buffer ' [timeout: %s]' "$TIMEOUT"
    fi
  else
    if [[ -n "$BATS_ENABLE_TIMING" ]]; then
      set_color 2
      buffer ' [%s]' "$TIMING"
    fi
  fi
  advance
  move_down $((line_backoff_count - 1))
}

pass() {
  local TIMING="${1:-}"
  finish_test ' ✓ %s' "$name"
  test_result=pass
}

skip() {
  local reason="$1" TIMING="${2:-}"
  if [[ -n "$reason" ]]; then
    reason=": $reason"
  fi
  finish_test ' - %s (skipped%s)' "$name" "$reason"
  test_result=skip
}

fail() {
  local TIMING="${1:-}"
  set_color 1 bold
  finish_test ' ✗ %s' "$name"
  test_result=fail
}

timeout() {
  local TIMING="${1:-}"
  set_color 3 bold
  TIMEOUT="${2:-}" finish_test ' ✗ %s' "$name"
  test_result=timeout
}

log() {
  case ${test_result} in
  pass)
    clear_color
    ;;
  fail)
    set_color 1
    ;;
  timeout)
    set_color 3
    ;;
  esac
  buffer '   %s\n' "$1"
  clear_color
}

summary() {
  if [ "$failures" -eq 0 ]; then
    set_color 2 bold
  else
    set_color 1 bold
  fi

  buffer '\n%d test' "$count"
  if [[ "$count" -ne 1 ]]; then
    buffer 's'
  fi

  buffer ', %d failure' "$failures"
  if [[ "$failures" -ne 1 ]]; then
    buffer 's'
  fi

  if [[ "$skipped" -gt 0 ]]; then
    buffer ', %d skipped' "$skipped"
  fi

  if ((timed_out > 0)); then
    buffer ', %d timed out' "$timed_out"
  fi

  not_run=$((count - passed - failures - skipped - timed_out))
  if [[ "$not_run" -gt 0 ]]; then
    buffer ', %d not run' "$not_run"
  fi

  if [[ -n "$BATS_ENABLE_TIMING" ]]; then
    buffer " in $SECONDS seconds"
  fi

  buffer '\n'
  clear_color
}

buffer_with_truncation() {
  local width="$1"
  shift
  local string

  # shellcheck disable=SC2059
  printf -v 'string' -- "$@"

  if [[ "${#string}" -gt "$width" ]]; then
    buffer '%s...' "${string:0:$((width - 4))}"
  else
    buffer '%s' "$string"
  fi
}

move_up() {
  if [[ $1 -gt 0 ]]; then # avoid moving if we got 0
    buffer '\x1B[%dA' "$1"
  fi
}

move_down() {
  if [[ $1 -gt 0 ]]; then # avoid moving if we got 0
    buffer '\x1B[%dB' "$1"
  fi
}

go_to_column() {
  local column="$1"
  buffer '\x1B[%dG' $((column + 1))
}

clear_to_end_of_line() {
  buffer '\x1B[K'
}

advance() {
  clear_to_end_of_line
  buffer '\n'
  clear_color
}

set_color() {
  local color="$1"
  local weight=22

  if [[ "${2:-}" == 'bold' ]]; then
    weight=1
  fi
  buffer '\x1B[%d;%dm' "$((30 + color))" "$weight"
}

clear_color() {
  buffer '\x1B[0m'
}

_buffer=

buffer() {
  local content
  # shellcheck disable=SC2059
  printf -v content -- "$@"
  _buffer+="$content"
}

prefix_buffer_with() {
  local old_buffer="$_buffer"
  _buffer=''
  "$@"
  _buffer="$_buffer$old_buffer"
}

flush() {
  printf '%s' "$_buffer"
  _buffer=
}

finish() {
  flush
  printf '\n'
}

trap finish EXIT
trap '' INT

bats_tap_stream_plan() {
  count="$1"
  index=0
  passed=0
  failures=0
  skipped=0
  timed_out=0
  name=
  update_count_column_width
}

bats_tap_stream_begin() {
  index="$1"
  name="$2"
  begin
  flush
}

bats_tap_stream_ok() {
  index="$1"
  name="$2"
  ((++passed))

  pass "${BATS_FORMATTER_TEST_DURATION:-}"
}

bats_tap_stream_skipped() {
  index="$1"
  name="$2"
  ((++skipped))
  skip "$3" "${BATS_FORMATTER_TEST_DURATION:-}"
}

bats_tap_stream_not_ok() {
  index="$1"
  name="$2"

  if [[ ${BATS_FORMATTER_TEST_TIMEOUT-x} != x ]]; then
    timeout "${BATS_FORMATTER_TEST_DURATION:-}" "${BATS_FORMATTER_TEST_TIMEOUT}s"
    ((++timed_out))
  else
    fail "${BATS_FORMATTER_TEST_DURATION:-}"
    ((++failures))
  fi

}

bats_tap_stream_comment() { # <comment> <scope>
  local scope=$2
  # count the lines we printed after the begin text,
  if [[ $line_backoff_count -eq 0 && $scope == begin ]]; then
    # if this is the first line after begin, go down one line
    buffer "\n"
    ((++line_backoff_count)) # prefix-increment to avoid "error" due to returning 0
  fi

  ((++line_backoff_count))
  ((line_backoff_count += ${#1} / screen_width)) # account for linebreaks due to length
  log "$1"
}

bats_tap_stream_suite() {
  #test_file="$1"
  line_backoff_count=0
  index=
  # indicate filename for failures
  local file_name="${1#"$BASE_PATH"}"
  name="File $file_name"
  set_color 4 bold
  buffer "%s\n" "$file_name"
  clear_color
}

line_backoff_count=0
bats_tap_stream_unknown() { # <full line> <scope>
  local scope=$2
  # count the lines we printed after the begin text, (or after suite, in case of syntax errors)
  if [[ $line_backoff_count -eq 0 && ($scope == begin || $scope == suite) ]]; then
    # if this is the first line after begin, go down one line
    buffer "\n"
    ((++line_backoff_count)) # prefix-increment to avoid "error" due to returning 0
  fi

  ((++line_backoff_count))
  ((line_backoff_count += ${#1} / screen_width)) # account for linebreaks due to length
  buffer "%s\n" "$1"
  flush
}

bats_parse_internal_extended_tap

summary
