#!/bin/bash
apply_debug_settings() {
    # 在子脚本中应用调试设置
    [[ "$BASH_XTRACE" == "1" ]] && set -x
    [[ "$BASH_VERBOSE" == "1" ]] && set -v
    [[ "$BASH_ERREXIT" == "1" ]] && set -e
}
apply_debug_settings


[ -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
if [ "${USE_CMAKE_MACA}" == "1" ] && [ "${MAKE_MACA_ING}" == "1" ]; then
    rm -rf $DST_WCUDA/CUDA_DIR
else
    rm -rf $DST_WCUDA
fi

# 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
    generator='make'

    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}
    [ -f "build.ninja" ] && generator="Ninja"
    [ "${generator}" == "make" ] && \
        make_cmd="make_maca ${newparam[@]} ${target[@]} ${transfer_param[@]}"
    [ "${generator}" == "Ninja" ] && \
        make_cmd="ninja_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"

if [ "${USE_CMAKE_MACA}" == "1" ] && [ "${MAKE_MACA_ING}" == "1" ]; then
    link_file=${CUCC_PATH}/tools/link.json
else
    link_file=$CURRENT_DIR/link.json
fi
#echo "cmake_maca--link_file:${link_file}"

INPUT=$(cat ${link_file})
INPUT_LENGTH="${#INPUT}"
parse "" "" <<<"${INPUT}"

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

ln -sf $CUDA_DIR $DST_WCUDA/CUDA_DIR

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

echo "prepare to cmake maca environment 0912 ... USE_CMAKE_MACA:${USE_CMAKE_MACA};MAKE_MACA_ING:${MAKE_MACA_ING}"

paramlen=$#

listp=()
for i in "$@"; do
    # echo "${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 "lpf ${i} 3--> ${flag}"
    elif [[ "${i}" =~  $macro_reg_str ]]; then
        flag="${i/=/=\\}"
        flag="${flag/%\"/\\\"}"
        # echo "lpf ${i} 2-->${flag}"
    elif [[ "${i}" =~  $macro_reg ]]; then
        flag="${i/=/=\"}"
        flag="${flag}\""
    else
        flag="${i//;/\\;}"
    fi

    if [ "${flag}" == "Unix Makefiles" ]; then
        flag="\\\"Unix\\ Makefiles\\\""
    fi
    if [ "${flag}" == "-GUnix Makefiles" ]; then
        flag="\\\"-GUnix\\ Makefiles\\\""
    fi

    listp+=(${flag})
done
# echo "listp:${#listp[@]} ${listp[@]}"

if [[ $paramlen -ge 1 ]]; then
    if [[ $* == *"--build"* ]]; then
        deal_build_param ${listp[@]}
    else
        if [ "${USE_CMAKE_MACA}" == "1" ] && [ "${MAKE_MACA_ING}" == "1" ]; then
            cmake_exe=$(which cmake)
            cmake_dir=$(dirname ${cmake_exe})
            #echo "lpf in cmake ls ${cmake_dir} | grep cmake"
            #ls ${cmake_dir} | grep cmake
            set -x
            full_cmd=(${cmake_dir}/cmake.bak.forcubridge ${listp[@]})
            eval ${full_cmd[@]}
            set +x
        else
            full_cmd=(cmake ${listp[@]})
            eval ${full_cmd[@]}
        fi
    fi
fi