#!/bin/bash
# Bash is required due to usage of arrays

SAVE_DIR=`pwd`
MY_DIR=$(cd "$(dirname "$(which "$0")")" && pwd)

DVMSYS_DIR="$1"
TEST_SUITE="$2"
TASK_PROCESSOR_FD=$3

. "$MY_DIR/machine-config.sh"

if [ -f "$SAVE_DIR/machine-config.sh" ]; then
    . "$SAVE_DIR/machine-config.sh"
fi

. "$MY_DIR/test-utils.sh"

PLATFORM_CMD=$(grep "PLATFORM=" <"$DVMSYS_DIR/bin/dvm_settings.sh" | sed -s 's/export //g')
eval $PLATFORM_CMD

SETTINGS_FILE=settings
ANALYZER_FILE=test-analyzer.sh

prepare_new_dir() {
    local TASK_DIR
    TASK_DIR=`mktemp -d`
    local COMP_OPTS
    COMP_OPTS="$1"
    local COMPILE_PID
    local COMPILE_RES
    cd "$TASK_DIR"
    echo "#!/bin/sh" >dvm
    echo "export dvmarithmloopsize=1000000" >>dvm
    echo "exec '$DVMSYS_DIR/bin/dvm_drv' \"\$@\"" >>dvm
    chmod a+x dvm
    cp "$DVMSYS_DIR/user/usr.par" ./
    set -m
    if [ -f "$TEST_FILE" ]; then
        cp "$TEST_FILE" "$TEST_NAME"
        ./dvm $LANG_COMP -shared-dvm $COMP_OPTS "$TEST_NAME" >"build.log" 2>& 1 &
        COMPILE_PID=$!
    else
        find "$TEST_FILE" -mindepth 1 -maxdepth 1 | xargs cp -r -t .
        PATH="$TASK_DIR:$PATH" ./compile.sh $COMP_OPTS >"build.log" 2>& 1 &
        COMPILE_PID=$!
    fi
    proc_killer -$COMPILE_PID 600 &
    KILLER_PID=$!
    disown
    wait $COMPILE_PID
    COMPILE_RES=$?
    kill -2 $KILLER_PID >/dev/null 2>& 1
    kill -15 $KILLER_PID >/dev/null 2>& 1
    kill -9 $KILLER_PID >/dev/null 2>& 1
    if [ ! -f "$TEST_FILE" ] && [ $COMPILE_RES -eq 0 ] && [ ! -f "$TEST_EXENAME" ]; then
        :> "$TEST_EXENAME"
    fi
    echo "$TASK_DIR"
}

do_test() {
    TEST_FILE="$1"
    TEST_NAME=`basename "$TEST_FILE"`
    TEST_SHORT_PATH="$TEST_NAME"
    TMPSTR=`dirname $TEST_FILE`
    while [ "$TMPSTR" != "$TEST_SUITE" ]; do
        TEST_SHORT_PATH="$(basename $TMPSTR)/$TEST_SHORT_PATH"
        TMPSTR=`dirname $TMPSTR`
    done
    TEST_EXENAME="${TEST_NAME%.*}"
    case ${TEST_NAME##*.} in
        c|cdv) IS_FORTRAN=0;;
        f|f90|fdv) IS_FORTRAN=1;;
    esac
    if [ $IS_FORTRAN -ne 0 ]; then
        LANG_COMP="f"
    else
        LANG_COMP="c"
    fi
    TEST_DIMS=
    if [ -n "$DIMENSION_COUNT" ]; then
        TEST_DIMS=$DIMENSION_COUNT
    else
        for t in $DIMENSION_MAP; do
            FN=`echo $t | sed 's/=/ /g' | awk '{print $1}'`
            DIM=`echo $t | sed 's/=/ /g' | awk '{print $2}'`
            if [ "$FN" = "$TEST_NAME" ]; then
                TEST_DIMS=$DIM
                break
            fi
        done
    fi
    if [ -z "$TEST_DIMS" ]; then
        # Trying to extract dimension number from filename - it is first digit in it.
        TEST_DIMS=`echo "$TEST_EXENAME" | sed 's/[^0-9]//g' | cut -c1`
    fi
    if [ -z "$TEST_DIMS" ]; then
        echo "Can not find information about dimension count for test $TEST_FILE" >& 2
        TEST_DIMS=1
    fi
    if [ $MAX_DIM_PROC_COUNT -le 0 ]; then
        MAX_DIM_PROC_COUNT=$MAX_PROC_COUNT
    fi
    while true; do
        if [ -f "$SAVE_DIR/dvm-tester.pause" ] && [ "$(cat "$SAVE_DIR/dvm-tester.pause")" = "Immediate" ]; then
            echo "Paused explicitly (local)"
        elif [ -f "$MY_DIR/dvm-tester.pause" ] && [ "$(cat "$MY_DIR/dvm-tester.pause")" = "Immediate" ]; then
            echo "Paused explicitly (global)"
        else
            break
        fi
        sleep 60
    done
    echo "Compiling $TEST_SHORT_PATH on $PLATFORM platform"
    if [ $GPU_ONLY -eq 0 ]; then
        # Compile with noH
        NOH_DIR=`prepare_new_dir "-noH"`
        if [ -f "$NOH_DIR/$TEST_EXENAME" ]; then
            ISSUE_NOH=1
        else
            ISSUE_NOH=0
        fi
    fi
    if [ $DVM_ONLY -eq 0 ]; then
        # Compile without noH
        H_DIR=`prepare_new_dir ""`
        if [ -f "$H_DIR/$TEST_EXENAME" ]; then
            ISSUE_H=1
        else
            ISSUE_H=0
        fi
        # And with autoTfm
        AUTOTFM_DIR=`prepare_new_dir "-autoTfm"`
        if [ -f "$AUTOTFM_DIR/$TEST_EXENAME" ]; then
            ISSUE_AUTOTFM=1
        else
            ISSUE_AUTOTFM=0
        fi
    fi
