#!/bin/bash
# This script checks the Fortran code of the solver corresponding to this directory.
# Usage: flint [--compiler_name [--all]] or flint --clean

# Parse inputs.
if [[ $# -gt 2 ]] ; then
    printf "Usage: flint [--compiler_name [--all]] or flint --clean\n"
    exit 2
fi

TEST_ALL="N"
CLEAN="N"

# sunf95 is case sensitive. Put it as the first.
# Disable atest for now, since it encounters licensing errors randomly
printf "\n****** ATEST is disabled ******\n"
# N.B.: () defines an array
COMPILER_LIST=(sunf95 gfortran nagfor g95 ifort nvfortran flang aflang ifx)
CLIST=(s g n 9 i v f d x)
COMP_LIST=""
for i in "${!COMPILER_LIST[@]}"; do
    if type "${COMPILER_LIST[$i]}" &> /dev/null ; then
        if [[ "${COMPILER_LIST[$i]}" == "flang" ]] ; then
            # Do not make ftest if flang is provided by ARM or AMD; rtest and dtest should be made instead.
            if ! type "${COMPILER_LIST[$i]}" | grep -q '\/opt\/arm\|\/opt\/AMD' ; then
                COMP_LIST="$COMP_LIST ${CLIST[$i]}"
            fi
        else
            COMP_LIST="$COMP_LIST ${CLIST[$i]}"
        fi
    fi
done

# Parse the arguments
while [[ -n "$1" ]]; do
    case "$1" in
        --all)
            TEST_ALL="Y"
            ;;
        --clean)
            CLEAN="Y"
            ;;
        -g|--gfortran)
            COMP_LIST=" g"
            ;;
        -i|--ifort)
            COMP_LIST=" i"
            ;;
        -n|--nagfor)
            COMP_LIST=" n"
            ;;
        -a|--absoft)
            COMP_LIST=" a"
            ;;
        -9|--g95)
            COMP_LIST=" 9"
            ;;
        -s|--sunf95)
            COMP_LIST=" s"
            ;;
        -v|--nvfortran)
            COMP_LIST=" v"
            ;;
        -f|--flang)
            COMP_LIST=" f"
            ;;
        -d|--aflang)
            COMP_LIST=" d"
            ;;
        -x|--ifx)
            COMP_LIST=" x"
            ;;
        *)
            printf "Usage: flint [--compiler_name [--all]] or flint -c|--clean\n"
            exit 2
            ;;
    esac
    shift
done


# The directory where this script resides. It is the solver's directory.
SOLVER_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

# The solver's name.
SOLVER=$(basename "$SOLVER_DIR")

# The log directory in the solver's directory.
SOLVER_LOG_DIR="$SOLVER_DIR"/flog

# Root directory of this project.
ROOT_DIR=../..

# The testing directory.
TIME=$(date +%s)
RANDNUM="$((RANDOM*RANDOM))"
TEST_DIR=/tmp/"$(basename -- "$0")"_"$TIME"_"$RANDNUM"
printf  "Test directory:\n\n%s\n" "$TEST_DIR"
# We export TEST_DIR, which will be referred to by the Makefiles.
export TEST_DIR
TEST_ROOT="$TEST_DIR"/prima
FTEST_DIR="$TEST_ROOT"/fortran/tests
FTEST_SOLVER_DIR="$FTEST_DIR"/test."$SOLVER"

# The log directory in the testing directory.
TEST_LOG_DIR="$FTEST_SOLVER_DIR"/log

# The checktest script
CHCKTST="$FTEST_DIR"/checktest

