#!/bin/bash
setup_debug_propagation() {
    # 检测并导出调试状态
    [[ $- == *x* ]] && export BASH_XTRACE=1
    [[ $- == *v* ]] && export BASH_VERBOSE=1
    [[ $- == *e* ]] && export BASH_ERREXIT=1
}
setup_debug_propagation

[ -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

OS=$(uname -s)
WIN32=0
if [[ "$OS" == "MINGW"* ]] || [[ "$OS" == "MSYS"* ]]; then
    WIN32=1
fi
if [[ ${WIN32} -eq 1 ]]; then
export WIN32
function uname()
{
    echo "x86_64"
}
fi

#if $DST_WCUDA/make.log is not exists
if [ ! -f "$DST_WCUDA/make.log" ]; then
    rm -rf $DST_WCUDA
fi
#set -x
if [ "${USE_CMAKE_MACA}" == "1" ]; then
    if [ "$(id -u)" -eq 0 ]; then
        :
    else
        echo "no root right"
    fi
    export MAKE_MACA_ING=1
    cmake_exe=$(which cmake)
    cmake_dir=$(dirname ${cmake_exe})
    if [ -d ${cmake_dir}/cmake.bak ]; then
       :
    else
        if [ "$UID" -eq 0 ]; then
            cp -n ${cmake_exe}  ${cmake_dir}/cmake.bak.forcubridge
            mv ${cmake_exe}  ${cmake_dir}/cmake.bak
            ln -sf ${CUCC_PATH}/tools/cmake_maca  ${cmake_exe}
        else
            sudo cp -n ${cmake_exe} ${cmake_dir}/cmake.bak.forcubridge
            sudo mv ${cmake_exe} ${cmake_dir}/cmake.bak
            if [ $? -eq 0 ]; then
                :
            else
                echo "-----cannot mv ${cmake_exe} ${cmake_dir}/cmake.bak ---- "
                echo "please use root to redo cmake_maca and make_maca"
                echo "!!!exit!!!"
                exit 1
            fi
            sudo ln -sf ${CUCC_PATH}/tools/cmake_maca  ${cmake_exe}
        fi
        # echo "ls -al ${cmake_dir} |grep cmake "
        # ls -al ${cmake_dir} | grep cmake
    fi
fi
#set +x

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

if [ -z "$CUDA_DIR" ]; then
    CUDA_DIR=$CUDA_PATH
fi

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

# 获取当前脚本所在目录
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# 导入同目录下的脚本
source "${SCRIPT_DIR}/multi_target.sh"

if check_target_set ; then
    :
else
    multi_target_check_tips
    exit 1
fi

mkdir -p $CUBRIDGE_HOME/_nvcc
TFILE="$CUBRIDGE_HOME/_nvcc/make_maca.$$.sh"

echo "export CUDA_PATH=$DST_WCUDA/CUDA_DIR/" >> $TFILE

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

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

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

    if [[ "$1" == pre_make.*.src ]]; then
        src="$2"
        echo "ln -s $src $DST_WCUDA/CUDA_DIR/$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 symlink_subitems() {

    #echo "LINK_DIR =$1,SRC_DIR=$2"
    local LINK_DIR="$1" #Symbolic link directory, content is empty
    local SRC_DIR="$2" #Source File directory

    rm -rf $LINK_DIR
    mkdir $LINK_DIR

    if [[ ! -d "$LINK_DIR" || ! -d "$SRC_DIR" ]]; then
        echo "link_dir:$LINK_DIR or source_dir:$SRC_DIR does not exist" >&2
        return 1
    fi

    find "$SRC_DIR" -mindepth 1 -maxdepth 1 -print0 |
    while IFS= read -r -d '' src; do
        name="$(basename -- "$src")"
        dst="$LINK_DIR/$name"

        # Skip if it exists
        [[ -e "$dst" || -L "$dst" ]] && continue

        ln -s -- "$src" "$dst"
    done
}


# get current file directory
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

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

echo "make maca verion:0220 ..."
export CUDA_PATH=$DST_WCUDA/CUDA_DIR/

# if make.log not exist, then make
if [ ! -f "$DST_WCUDA/make.log" ]; then
    mkdir -p $DST_WCUDA
    echo "1" > $DST_WCUDA/make.log
    source $TFILE
    if [ -n "$CUCC_CCCL_VERSION" ]; then
        if [ "$CUCC_CCCL_VERSION" -ge 3000000 ] && [ "$CUCC_CCCL_VERSION" -le 3002000 ]; then
            symlink_subitems $DST_WCUDA/CUDA_DIR/include $CUCC_PATH/include
            ln -sf $CUCC_PATH/include/thrust $DST_WCUDA/CUDA_DIR/include/cccl/thrust
            rm -rf $DST_WCUDA/CUDA_DIR/include/cuda
            rm -rf $DST_WCUDA/CUDA_DIR/include/thrust
        else
        echo "CUCC_CCCL_VERSION must be between 3000000 and 3002000, otherwise the parameter is invalid." >&2
        fi
    fi
    HOST_ARCH=$(uname -m)
    HOST_OS=$(uname -s 2>/dev/null | tr "[:upper:]" "[:lower:]")
    mkdir -p ${CUDA_PATH}/targets/${HOST_ARCH}-${HOST_OS}
    ln -s ${CUDA_PATH}/include ${CUDA_PATH}/targets/${HOST_ARCH}-${HOST_OS}/include
    ln -s ${CUDA_PATH}/lib64 ${CUDA_PATH}/targets/${HOST_ARCH}-${HOST_OS}/lib
    rm $TFILE
fi

# if param contain "clean" then clean all
if [[ $* == *"clean"* ]]; then
    rm -rf $DST_WCUDA
fi

export PATH=$DST_WCUDA/bin:$CUDA_PATH/bin:$PATH
#set -x
if [ "${USE_CMAKE_MACA}" == "1" ]; then
    make $*
    result=$?
    export MAKE_MACA_ING=0
    cmake_exe=$(which cmake)
    cmake_dir="$(dirname ${cmake_exe})"
    #first delete the link file, then you can mv a file to it
    if [ -L ${cmake_exe} ]; then
        rm -f ${cmake_exe}
    fi
    if [ -L ${cmake_dir}/cmake.bak ]; then
        if [ "$UID" -eq 0 ]; then
            cp ${cmake_dir}/cmake.bak.forcubridge  ${cmake_dir}/cmake
            rm -rf ${cmake_dir}/cmake.bak
        else
            sudo cp ${cmake_dir}/cmake.bak.forcubridge  ${cmake_dir}/cmake
            sudo rm -rf ${cmake_dir}/cmake.bak
        fi
    else
        if [ "$UID" -eq 0 ]; then
            mv ${cmake_dir}/cmake.bak  ${cmake_dir}/cmake
        else
            sudo mv ${cmake_dir}/cmake.bak  ${cmake_dir}/cmake
        fi
    fi
    #echo "ls ${cmake_dir} | grep cmake"
    #ls ${cmake_dir} | grep cmake
    exit $result
else
    make $*
fi
#set +x
