#!/bin/bash
export DIR_ORTHOLOGER=${DIR_ORTHOLOGER:="$(cd -P "$(dirname "${BASH_SOURCE[0]}")"/../ && pwd)"}
export DIR_BRHCLUS=""
export TESTDIR=""
export TESTNAME=""
export LOGFILE=""
export EXTRA_INFO=""
export IGNORE_CHECK_ENV=0
export USE_DIR_PROJECT=${USE_DIR_PROJECT:=}
#---------------------------------------------------------------------
# These values are set in orthologer_conf.sh
export URL_ORTHODB_API_BASE=
export URL_ORTHODB_API_VERSION=
export USE_API_URL=
###"$URL_ORTHODB_API_BASE/$URL_ORTHODB_API_VERSION"
#---------------------------------------------------------------------

# if running in docker whoami may not find a valid user
export SYS_WHOAMI="$(whoami 2>/dev/null)"
[[ -n $SYS_WHOAMI ]] || SYS_WHOAMI="uid$(id -u)"

# Error codes to be used by testfun()
EC_OK=0
EC_CMP_ALMOST=1
EC_CMP_POOR=2
EC_CMP_NOREF=3
EC_CMP_FAILED=255
EC_BUSCO_ALMOST=11
EC_BUSCO_POOR=12
EC_BUSCO_NONE=13
EC_BUSCO_BADID=14
EC_BUSCO_FAILED=15

EC_FAIL=255

load_url_config() {
    local fconf=${USE_DIR_PROJECT}orthologer_conf.sh
    echo "load orthologer conf at: $fconf"
    [[ -f $fconf ]] || return 255
    if [[ -z $URL_ORTHODB_API_BASE ]]; then
        local line=$(grep '^URL_ORTHODB_API_BASE=' $fconf)
        eval "$line"
    fi
    if [[ -z $URL_ORTHODB_API_VERSION ]]; then
        local line=$(grep '^URL_ORTHODB_API_VERSION=' $fconf)
        eval "$line"
    fi
}

append_slash() {
    local vname="$1"
    local value="${!vname}"
    [[ -n $value ]] && [[ "${value: -1}" == "/" ]] || value="${value}/"
    printf -v "$vname" '%s' "$value"
}

set_dir_brhclus() {
    if [[ ! -d $DIR_BRHCLUS ]]; then
        local fconf=${USE_DIR_PROJECT}orthologer_conf.sh
        [[ -f $fconf ]] || return 255
        local line=$(grep '^DIR_BRHCLUS=' $fconf)
        eval "$line"
    fi
    [[ -d $DIR_BRHCLUS ]]
}

check_setup() {
    if [[ ! -f orthologer_conf.sh ]]; then
        echo "fatal: no orthologer_conf.sh found in $(pwd)!" 1>&2
        return 255
    fi
    eval $(grep "^DIR_BRHCLUS" orthologer_conf.sh)
}

dated_logfile() {
    echo -n "$1"
    local D=$(date +'%y%m%d_%H%M')
    echo "_${D}.log"
}

check_env() {
    (( IGNORE_CHECK_ENV )) && return 0
    [[ -d Results ]] || { echo "error: environment is missing Results dir! Please run setup.sh."; return 255; }
    [[ -d PWC     ]] || { echo "error: environment is missing PWC dir! Please run setup.sh.";     return 255; }
}

set_test_name() {
    TESTDIR=$(basename $(pwd))
    TESTNAME=$(basename "$1" .sh)
    LOGFILE=RunLogs/$(dated_logfile "$TESTNAME")
}

exitfunc() {
    __exit_value__=$?
    if [[ $__exit_value__ -eq 0 ]]; then
        echo "OK$EXTRA_INFO"
    else
        echo "FAILED$EXTRA_INFO"
    fi
    trap - EXIT
    exit $__exit_value__
}
trap exitfunc EXIT

TEST_NCMPFAIL=0
TEST_NFAILED=0
TEST_NPOOR=0
TEST_NNEAR=0
TEST_NOK_NOREF=0
TEST_NOK=0
TEST_T=0
update_nfailed() {
    local ec=$1
    if (( ec == $EC_OK )); then
        TEST_NOK=$((TEST_NOK+1))
    elif (( ec == $EC_CMP_NOREF )); then
        TEST_NOK_NOREF=$((TEST_NOK_NOREF+1))
    elif (( ec == $EC_CMP_ALMOST )); then
        TEST_NNEAR=$((TEST_NNEAR+1))
    elif (( ec == $EC_CMP_POOR )); then
        TEST_NPOOR=$((TEST_NPOOR+1))
    elif (( ec == $EC_BUSCO_ALMOST )); then
        TEST_NNEAR=$((TEST_NNEAR+1))
    elif (( ec == $EC_BUSCO_POOR )); then
        TEST_NPOOR=$((TEST_NPOOR+1))
    else
        TEST_NFAILED=$((TEST_NFAILED+1))
    fi
}

