#! /usr/bin/env sh
# Copyright (C) 2023 - 2024:
# The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This file is part of the Trixy crate for Trinitrix.
#
# Trixy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# and the GNU General Public License along with this program.
# If not, see <https://www.gnu.org/licenses/>.

# Parts of a shell library {{{
die() {
    error "$1"
    if [ -n "$2" ]; then
        exit "$2"
    else
        exit 1
    fi
}

print() {
    # The direct usage is intended as `print` is rather lowlevel
    # shellcheck disable=2059
    printf "$*"
}
println() {
    # The direct usage is intended as `println` is rather lowlevel
    # shellcheck disable=2059
    printf "$*\n"
}
eprint() {
    >&2 print "$@"
}
eprintln() {
    >&2 println "$@"
}

if [ -n "$NO_COLOR" ]; then
    error() {
        eprintln "==> ERROR:" "$*"
    }
    warning() {
        eprintln "==> WARNING:" "$*"
    }
    debug() {
        [ -n "$SHELL_LIBRARY_DEBUG" ] && eprintln "==> [Debug:]" "$*"
    }
    debug2() {
        [ -n "$SHELL_LIBRARY_DEBUG" ] && eprintln " -> [Debug:]" "$*"
    }
    msg() {
        eprintln "==>" "$*"
    }
    msg2() {
        eprintln " ->" "$*"
    }
    prompt() {
        eprint "..>" "$*"
    }
else
    # See https://stackoverflow.com/a/33206814 for ansi codes
    error() {
        eprintln "\033[1;91m==> ERROR:\033[0m" "\033[1;93m$*\033[0m"
    }
    warning() {
        eprintln "\033[1;91m==> WARNING:\033[0m" "\033[1;93m$*\033[0m"
    }
    debug() {
        [ -n "$SHELL_LIBRARY_DEBUG" ] && eprintln "\033[1;94m==> [Debug:]\033[0m" "\033[1;93m$*\033[0m"
    }
    debug2() {
        [ -n "$SHELL_LIBRARY_DEBUG" ] && eprintln "\033[1;94m -> [Debug:]\033[0m" "\033[1;93m$*\033[0m"
    }
    msg() {
        eprintln "\033[1;96m==>\033[0m" "\033[1;93m$*\033[0m"
    }
    msg2() {
        eprintln "\033[1;96m ->\033[0m" "\033[1;93m$*\033[0m"
    }
    prompt() {
        eprint "\033[1;96m..>\033[0m" "\033[1;93m$*\033[0m"
    }
fi

mktmp() {
    ensure_tmp_dir
    mktemp -p "$SHELL_LIBRARY_TEMP_PREFIX_DIR"
}

ensure_tmp_dir() {
    if ! [ -d "$SHELL_LIBRARY_TEMP_PREFIX_DIR" ]; then
        SHELL_LIBRARY_TEMP_PREFIX_DIR="$(mktemp -d)"
    fi
}

# A new version of tmp, which allows you to specify the commandline args as normal
# arguments
tmp() {
    if echo "$1" | grep -E ' ' -q; then
        warn "Detected an old version of tmp, as there are spaces in the input string!"
    fi

    TEMP_DIR_WITH_DEL="$(mktmp)"
    "$@" 1>"$TEMP_DIR_WITH_DEL"
    echo "$TEMP_DIR_WITH_DEL"
}

# Takes a path to search as argument.
# Also takes further arguments as names to search for.
# Prints the path to a file, if a file matching the given name is found.
search_dir_for_file() {
    [ -d "$1" ] || die "Arg $1 is not a directory"
    directory="$1" && shift 1

    while read -r dir_entry; do
        dir_entry_count=$((dir_entry_count + 1))
        dir_entry_basename="$(basename "$dir_entry")"

        for name in "$@"; do
            if [ "$name" = "$dir_entry_basename" ]; then
                println "$dir_entry"
            fi
        done
    done <"$(tmp fd . "$directory" --type file --max-depth 1)"
    print ""
}

# Returns the path to the directory which contains all the specified files:
# `search_upward_files file1 file2 file3` returns a path containing file1 ...
search_upward_files() {
    directory="$(pwd)"
    final_directory=""
    output="$(mktmp)"
    while [ "$(wc -l <"$output")" = 0 ]; do
        search_dir_for_file "$directory" "$@" >"$output"
        directory="$(dirname "$directory")"
        if [ "$directory" = "/" ]; then
            warning "We bailed out, as there seems to be no directory containing $*"
            final_directory=""
            return
        fi
    done
    final_directory="$(dirname "$(head -n1 "$output")")"
    print "$final_directory"
}
# }}}

help() {
    cat <<EOF
A test manager (for trixy integration tests)

USAGE:
    ./scripts/tests.sh [OPTIONS] COMMAND

OPTIONS:
    --help   | -h
                            Display this help and exit.

COMMANDS:
    update [TESTNAME]
                            Updates the generated files of a test with TESTNAME, if
                            TESTNAME is blank, all tests are updated.

    new TESTNAME
                            Generates a new test from the '.template' directory and drops
                            you into an editor for the 'input.tri' and the 'main.rs' file.
                            It will also generate the generated files, after you close
                            the editor.

ARGUMENTS:
    TESTNAME | *([a-zA-Z0-9]) := [[ false ]]
                            The name of a test.
                            (The notation above is only really useful, when
                            the shell library is employed to generate
                            a completion script.)

EOF
}

ROOT_DIR="$(search_upward_files "Cargo.toml")"

new() {
    test_name="$1"
    test_dir="$ROOT_DIR/tests/$test_name"

    [ -d "$test_dir" ] && die "$test_dir already exists, refusing to override"

    cp --recursive "$ROOT_DIR/tests/.template" "$test_dir"

    sed --in-place "s|template_main_function_name|$test_name|g" "$test_dir/main.rs"

    editor="${EDITOR:-$VI}"
    "$editor" "$test_dir/input.tri"

    update "$test_name"

    "$editor" "$test_dir/main.rs"
}

update() {
    test_name="$1"
    test_dir="$ROOT_DIR/tests/$test_name"

    if [ -n "$test_name" ]; then
        cd "$test_dir" || die "Bug: Test dir ($test_dir) does not exist yet!"
        if ! cargo run --features build-binary -- "$test_dir/input.tri" generate --write-files all; then
            die "The update process for '$test_name' failed!"
        fi
    else
        fd . "$ROOT_DIR/tests" --type directory --max-depth=1 --exec basename | while read -r dir; do
            msg2 "Updating $dir.."
            update "$dir"
        done
    fi
}

for arg in "$@"; do
    case "$arg" in
    "--help" | "-h")
        help
        exit 0
        ;;
    esac
done

case "$1" in
"update")
    shift 1
    [ -n "$1" ] && test_name="$1"
    update "$test_name"
    ;;
"new")
    shift 1
    test_name="$1"
    [ -z "$test_name" ] && die "You must specify a TESTNAME"
    new "$test_name"
    ;;
*)
    die "'$1' is not a recognized command; See --help for more info."
    ;;
esac
# vim: ft=sh
