#!/bin/bash

[ -z ${CUBRIDGE_HOME+x} ] && CUBRIDGE_HOME=$HOME
[ -n "${WCUDA_HOME}" ] && CUBRIDGE_HOME=${WCUDA_HOME}
[ -z ${CUCC_PATH+x} ] && export CUCC_PATH=${MACA_PATH}/tools/cu-bridge/
TEMP_DIR_NAME="cu-bridge"
if [ ! -z "$USE_WCUDA_FOR_MULTI_JOB" ]; then
    DST_WCUDA="$CUBRIDGE_HOME/${TEMP_DIR_NAME}/${TEMP_DIR_NAME}-$PPID"
else
    DST_WCUDA="$CUBRIDGE_HOME/${TEMP_DIR_NAME}"
fi
rm -rf $DST_WCUDA

# find cuda toolkit path
NVCC_PATH=$(which nvcc)
NVCC_PATH=${NVCC_PATH//\/\//\/}
# IF NVCC_PATH is not empty
CUDA_DIR=${NVCC_PATH%/bin/nvcc}
# echo "CUDA_DIR: $CUDA_DIR" "CUDA_PATH: $CUDA_PATH"


if [ -z "$CUDA_DIR" ]; then
    if [ -d "/usr/local/cuda" ]; then
       CUDA_DIR="/usr/local/cuda"
    fi
fi


mkdir -p $CUBRIDGE_HOME/_nvcc
TFILE="$CUBRIDGE_HOME/_nvcc/cmake_maca.$$.sh"
echo "export CUDA_PATH=$CUDA_DIR" >> $TFILE


dir=""
ln=""
src=""
function output_entry() {
    # echo "$1=$2"

    if [[ "$1" == pre_cmake.*.dir ]]; then
        dir="$2"
        # echo "mkdir -p $DST_WCUDA/$dir"
        (mkdir -p $DST_WCUDA/$dir)
    fi

    if [[ "$1" == pre_cmake.*.ln ]]; then
        ln="$2"
    fi

    if [[ "$1" == pre_cmake.*.src ]]; then
        src="$2"
        echo "ln -s $src $DST_WCUDA/$dir/$ln" >> $TFILE
    fi


}

function parse_array() {
    local current_path="${1:+$1.}$2"
    local current_scope="root"
    local current_index=0

    while [ "$chars_read" -lt "$INPUT_LENGTH" ]; do
        [ "$preserve_current_char" == "0" ] && chars_read=$((chars_read + 1)) && read -r -s -n 1 c
        preserve_current_char=0
        c=${c:-' '}

        case "$current_scope" in
        "root") # Waiting for new object or value
            case "$c" in
            '{')
                parse_object "$current_path" "$current_index"
                current_scope="entry_separator"
                ;;
            ']')
                return
                ;;
            [\"tfTF\-0-9])
                preserve_current_char=1 # Let the parse value function decide what kind of value this is
                parse_value "$current_path" "$current_index"
                preserve_current_char=1 # Parse value has terminated with a separator or an array end, but we can handle this only in the next while iteration
                current_scope="entry_separator"
                ;;

            esac
            ;;
        "entry_separator")
            [ "$c" == "," ] && current_index=$((current_index + 1)) && current_scope="root"
            [ "$c" == "]" ] && return
            ;;
        esac
    done
}

