#!/bin/bash
# This script checks the Fortran code of the solver corresponding to this directory.
# Usage: mlint [--compiler_name [-all]] or mlint --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 [[ $# -gt 2 ]] ; then
    printf "Usage: mlint [--compiler_name [-all]] or mlint --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.
# 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"
            ;;
        -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 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

# 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 -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."$SOLVER"
        done
    else
       if [[ $COMP == 'f' || $COMP == 'v' || $COMP == 'd' || $COMP == 'x' || $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."$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" \
                    | 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 -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
        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