# error code:
#  0   - all ok
#  1   - busco ran OK but match not 100
#  2   - busco ran OK but nodeid is not expected
BUSCO_RES=
BUSCO_ID=
check_busco_result() {
    BUSCO_RES=""
    BUSCO_ID=""
    local run_label=$1
    local expected=$2  # expected node id
    local ec=$EC_OK

    #C:100.0% S:99.5% D:0.5% F:0.0% M:0.0% n:440
    local result=( $(./orthomapper.sh BUSCO_RESULT "$run_label" 2>/dev/null) )
	BUSCO_RES="${result[0]}"
    ec=$EC_BUSCO_ALMOST
	[[ "$BUSCO_RES" == "C:100.0%" ]] && ec=$EC_OK
    [[ "$BUSCO_RES" == "<NONE>"   ]] && ec=$EC_BUSCO_NONE

    # 4. check node id found
    if [[ $ec -ne $EC_BUSCO_NONE ]]; then
        BUSCO_ID=$(./orthomapper.sh BUSCO_RESULT $run_label what=nodeid)
        [[ "$BUSCO_ID" == "$expected" ]] || ec=$EC_BUSCO_BADID
    fi
    
    return $ec
}

check_map_result_files() {
    local run_label="$1"

    # check existance of result
    local res="${run_label}.og"
    local fclus="${USE_DIR_PROJECT}Cluster/$res"
    local fbase="${USE_DIR_PROJECT}Results/$res"
    [[ -f ${fclus}      ]] || { echo "error: missing result clust file ${fclus}";      return $EC_FAIL; }
    [[ -f ${fbase}.odb  ]] || { echo "error: missing result odb file ${fbase}.odb";  return $EC_FAIL; }
    [[ -f ${fbase}.desc ]] || { echo "error: missing result desc file ${fbase}.desc"; return $EC_FAIL; }
    [[ -f ${fbase}.hits ]] || { echo "error: missing result hits file ${fbase}.hits"; return $EC_FAIL; }
    [[ -f ${fbase}.annotations ]] || { echo "error: missing annot result file ${fbase}.annotations"; return $EC_FAIL; }
    return $EC_OK
}

run_test() {
    [[ -n $TESTNAME ]] || {
        echo "no test given"
        return 255
    }
    check_env || return 255
    printf "%-30s %-60s" "$TESTNAME" "$TESTDIR/$LOGFILE ... "
    local rstr="OK"
    local T0=$(date '+%s%N')
    local ec=0
    testfun >& $LOGFILE
    ec=$?
    update_nfailed $ec
    if (( ec == $EC_OK )); then
        rstr="OK"
    elif (( ec == $EC_CMP_NOREF )); then
        rstr="OK, but no reference"
    elif (( ec == $EC_CMP_ALMOST )); then
        rstr="OK, F1=$CMPREF_F1"
    elif (( ec == $EC_CMP_POOR )); then
        rstr="POOR, F1=$CMPREF_F1"
    elif (( ec == $EC_CMP_FAILED )); then
        rstr="FAILED"
    elif(( ec == $EC_BUSCO_ALMOST )); then
        rstr="OK, BUSCO=$BUSCO_RES"
    elif (( ec == $EC_BUSCO_POOR )); then
        rstr="POOR, BUSCO=$BUSCO_RES"
    elif (( ec == $EC_BUSCO_NONE )); then
        rstr="BUSCO NONE"
    elif (( ec == $EC_BUSCO_BADID )); then
        rstr="BUSCO bad id: $BUSCO_ID"
    elif (( ec == $EC_BUSCO_FAILED )); then
        rstr="BUSCO FAILED"
    elif (( ec > $EC_BUSCO_FAILED )); then
        rstr="FAILED; ec=$ec"
    else
        rstr="ERROR, unknown code <$ec>"
    fi
    local T1=$(date '+%s%N')
    local expr="int((${T1}-${T0})*1.0e-9)"
    local dT=$(awk "BEGIN{ print $expr }")
    TEST_T=$dT
    printf "%-24s %s\n" "$rstr"    "T(secs) = $dT"
    trap - EXIT
    
    # near is good enough
    (( ec==1 )) && ec=0
    return $ec
}

