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

# This test needs the MathWorks fintrf.h. Exit if the file cannot be found.
FINTRFH=$(locate '/extern/include/fintrf.h' 2>/dev/null | head -n 1)
if [[ -z "$FINTRFH" ]] ; then
    printf "\n***********************************************"
    printf "\nfintrf.h is not found. Skip mlint.\n"
    printf "*********************************************\n\n"
    exit 0
fi

# Parse inputs.
if ! [[ $# -eq 0 || $# -eq 1 ]] ; then
    printf "Usage: mlint [--compiler_name [-all]] or mlint -c|--clean\n"
    exit 2
fi

TEST_ALL="N"
CLEAN="N"

# af95 does not support internal subroutine as an actual argument. So it is not included
# sunf95 is case sensitive. Put it as the first.
# ifx encounters internal errors when compiling BOBYQA. Disable xtest for now.
# Disable ntest for now, since it encounters internal errors randomly
#COMPILER_LIST=(sunf95 gfortran nagfor g95 ifort nvfortran flang aflang ifx)
#CLIST=(s g n 9 i v f d x)
printf "\n****** XTEST is disabled ******\n"
printf "\n****** NTEST is disabled ******\n"

# N.B.: () defines an array
COMPILER_LIST=(sunf95 gfortran g95 ifort nvfortran flang aflang)
CLIST=(s g 9 i v f d)
COMP_LIST=""
for i in "${!COMPILER_LIST[@]}"; do
   if  type "${COMPILER_LIST[$i]}" &> /dev/null ; then
       COMP_LIST="$COMP_LIST ${CLIST[$i]}"
   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"
            ;;
        -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: mlint [--compiler_name [--all]] or mlint -c|--clean\n"
            exit 2
            ;;
    esac
    shift
done

# The default Fortran compiler options.
FFLAGS=''
# We export FFLAGS, which will be referred to by the Makefiles.
export FFLAGS

# 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"/mlog

# 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
MTEST_DIR="$TEST_ROOT"/matlab/mex_gateways/tests
MTEST_SOLVER_DIR="$MTEST_DIR"/test."$SOLVER"

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

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

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

# Conduct the test.
cd "$ROOT_DIR"/matlab/mex_gateways/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

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

for COMP in $COMP_LIST; do
    if [[ $TEST_ALL == 'Y' ]] ; then
        make  "$COMP"test."$SOLVER"
    else
#        TESTS="i4_r8_d1"
       if [[ $COMP == 'f' || $COMP == 'v' || $COMP == 'd' || $COMP == 'x' ]] ; then
            TESTS="i2_r4_d1 i8_r4_d0"
        else
            TESTS="i2_r16_d1 i8_r4_d0"
        fi
        for TEST in $TESTS ; do
            INFO="$(make "$COMP"test_"$TEST"_tst."$SOLVER" \
                | grep -i "starts\|warning\|error\|info\|abort\|invalid\|violation\|fault\|illegal\|fail\|questionable\|remark\|attention\|Could\ not\ resolve" \
                | grep -vi "[0-9]\s*warning\|[0-9]\s*error\|[0-9]\s*info\|infos.f90\|information\|--warning\|--error" \
                | grep -vi "pedantic-errors\|Werror\|warn\ errors\|diag-error-limit\|colour=error" \
                | grep -vi "INFO_DFT\|==\s*info\|errorid\|errormsg" \
                | grep -vi "\[xopt, fopt, info, nf, xhist, fhist\] = FUNCTION_NAME" \
                | grep -v "^- \|^| \|^\* \|^+ \|^X \|\# " \
                )"
            echo "$INFO" | grep -i --color "starts\|warning\|error\|info\|abort\|invalid\|violation\|fault\|illegal\|fail\|questionable\|remark\|attention\|Could\ not\ resolve"
            if echo "$INFO" | grep -iq "error\|abort\|invalid\|violation\|fault\|illegal\|fail\|Could\ not\ resolve" ; then
                if [[ -f "$TEST_LOG_DIR"/"$COMP"test_"$TEST"_tst.log ]] ; then
                    LOGFILE="$COMP"test_"$TEST"_tst.log
                else
                    LOGFILE="$COMP"test.log
                fi
                mv "$TEST_LOG_DIR"/"$LOGFILE" "$SOLVER_LOG_DIR"
                cat "$SOLVER_LOG_DIR"/"$LOGFILE"
                exit 2
            fi
        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
