#!/usr/bin/env bash
#
# orthologer - Pipeline for deducing orthology from fasta files.
# Copyright (C) 2005-2023 E. Zdobnov lab: F. Tegenfeldt
# <fredrik.tegenfeldt@unige.ch> based on code by E. Kriventseva
#
# This file is part of orthologer.
#
# orthologer 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.
#
# orthologer 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
# along with orthologer.  If not, see <http://www.gnu.org/licenses/>.
#
if [[ $# -ge 1 ]]; then
    [[ -f $1 ]] || { echo "error: input file is not readable - should be a orthologer_conf.sh"; exit 255; }
    source $1
fi
#--- check if environment is ok -----------------
[ "${LOADED_COMMON_PIPELINE_SH+1}" == "" ] && { echo "ERROR $0 : main orthologer_conf.sh has not been loaded" 1>&2; exit 255; }
#------------------------------------------------
set -u

# verbose flag - may be set in calling environment
VERBOSE=${VERBOSE:=0}

# error count for the check
ERROR_COUNT=0

# list of missing packages (Not yet implemented...)
MISSING_PACKAGES=

# what to dump
#  0 - dump nothing, just return error code
#  1 - dump all
#  2 - dump only failed tests
CHK_DUMP_LEVEL=${CHK_DUMP_LEVEL=1}

#------------------------------------------------
#
#------------------------------------------------
print_msg() {
###    [ $VERBOSE -gt 0 ] || return
    local col="$1"
    local var="$2"
    local status="$3"
    local evar=$(evalvar "$var") # echo \${$var[@]})
    printf "%-30s${col}%-20s${COLOR_RESET}$evar\n" "$var" "$status"
}

#------------------------------------------------
#
#------------------------------------------------
export _HEADER_COUNTER=1
print_header() {
    ###    [ $VERBOSE -gt 0 ] || return
    [[ $CHK_DUMP_LEVEL -eq 1 ]] || return 0
    [[ $# -ge 2 ]] && _HEADER_COUNTER=1
    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
    printf  "${FACE_BOLD}%-8s %s${FACE_NORMAL}\n" "${_HEADER_COUNTER}." "$1"
    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
    _HEADER_COUNTER=$((_HEADER_COUNTER+1))
}

#------------------------------------------------
#
#------------------------------------------------
printf_v() {
###    [ $VERBOSE -gt 0 ] || return
    str=""
    args=""
    if [ $# -gt 0 ]; then
        str="$1"
        shift
        args="$*"
    fi
    printf "$str" $args
}

tst_var() {
    [[ $# -eq 2 ]] || return 255
    local varname="$1"
    shift
    local checkval="$*"
    set +u
    local vval="${!varname}"
    set -u
    [[ "$vval" == "$checkval" ]] || { echo "2"; return 0; }
    echo "0"
    return 0
}
#------------------------------------------------
#
#------------------------------------------------
check_var_setup() {
    local type="$1"
    local var="$2"
    local evar=""
    local reqflag=0
    local cmpopt=
    local errmsg=
    [ $# -gt 2 ] && reqflag="$3"
    [ $# -gt 3 ] && cmpopt="$4"
    [ $# -gt 4 ] && errmsg="$5"

    local status="OK"
    local col=$COLOR_GREEN
    local prefix=
    local notset="NOT SET"
    local rval=0
    #
    # reqflag = 0  --> always required
    # reqflag = 1  --> never required
    # reqflag = 2  --> not used
    #
    [[ $reqflag -eq 0 ]] && notset="MISSING"
    if [[ $reqflag -eq 1 ]]; then
        prefix="<NOT REQUIRED>"
    elif [[ $reqflag -eq 2 ]]; then
        prefix="<NOT USED>"
    fi

    # check if set
    if ! isset "$var"; then
        # mark this with red color only if required
        status="$notset"
        [ $reqflag -eq 0 ] && { col=$COLOR_RED; ERROR_COUNT=$((ERROR_COUNT+1)); rval=1; }
    else
        # evaluate variable and check size
        evar=$(evalvar "${var}[@]")
        if [ ${#evar} -eq 0 ]; then
            status="$notset"
            [ $reqflag -eq 0 ] && { col=$COLOR_RED; ERROR_COUNT=$((ERROR_COUNT+1)); rval=1; }
        else
            if [ "${type}" == "DIR" ]; then
                [ ! -d "$evar" ] && { status="NO DIR";  col=$COLOR_RED; [ $reqflag -eq 0 ] && ERROR_COUNT=$((ERROR_COUNT+1)); rval=1; }
            elif [ "${type}" == "FILE" ]; then
                [ ! -f "$evar" ] && { status="NO FILE"; col=$COLOR_RED; [ $reqflag -eq 0 ] && ERROR_COUNT=$((ERROR_COUNT+1)); rval=1; }
            elif [ "${type}" == "EXE" ]; then
                [ ! -x "$evar" ] && { status="NO EXEC"; col=$COLOR_RED; [ $reqflag -eq 0 ] && ERROR_COUNT=$((ERROR_COUNT+1)); rval=1; }
            fi
	        if [ ${#cmpopt} -ne 0 ]; then
		        eval $cmpopt
		        local tstcmp=$?
		        [ ${#errmsg} -eq 0 ] && errmsg="ILLEGAL VALUE"
		        [ $tstcmp -eq 0 ] || { status="$errmsg"; col=$COLOR_RED; [ $reqflag -eq 0 ] && ERROR_COUNT=$((ERROR_COUNT+1)); rval=1; }
            fi
        fi
    fi
    [ $reqflag -eq 1 ] && [ "$status" == "$notset" ] && status=
    [ $reqflag -eq 1 ] && [ "$status" == "OK" ]      && prefix=

    local doprint=1
    [[ $CHK_DUMP_LEVEL -eq 0 ]] && doprint=0     # never print
    [[ $CHK_DUMP_LEVEL -eq 2 ]] && doprint=$rval # print only errors

    if [[ $doprint -gt 0 ]]; then
        print_msg "$col" "$var" "$status$prefix"
    fi
    return $rval
}

#------------------------------------------------
#
#------------------------------------------------
check_plugin_setup() {
    [ $# -eq 1 ] || { fatal_msg "check_plugin_setup has wrong nr of arguments ($#)!"; return 255; }
    local plugin=$1
    local plup=${plugin^^}
    local labelvar=${plup}_LABEL
    local status="OK"
    local col=$COLOR_GREEN
    local msg=
    local ec=0
    check_plugin $plup
    ec=$?
    if [ $ec -ne 0 ]; then
        status="BAD"
        col=$COLOR_RED
        ERROR_COUNT=$((ERROR_COUNT+1))
        msg=$(parse_check_plugin_ec $plup $ec)
    fi

    local doprint=1
    [[ $CHK_DUMP_LEVEL -eq 0 ]] && doprint=0     # never print
    [[ $CHK_DUMP_LEVEL -eq 2 ]] && doprint=$ec # print only errors

    if [[ $doprint -ne 0 ]]; then
        printf_v "%-30s${col}%-20s${COLOR_RESET}$msg\n" $plup "$status"
    fi
}

#------------------------------------------------
#
#------------------------------------------------
check_setup() {
    if [[ $CHK_DUMP_LEVEL -eq 1 ]]; then
        printf_v
        printf_v             "============================\n"
        printf_v "$FACE_BOLD        CHECK SETUP${FACE_NORMAL}\n"
        printf_v             "============================\n"
        
        printf_v "Below are all global variables used by the pipeline.\n"
        printf_v "Each line has typically 3 columns:\n"
        printf_v "  <variable>    <status>   <value>\n\n"
        printf_v "The <status> can be any of\n"
        printf_v "  OK                 value is OK\n"
        printf_v "  OK<NOT USED>       value is OK but variable is not used in current setup\n"
        printf_v "  NOT SET            value is not set - ERROR\n"
        printf_v "  NOT SET<NOT USED>  value is not set - OK since not used\n"
        printf_v "  NOT IN [...]       value is not within allowed range - ERROR\n"
        printf_v "  <NOT REQUIRED>     variable is not required to be set\n\n"
        printf_v "There are a few more but they are self-explanatory.\n\n"
        printf_v "Note that the user should refrain from changing variables that\n"
        printf_v "are presented below but are NOT present in the user orthologer_conf.sh.\n\n"
        echo
    fi
    print_header "General"  "reset"
    check_var_setup VAR MAX_MESSAGES
    check_var_setup VAR VERBOSE
    
    print_header "Pipeline root"
    check_var_setup DIR DIR_ORTHOLOGER
    check_var_setup DIR DIR_ORTHOLOGER_BIN

    print_header "Paths"
    check_var_setup DIR TMP_DIR_BASE 0 "iswriteable $TMP_DIR_BASE" "NOT WRITEABLE"

    print_header "Work area"
    local var=
    for var in $ALL_DIR_PROJECT; do
        local dirname="$(evalvar $var)"
        check_var_setup DIR $var
    done

    print_header "Data definition"
    check_var_setup VAR PL_TODO

    print_header "Running"
    check_var_setup VAR SKIP_REMAKE_CHECK
    check_var_setup VAR SKIP_CHECK_ERROR_LOGS
    
    print_header "Plugin labels"
    local plugin=
    for plugin in $ALL_PLUGINS; do
        var=${plugin}_LABEL
        check_var_setup VAR $var
    done

    print_header "Plugin scripts"
    for plugin in $ALL_PLUGINS; do
        var=${plugin}_SCRIPT
        check_var_setup EXE $var
    done

    print_header "Verify plugins"
    for plugin in $ALL_PLUGINS; do
        check_plugin_setup $plugin
    done
    check_var_setup VAR POSTPROC_LABELS 1
    
    print_header "Pipeline step selection"
    check_var_setup VAR OP_ALL_STEPS
    check_var_setup VAR OP_NJOBMAX_BATCH
    check_var_setup VAR OP_NJOBMAX_LOCAL
    check_var_setup VAR OP_SAVE_JOBLOG


    print_header "Pipeline step setup"
    # check all OP_STEP vars
    # do a table if 
    do_table=1
#    [ $VERBOSE -eq 0 ] && do_table=0

    local step=
    if [ $do_table -eq 0 ]; then
#        [ $VERBOSE -gt 0 ] && echo
        echo
        for step in ${OP_ALL_STEPS[@]}; do
            check_var_setup VAR OP_STEP_NPARALLEL[${step}]
        done

#        [ $VERBOSE -gt 0 ] && echo
        echo
        for step in ${OP_ALL_STEPS[@]}; do
            check_var_setup VAR OP_STEP_NTHREADS[${step}]
        done

#        [ $VERBOSE -gt 0 ] && echo
        echo
        for step in ${OP_ALL_STEPS[@]}; do
            check_var_setup VAR OP_STEP_ENABLE[${step}]
        done

#        [ $VERBOSE -gt 0 ] && echo
        echo
        for step in ${OP_ALL_STEPS[@]}; do
            check_var_setup VAR OP_STEP_RUNMODE[${step}]
        done
    else
        # print table
        if [[ $CHK_DUMP_LEVEL -eq 1 ]]; then
            echo
            echo "-------------------------------------------------------------------------------------------"
            fmtstr="%-20s%-10s%12s%12s%12s%12s\n"
            printf "$fmtstr" "STEP" "ENABLE" "RUNMODE" "NPARALLEL" "NMERGE" "NTHREADS"
            echo "-------------------------------------------------------------------------------------------"
            for step in "${OP_ALL_STEPS[@]}"; do
                printf "$fmtstr" "$step" "${OP_STEP_ENABLE[$step]}" "${OP_STEP_RUNMODE[$step]}" "${OP_STEP_NPARALLEL[$step]}" "${OP_STEP_NMERGE[$step]}" "${OP_STEP_NTHREADS[$step]}"
            done
            echo
        fi
    fi

    if isfunc proj_check_setup; then
        proj_check_setup
    else
        warn_msg "no proj_check_setup() function defined!"
    fi

    local doprint=1
    local rval=0
    [[ $ERROR_COUNT -eq 0 ]] || rval=1
    
    [[ $CHK_DUMP_LEVEL -eq 0 ]] && doprint=0
    [[ $CHK_DUMP_LEVEL -eq 2 ]] && doprint=$rval
    if [[ $doprint -gt 0 ]]; then
        echo
        print_header "--- SUMMARY ---"
        if [ $ERROR_COUNT -gt 0 ]; then
            error_msg "failed $ERROR_COUNT check(s)"
            return $rval
        fi
        info_msg "all checks passed"
    fi
    return $rval
}

if [[ "$0" == "$BASH_SOURCE" ]]; then
    check_setup
fi