function parse_value() {
    local current_path="${1:+$1.}$2"
    local current_scope="root"

    while [ "$chars_read" -lt "$INPUT_LENGTH" ]; do
        [ "$preserve_current_char" == "0" ] && chars_read=$((chars_read + 1)) && read -r -s -n 1 c
        preserve_current_char=0
        c=${c:-' '}

        case "$current_scope" in
        "root") # Waiting for new string, number or boolean
            case "$c" in
            '"') # String begin
                current_scope="string"
                current_varvalue=""
                ;;
            [\-0-9]) # Number begin
                current_scope="number"
                current_varvalue="$c"
                ;;
            [tfTF]) # True or false begin
                current_scope="boolean"
                current_varvalue="$c"
                ;;
            "[") # Array begin
                parse_array "" "$current_path"
                return
                ;;
            "{") # Object begin
                parse_object "" "$current_path"
                return
                ;;
            esac
            ;;
        "string") # Waiting for string end
            case "$c" in
            '"') # String end if not in escape mode, normal character otherwise
                [ "$current_escaping" == "0" ] && output_entry "$current_path" "$current_varvalue" && return
                [ "$current_escaping" == "1" ] && current_varvalue="$current_varvalue$c" && current_escaping=0
                ;;
            '\') # Escape character, entering or leaving escape mode
                [ "$current_escaping" == "1" ] && current_varvalue="$current_varvalue$c"
                current_escaping=$((1 - current_escaping))
                ;;
            *) # Any other string character
                current_escaping=0
                current_varvalue="$current_varvalue$c"
                ;;
            esac
            ;;
        "number") # Waiting for number end
            case "$c" in
            [,\]}]) # Separator or array end or object end
                output_entry "$current_path" "$current_varvalue"
                preserve_current_char=1 # The caller needs to handle this char
                return
                ;;
            [\-0-9.]) # Number can only contain digits, dots and a sign
                current_varvalue="$current_varvalue$c"
                ;;
                # Ignore everything else
            esac
            ;;
        "boolean") # Waiting for boolean to end
            case "$c" in
            [,\]}]) # Separator or array end or object end
                output_entry "$current_path" "$current_varvalue"
                preserve_current_char=1 # The caller needs to handle this char
                return
                ;;
            [a-zA-Z]) # No need to do some strict checking, we do not want to validate the incoming json data
                current_varvalue="$current_varvalue$c"
                ;;
                # Ignore everything else
            esac
            ;;
        esac
    done
}

function parse_object() {
    local current_path="${1:+$1.}$2"
    local current_scope="root"

    while [ "$chars_read" -lt "$INPUT_LENGTH" ]; do
        [ "$preserve_current_char" == "0" ] && chars_read=$((chars_read + 1)) && read -r -s -n 1 c
        preserve_current_char=0
        c=${c:-' '}

        case "$current_scope" in
        "root") # Waiting for new field or object end
            [ "$c" == "}" ] && return
            [ "$c" == "\"" ] && current_scope="varname" && current_varname="" && current_escaping=0
            ;;
        "varname") # Reading the field name
            case "$c" in
            '"') # String end if not in escape mode, normal character otherwise
                [ "$current_escaping" == "0" ] && current_scope="key_value_separator"
                [ "$current_escaping" == "1" ] && current_varname="$current_varname$c" && current_escaping=0
                ;;
            '\') # Escape character, entering or leaving escape mode
                current_escaping=$((1 - current_escaping))
                current_varname="$current_varname$c"
                ;;
            *) # Any other string character
                current_escaping=0
                current_varname="$current_varname$c"
                ;;
            esac
            ;;
        "key_value_separator") # Waiting for the key value separator (:)
            [ "$c" == ":" ] && parse_value "$current_path" "$current_varname" && current_scope="field_separator"
            ;;
        "field_separator") # Waiting for the field separator (,)
            [ "$c" == ',' ] && current_scope="root"
            [ "$c" == '}' ] && return
            ;;
        esac
    done
}

function parse() {
    chars_read=0
    preserve_current_char=0

    while [ "$chars_read" -lt "$INPUT_LENGTH" ]; do
        read -r -s -n 1 c
        c=${c:-' '}
        chars_read=$((chars_read + 1))

        # A valid JSON string consists of exactly one object
        [ "$c" == "{" ] && parse_object "" "" && return
        # ... or one array
        [ "$c" == "[" ] && parse_array "" "" && return

    done
}