#    cat "$H_DIR/build.log"
    echo "Generating tasks for $TEST_SHORT_PATH with $TEST_DIMS dimensions on $PLATFORM platform"
    COMMON_PART=$(
        echo -n "TASK_TYPE=1"
        echo -n " TEST_PLATFORM=$PLATFORM"
        echo -n " SHARE_RESOURCES=$SHARE_RESOURCES"
        echo -n " TEST_ANALYZER=\"$TEST_ANALYZER\""
        echo -n " TEST_SHORT_PATH=\"$TEST_SHORT_PATH\""
        echo -n " TASK_EXE=\"$TEST_EXENAME\""
        echo -n " TEST_MAX_TIME=$MAX_TIME"
    )
    # Additional size number 0 added
    i=0
    while [ $i -le $TEST_DIMS ]; do
        sizes[$i]=1
        i=$(( i + 1 ))
    done
    counter=0
    totalSize=1
    while [ $(( sizes[0] )) -eq 1 ]; do
        PROC_GRID=
        if [ $IS_FORTRAN -eq 0 ]; then
            i=1
            while [ $i -le $TEST_DIMS ]; do
                PROC_GRID="$PROC_GRID $((sizes[i]))"
                i=$(( i + 1 ))
            done
        else
            i=$TEST_DIMS
            while [ $i -ge 1 ]; do
                PROC_GRID="$PROC_GRID $((sizes[i]))"
                i=$(( i - 1 ))
            done
        fi
        if [ $GPU_ONLY -eq 0 ]; then
            if [ $ISSUE_NOH -ne 0 ]; then
                echo -n "$COMMON_PART" >&$TASK_PROCESSOR_FD
                echo -n " TASK_DIR=\"$NOH_DIR\"" >&$TASK_PROCESSOR_FD
                echo -n " TASK_NOH_FLAG=1" >&$TASK_PROCESSOR_FD
                echo -n " CPUS_PER_PROC=1" >&$TASK_PROCESSOR_FD
                echo -n " PROC_GRID=\"$PROC_GRID\"" >&$TASK_PROCESSOR_FD
                counter=$(( counter + 1 ))
                echo >&$TASK_PROCESSOR_FD
            fi
        fi
        if [ $DVM_ONLY -eq 0 ]; then
            # Single-device and single-threaded configurations
            if [ $GPU_ONLY -eq 0 ]; then
                if [ $ISSUE_H -ne 0 ]; then
                    echo -n "$COMMON_PART" >&$TASK_PROCESSOR_FD
                    echo -n " TASK_DIR=\"$H_DIR\"" >&$TASK_PROCESSOR_FD
                    echo -n " CPUS_PER_PROC=1" >&$TASK_PROCESSOR_FD
                    echo -n " PROC_GRID=\"$PROC_GRID\"" >&$TASK_PROCESSOR_FD
                    counter=$(( counter + 1 ))
                    echo >&$TASK_PROCESSOR_FD
                fi
            fi
            if [ $CUDAS_PER_NODE -gt 0 ]; then
                if [ $ISSUE_H -ne 0 ]; then
                    echo -n "$COMMON_PART" >&$TASK_PROCESSOR_FD
                    echo -n " TASK_DIR=\"$H_DIR\"" >&$TASK_PROCESSOR_FD
                    echo -n " CUDAS_PER_PROC=1" >&$TASK_PROCESSOR_FD
                    echo -n " PROC_GRID=\"$PROC_GRID\"" >&$TASK_PROCESSOR_FD
                    counter=$(( counter + 1 ))
                    echo >&$TASK_PROCESSOR_FD
                fi
                if [ $ISSUE_AUTOTFM -ne 0 ]; then
                    echo -n "$COMMON_PART" >&$TASK_PROCESSOR_FD
                    echo -n " TASK_DIR=\"$AUTOTFM_DIR\"" >&$TASK_PROCESSOR_FD
                    echo -n " TASK_AUTOTFM_FLAG=1" >&$TASK_PROCESSOR_FD
                    echo -n " CUDAS_PER_PROC=1" >&$TASK_PROCESSOR_FD
                    echo -n " PROC_GRID=\"$PROC_GRID\"" >&$TASK_PROCESSOR_FD
                    counter=$(( counter + 1 ))
                    echo >&$TASK_PROCESSOR_FD
                fi
            fi
            # Multi-device and multi-threaded configurations
            MAX_DEVS_PER_PROC=$((sizes[1]))
            DEVS_PER_PROC=2
            while [ $DEVS_PER_PROC -le $MAX_DEVS_PER_PROC ]; do
                if [ $(( MAX_DEVS_PER_PROC % DEVS_PER_PROC )) -ne 0 ]; then
                    DEVS_PER_PROC=$(( $DEVS_PER_PROC + 1 ))
                    continue
                fi
                if [ $IS_FORTRAN -eq 0 ]; then
                    MD_PROC_GRID=" $((MAX_DEVS_PER_PROC / DEVS_PER_PROC))"
                    i=2
                    while [ $i -le $TEST_DIMS ]; do
                        MD_PROC_GRID="$MD_PROC_GRID $((sizes[i]))"
                        i=$(( i + 1 ))
                    done
                else
                    MD_PROC_GRID=
                    i=$TEST_DIMS
                    while [ $i -ge 2 ]; do
                        MD_PROC_GRID="$MD_PROC_GRID $((sizes[i]))"
                        i=$(( i - 1 ))
                    done
                    MD_PROC_GRID="$MD_PROC_GRID $((MAX_DEVS_PER_PROC / DEVS_PER_PROC))"
                fi
                if [ $GPU_ONLY -eq 0 ]; then
                    if [ $ISSUE_H -ne 0 ]; then
                        echo -n "$COMMON_PART" >&$TASK_PROCESSOR_FD
                        echo -n " TASK_DIR=\"$H_DIR\"" >&$TASK_PROCESSOR_FD
                        echo -n " CPUS_PER_PROC=$DEVS_PER_PROC" >&$TASK_PROCESSOR_FD
                        echo -n " PROC_GRID=\"$MD_PROC_GRID\"" >&$TASK_PROCESSOR_FD
                        counter=$(( counter + 1 ))
                        echo >&$TASK_PROCESSOR_FD
                    fi
                fi
                if [ $ALLOW_MULTIDEV -ne 0 ] && [ $CUDAS_PER_NODE -gt 0 ]; then
                    for ((GPUS_PER_PROC=1; GPUS_PER_PROC<=$DEVS_PER_PROC; GPUS_PER_PROC++)); do
                        if [ $ISSUE_H -ne 0 ]; then
                            echo -n "$COMMON_PART" >&$TASK_PROCESSOR_FD
                            echo -n " TASK_DIR=\"$H_DIR\"" >&$TASK_PROCESSOR_FD
                            echo -n " CPUS_PER_PROC=$(($DEVS_PER_PROC - $GPUS_PER_PROC))" >&$TASK_PROCESSOR_FD
                            echo -n " CUDAS_PER_PROC=$GPUS_PER_PROC" >&$TASK_PROCESSOR_FD
                            echo -n " PROC_GRID=\"$MD_PROC_GRID\"" >&$TASK_PROCESSOR_FD
                            counter=$(( counter + 1 ))
                            echo >&$TASK_PROCESSOR_FD
                        fi
                        if [ $ISSUE_AUTOTFM -ne 0 ]; then
                            echo -n "$COMMON_PART" >&$TASK_PROCESSOR_FD
                            echo -n " TASK_DIR=\"$AUTOTFM_DIR\"" >&$TASK_PROCESSOR_FD
                            echo -n " TASK_AUTOTFM_FLAG=1" >&$TASK_PROCESSOR_FD
                            echo -n " CPUS_PER_PROC=$(($DEVS_PER_PROC - $GPUS_PER_PROC))" >&$TASK_PROCESSOR_FD
                            echo -n " CUDAS_PER_PROC=$GPUS_PER_PROC" >&$TASK_PROCESSOR_FD
                            echo -n " PROC_GRID=\"$MD_PROC_GRID\"" >&$TASK_PROCESSOR_FD
                            counter=$(( counter + 1 ))
                            echo >&$TASK_PROCESSOR_FD
                        fi
                    done
                fi
                DEVS_PER_PROC=$(( $DEVS_PER_PROC + 1 ))
            done
        fi
        # Advance to next configuration
        i=$TEST_DIMS
        while [ $i -ge 0 ]; do
            sizes[$i]=$(( sizes[i] + 1 ))
            totalSize=1
            j=1
            while [ $j -le $TEST_DIMS ]; do
                totalSize=$(( totalSize * sizes[j] ))
                j=$(( j + 1 ))
            done
            if [ $(( sizes[i] )) -le $MAX_DIM_PROC_COUNT -a $totalSize -le $MAX_PROC_COUNT ]; then
                break
            elif [ $i -gt 0 ]; then
                sizes[$i]=1
            fi
            i=$(( i - 1 ))
        done
    done
    echo "Generated $counter tasks"
    COMMON_PART=$(
        echo -n "TASK_TYPE=0"
        echo -n " TEST_PLATFORM=$PLATFORM"
        echo -n " TEST_SHORT_PATH=\"$TEST_SHORT_PATH\""
        echo -n " TASK_EXE=\"$TEST_EXENAME\""
    )
    if [ $GPU_ONLY -eq 0 ]; then
        echo -n "$COMMON_PART" >&$TASK_PROCESSOR_FD
        echo -n " TASK_DIR=\"$NOH_DIR\"" >&$TASK_PROCESSOR_FD
        echo -n " TASK_NOH_FLAG=1" >&$TASK_PROCESSOR_FD
        echo >&$TASK_PROCESSOR_FD
    fi
    if [ $DVM_ONLY -eq 0 ]; then
        echo -n "$COMMON_PART" >&$TASK_PROCESSOR_FD
        echo -n " TASK_DIR=\"$H_DIR\"" >&$TASK_PROCESSOR_FD
        echo >&$TASK_PROCESSOR_FD
        echo -n "$COMMON_PART" >&$TASK_PROCESSOR_FD
        echo -n " TASK_DIR=\"$AUTOTFM_DIR\"" >&$TASK_PROCESSOR_FD
        echo -n " TASK_AUTOTFM_FLAG=1" >&$TASK_PROCESSOR_FD
        echo >&$TASK_PROCESSOR_FD
    fi
}