download_fasta() {
    local fbase=$1
    mkdir -p ${USE_DIR_PROJECT}refs || {
        echo "error: failed creating refs dir at ${USE_DIR_PROJECT}refs"
        return 255
    }
    local fref=${USE_DIR_PROJECT}refs/${fbase}
    local frefgz=${fref}.gz
    if [[ ! -f $fref ]]; then
        load_url_config || {
            echo "error: unable to load URL params from local orthologer_conf.sh"
            return 255
        }
        local urlbase="$URL_ORTHODB_API_BASE/$URL_ORTHODB_API_VERSION"
        local url="${urlbase}/download/test/orthologer/fasta/${fbase}.gz"
        curl --silent "$url" -o $frefgz || {
            echo "error: failed downloading fasta $fbase"
            echo "url: $url"
            rm -f $frefgz
            return 255
        }
        gunzip $frefgz || {
            echo "error: failed unzipping fasta"
            echo "url: $url"
            #rm -f $frefgz
            return 255
        }
    fi
}

calc(){ awk "BEGIN{ print $T1 }" ;}
# compare with reference
# fsrc - local file with path
# fref - remote path relative to URL
#        eg "proj_prot/reference.og" (no .gz)
#        OR an existing file prefixed with 'file:'
# will search for https://data.orthodb.org/current/download/test/orthologer/proj_prot/reference.og.gz
export CMPREF_F1=
cmp_with_ref() {
    CMPREF_F1=
    [[ $# -eq 2 ]] || { TEST_NCMPFAIL=$((TEST_NCMPFAIL+1)); return 255; }
    local fsrc=$1
    local fref_in=$2
    [[ -f $fsrc ]] || {
        echo "error: cmp_with_ref - missing source file at $fsrc"
        TEST_NCMPFAIL=$((TEST_NCMPFAIL+1))
        return 255
    }
    set_dir_brhclus || {
        TEST_NCMPFAIL=$((TEST_NCMPFAIL+1))
        echo "error: failed setting DIR_BRHCLUS"
        return 255
    }

    local refsrc="$fref_in"
    local frefbase=""
    if [[ "${fref_in:0:5}" == "file:" ]]; then
        fref="${fref_in:5}"
        refsrc="$fref"
        frefbase=$(basename "$fref")
        [[ -f "$fref" ]] || {
            echo "error: unable to find reference: $fref"
            TEST_NCMPFAIL=$((TEST_NCMPFAIL+1))
            return 255
        }
    else
        load_url_config || {
            echo "error: unable to load URL params from local orthologer_conf.sh"
            return 255
        }
        local urlbase="$URL_ORTHODB_API_BASE/$URL_ORTHODB_API_VERSION"
        refsrc="${urlbase}/download/test/orthologer/${fref_in}.gz"
        local frefbase=$(basename $fref_in)
        local fref="${USE_DIR_PROJECT}refs/$frefbase"
        if [[ ! -f $fref ]]; then
            echo "info: downloading reference result $fref_in"
	        echo "info: $refsrc"
	        curl --silent "$refsrc" -o "${fref}.gz" > /dev/null
            if [[ $? -eq 0 ]]; then
                gunzip "${fref}.gz" || {
                    echo "warning: failed unzipping reference result at $fref"
                    rm -f "${fref}.gz"
                    f=""
                }
            else
                echo "warning: failed downloading reference result $refsrc"
                rm -f "${fref}.gz"
                fref=""
            fi
        fi
    fi
    
    # compare source with ref
    echo
    echo "----------------------------------------------------------------"
    local ec=0
    if [[ -f $fref ]]; then
	    echo "info: comparing reference with test result - log at ${USE_DIR_PROJECT}RunLogs/cmp_${frefbase}.log"
        $DIR_BRHCLUS/cmpclus mismatch --noexact --A "$fref" --B "$fsrc" >& "${USE_DIR_PROJECT}RunLogs/cmp_${frefbase}.log"
        if [[ $? -eq 0 ]]; then
            echo "SUCCESS - perfect match with reference"
            ec=0
        else
            CMPREF_F1=$(grep 'mean all F1' "${USE_DIR_PROJECT}RunLogs/cmp_${frefbase}.log" | awk '{print$6}')
            local F1ok=$(echo "$CMPREF_F1 > 0.90" | bc -l)
            if (( F1ok )); then
                echo "NEAR PERFECT MATCH - F1 = $CMPREF_F1"
                ec=$EC_CMP_ALMOST
            else
                echo "POOR MATCH - does not match perfectly with reference"
                ec=$EC_CMP_POOR
            fi
        fi
    else
        ec=$EC_CMP_NOREF
	    EXTRA_INFO=" - but missing reference at $refsrc"
        echo "SUCCESS - but unable to compare result with reference"
    fi
    echo "----------------------------------------------------------------"
    echo
    return $ec
}
