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

# shellcheck source=lib/bats-core/formatter.bash
source "$BATS_ROOT/lib/bats-core/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 "$BATS_ENABLE_TIMING" ]]; then
    set_color 2
    buffer ' [%s]' "$TIMING"
  fi
  advance
  move_down $(( line_backoff_count - 1 ))
}

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

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

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

log() {
  case ${test_result} in
    pass)
    clear_color
    ;;
    fail)
    set_color 1
    ;;
  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

  not_run=$((count - passed - failures - skipped))
  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
  name=
  update_count_column_width
}

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

bats_tap_stream_ok() {
  local duration=
  if [[ "$1" == "--duration" ]]; then
    duration="$2"
    shift 2
  fi
  index="$1"
  name="$2"
  ((++passed))
  
  pass "$duration"
}

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

bats_tap_stream_not_ok() {
  local duration=
  if [[ "$1" == "--duration" ]]; then
    duration="$2"
    shift 2
  fi
  index="$1"
  name="$2"
  ((++failures))
  
  fail "$duration"
}

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