# Remove the old logs.
mkdir -p "$SOLVER_LOG_DIR"
rm -f "$SOLVER_LOG_DIR"/*test*.log

# Conduct the test.
cd "$ROOT_DIR"/fortran/tests || exit 1

# Make clean (old logs in $SOLVER_LOG_DIR have been cleaned up in the above).
printf "\nCleaning up ... "
make cleanall."$SOLVER" > /dev/null
printf "Done.\n\n"

if [[ "$CLEAN" == "Y" ]] ; then
    exit 0
fi

# Check whether this is a 32-bit ARM machine (e.g., Raspberry Pi with 32-bit OS).
if [[ "$(uname -m)" = "aarch" || "$(uname -m)" = armv7* ]] ; then
    ARM32='Y'
else
    ARM32='N'
fi

printf "Tests to make:%s\n\n" "$COMP_LIST"

if [[ $TEST_ALL == 'Y' ]] ; then
    FLG_LIST="-g -O1 -O2 -O3 -fast"
else
    FLG_LIST="-g -O2 -fast"
fi

printf "Flags to test: %s\n\n" "$FLG_LIST"

for COMP in $COMP_LIST; do
    if [[ $TEST_ALL == 'Y' && $ARM32 != 'Y' ]] ; then
        for FLG in $FLG_LIST ; do
            printf "%s\t" "$FLG"
            export FFLAGS=$FLG && make "$COMP"test_c."$SOLVER"
        done
    else
        if [[ $COMP == 'f' || $COMP == 'v' || $COMP == 'd' || $ARM32 == 'Y' ]] ; then
            TESTS="i2_r4_d1 i8_r4_d0"
        else
            TESTS="i2_r16_d1 i8_r4_d0"
        fi
        for TEST in $TESTS ; do
            for FLG in $FLG_LIST ; do
                printf "%s\t" "$FLG"
                export FFLAGS=$FLG
                INFO="$(make "$COMP"test_"$TEST"_tst_c."$SOLVER" 2>&1 \
                    | grep -v "binary\ file\ matches" \
                    | grep -i "starts\|warning\|error\|info\|abort\|invalid\|violation\|fault\|illegal\|fail\|questionable\|remark\|attention\|Could\ not\ resolve\|not\ defined\|not\ public\ entity" \
                    | grep -vi "[0-9]\s*warning\|[0-9]\s*error\|[0-9]\s*info\|infos.f90\|information\|xhist, info)\|zmat, info)\|--warning\|--error" \
                    | grep -vi "pedantic-errors\|Werror\|warn\ errors\|diag-error-limit\|colour=error\|rounding\ error\|constraint violation\|default" \
                    | grep -v "^- \|^| \|^\* \|^+ \|^X \|\# " \
                    )"
                echo "$INFO" | grep -i --color "starts\|warning\|error\|info\|abort\|invalid\|violation\|fault\|illegal\|fail\|questionable\|remark\|attention\|Could\ not\ resolve\|not\ defined\|not\ public\ entity"
                if echo "$INFO" | grep -iq "error\|abort\|invalid\|violation\|fault\|illegal\|fail\|Could\ not\ resolve\|not\ defined\|not\ public\ entity" ; then
                    if [[ -f "$TEST_LOG_DIR"/"$COMP"test_"$TEST"_tst_c.log ]] ; then
                        LOGFILE="$COMP"test_"$TEST"_tst_c.log
                    else
                        LOGFILE="$COMP"test_c.log
                    fi
                    mv "$TEST_LOG_DIR"/"$LOGFILE" "$SOLVER_LOG_DIR"
                    cat "$SOLVER_LOG_DIR"/"$LOGFILE"
                    exit 2
                fi
            done
        done
    fi
    mv "$TEST_LOG_DIR"/"$COMP"test*.log "$SOLVER_LOG_DIR" 2>/dev/null || true
done

# Check the logfiles
LOGFILES=("$SOLVER_LOG_DIR"/*test*.log)  # An array
if [[ "$TEST_ALL" == "Y" ]] ; then
    for LOGFILE in "${LOGFILES[@]::${#LOGFILES[@]}}" ; do
        printf "\nChecking %s ...\n" "$LOGFILE"
        INFO="$(bash "$CHCKTST" --warnerror "$LOGFILE")"
        printf "%s" "$INFO"
        printf "\nDone!\n"
        if [[ -n "$INFO" ]] ; then
            printf "\nWarning or error found in log file.\n"
            exit 1
        fi
    done
else
    for LOGFILE in "${LOGFILES[@]::${#LOGFILES[@]}-1}" ; do
        printf "\nChecking %s ...\n" "$LOGFILE"
        bash "$CHCKTST" --error "$LOGFILE"  # Will lead to failure in case of error logged
        INFO="$(bash "$CHCKTST" --warning "$LOGFILE")"
        printf "%s" "$INFO"
        printf "\nDone!\n"
        if [[ -n "$INFO" ]] ; then
            read -n1 -s -r -p $'Continue? [Y/n] ' KEY
            printf "\n"
            if ! [[ "$KEY" == 'Y' || "$KEY" == 'y' || "$KEY" == "" ]]; then
                exit 0
            fi
        fi
    done
    # No pause needed for the last logfile.
    LOGFILE="${LOGFILES[-1]}"  # The last logfile; it needs Bash 4.x.
    printf "\nChecking %s ...\n" "$LOGFILE"
    bash "$CHCKTST" --error "$LOGFILE"  # Will lead to failure in case of error logged
    INFO="$(bash "$CHCKTST" --warning "$LOGFILE")"
    printf "%s" "$INFO"
    printf "\nDone!\n\n"
fi

rm -rf "$TEST_DIR"
export -n TEST_DIR && unset TEST_DIR  # De-export and unset TEST_DIR

export -n FFLAGS && unset FFLAGS  # De-export and unset FFLAGS

exit 0