traverse_tests() {
    CUR_DIR="$1"
    if [ -f "$CUR_DIR/$SETTINGS_FILE" ]; then
        . "$CUR_DIR/$SETTINGS_FILE"
    fi
    if [ -f "$CUR_DIR/$ANALYZER_FILE" ]; then
        TEST_ANALYZER="$CUR_DIR/$ANALYZER_FILE"
    fi
    TESTS=`mktemp`
    find "$CUR_DIR" -mindepth 1 -maxdepth 1 -regex '.*[.]\(c\|cdv\|f\|f90\|fdv\)' | LC_ALL=C sort >$TESTS
    DIRS=`mktemp`
    find "$CUR_DIR" -mindepth 1 -maxdepth 1 -type d -regex '.*/[^.]*' | LC_ALL=C sort >$DIRS
    while IFS= read -r f; do
        ( do_test "$f" )
    done <$TESTS
    while IFS= read -r d; do
        ( traverse_tests "$d" )
    done <$DIRS
    rm $DIRS $TESTS
}

set_default_settings() {
    MAX_PROC_COUNT=1
    MAX_DIM_PROC_COUNT=0
    SHARE_RESOURCES=0
    ALLOW_MULTIDEV=1
    DVM_ONLY=0
    GPU_ONLY=0
    TEST_ANALYZER="$MY_DIR/default-test-analyzer.sh"
    MAX_TIME=300
}

set_default_settings
(traverse_tests "$TEST_SUITE")