function deal_build_param() {
    para_len=$#
    if [[ ${para_len} -eq 1 ]];then
        # cmake --build
        cmake $*
        return
    fi

    dir=""
    newparam=()
    target=()
    transfer_param=()
    transfer_flag=0

    for ((i = 1; $i <= $(($para_len)); i++ )); do
        #echo "${i}:${!i}"
        ii=$((i + 1))
        case ${!i} in
        "--build" )
            dir=${!ii}
            ;;
        "--target" | "-t")
            # not only on target
            while [ ${ii} -le ${para_len} ]
            do
                if [[ "${!ii}" == "-"* ]] ; then
                    break
                fi
                target+=(${!ii})
                let ii++
            done
            ;;
        "--parallel" | "-j" )
            newparam+=("-j")
            newparam+=(${!ii})
            ;;
        "--config" )
            #echo "ignore --config ${!ii} "
            ;;
       "--verbose" | "-v")
            #echo "ignore ${!i} "
            ;;
        "--")
            transfer_flag=${i}
            ;;
        * )
         #echo "ignore unknow ${!i} "
            ;;
        esac
    done

    if [ ${transfer_flag} -gt 0 ] ; then
        for ((i = $((transfer_flag + 1)); $i <= $(($para_len)); i++ )); do
            transfer_param+=(${!i})
        done
    fi
    cd ${dir}
    make_cmd="make_maca ${newparam[@]} ${target[@]} ${transfer_param[@]}"
    echo "dir:${dir}, transfer param:${transfer_param[@]}, cmd:${make_cmd}"
    ${make_cmd}
    cd -
}
# get current file directory
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
# echo "CURRENT_DIR: $CURRENT_DIR"
# echo "TFILE=$TFILE"


INPUT=$(cat $CURRENT_DIR/link.json)
INPUT_LENGTH="${#INPUT}"
parse "" "" <<<"${INPUT}"

# echo "rm -rf $DST_WCUDA"
mkdir -p $DST_WCUDA
source $TFILE
rm $TFILE

ln -s $CUDA_DIR $DST_WCUDA/CUDA_DIR

export CUDA_PATH=$DST_WCUDA/CUDA_DIR
export PATH=$DST_WCUDA/bin:$CUDA_PATH/bin:$PATH

listp=()
for i in "$@"; do
    #echo "i: ${i}"
    macro_reg_blank_str="^-D\S+=\"\S+([[:blank:]]+\S+)+[[:blank:]]*\""
    macro_reg_str="^-D\S+=\"\S+\""
    macro_reg="^-D\S+=\S+([[:blank:]]+\S+)+[[:blank:]]*"
    if [[ "${i}" =~  $macro_reg_blank_str ]]; then
        flag="${i//=/=\"\\}"
        flag="${flag/%\"/\\\"\"}"
        # echo "${i} 3--> ${flag}"
    elif [[ "${i}" =~  $macro_reg_str ]]; then
        flag="${i//=/=\\}"
        flag="${flag/%\"/\\\"}"
        # echo "${i} 2-->${flag}"
    elif [[ "${i}" =~  $macro_reg ]]; then
        flag="${i//=/=\"}"
        flag="${flag}\""
        # echo "${i} 1-->${flag}"
    else
        flag="${i// /nvccsign_space}"
        flag="${flag//\"/nvccsign_quote}"
        flag="${flag/$/nvccsign_dollar}"
        flag="${flag//\(/nvccsign_lparenthesis}"
        flag="${flag//\)/nvccsign_rparenthesis}"
    fi
    listp+=(${flag})
done
#echo "listp:" ${listp[@]}
params=()
for i in "${listp[@]}"; do
    #spaceleft=${i%%#*}
    eqleft=${i%%=*}
    eqright=${i#*=}
    val=${i}
    quoteflag=0

    if [[ $i =~ \= ]]; then
        if [[ $eqright == *[\=\#]* ]]; then
            val="${eqleft}=\"${eqright}\""
            quoteflag=1
        fi
    fi
    if [[ $i =~ "nvccsign_space" ]]; then
        val="\"${i}\""
    fi
    params+=(${val})
done
strlistp="${params[@]}"
strlistp="${strlistp//nvccsign_space/ }"
strlistp="${strlistp//nvccsign_quote/\\\"}"
strlistp="${strlistp//nvccsign_dollar/\\\$}"
strlistp="${strlistp//nvccsign_lparenthesis/\\(}"
strlistp="${strlistp//nvccsign_rparenthesis/\\)}"
#echo "strlistp:${strlistp[@]}"
paramlen=$#
if [[ $paramlen -ge 1 ]]; then
    if [[ $* == *"--build"* ]]; then
        deal_build_param ${strlistp[@]}
    else
        eval ${strlistp[@]}
    fi
fi
