#!/bin/bash
listp=()

nvccdate="0310"
jsondate="0900"

for i in "$@"; do
    flag="${i// /nvccsign_space}"
    flag="${flag//\"/nvccsign_quote}"
    flag="${flag//$/nvccsign_dollar}"
    flag="${flag//\(/nvccsign_lparenthesis}"
    flag="${flag//\)/nvccsign_rparenthesis}"
    listp+=(${flag})
done
# echo "listp:${#listp[@]} ${listp[@]}"

all_filter=()
all_replacer=()
all_adder=()

compile_filter=()
compile_replacer=()
compile_adder=()

link_filter=()
link_replacer=()
link_adder=()

compile_and_link_filter=()
compile_and_link_replacer=()
compile_and_link_adder=()

key_match=0
wrapperskipstrings=()
wrapperskipflag=0

function debug_echo(){
    if [ "${WCUDA_DEBUG}" == "1" ]; then
        echo "/* $1 */"
    fi
}

[ -z ${CUBRIDGE_HOME+x} ] && CUBRIDGE_HOME=$HOME
[ -n "${WCUDA_HOME}" ] && CUBRIDGE_HOME=${WCUDA_HOME}
debug_echo "CUBRIDGE_HOME: ${CUBRIDGE_HOME}"
[ -z ${CUCC_PATH+x} ] && CUCC_PATH=${MACA_PATH}/tools/cu-bridge/
debug_echo "CUCC_PATH: ${CUCC_PATH}"

function output_entry() {

    rflag="${2// /&}"
    # echo "$1=$2  fflag $fflag  rflag $rflag"
    if [[ "$1" == "data" ]]; then
        jsondate="$2"
    fi

    if [[ "$1" == skipstring.* ]]; then
        wrapperskipstrings+=("$2")
    fi

    if [[ "$1" == all.filter.* ]]; then
        all_filter+=("$2")
    elif [[ "$1" == all.replacer.* ]]; then
        all_replacer+=("$rflag")
    elif [[ "$1" == all.adder.* ]]; then
        all_adder+=("$2")
    fi

    if [[ "$1" == compile.filter.* ]]; then
        compile_filter+=("$2")
    elif [[ "$1" == compile.replacer.* ]]; then
        compile_replacer+=("$rflag")
    elif [[ "$1" == compile.adder.* ]]; then
        compile_adder+=("$2")
    fi

    if [[ "$1" == link.filter.* ]]; then
        link_filter+=("$2")
    elif [[ "$1" == link.replacer.* ]]; then
        link_replacer+=("$rflag")
    elif [[ "$1" == link.adder.* ]]; then
        link_adder+=("$2")
    fi

    if [[ "$1" == compile_and_link.filter.* ]]; then
        compile_and_link_filter+=("$2")
    elif [[ "$1" == compile_and_link.replacer.* ]]; then
        compile_and_link_replacer+=("$rflag")
    elif [[ "$1" == compile_and_link.adder.* ]]; then
        compile_and_link_adder+=("$2")
    fi


    if [[ "$1" == condition.*.match ]]; then
        key_match=0
        for ((i=0; i<${#listp[@]}; i++)); do
            if [[ "${listp[$i]}" == $2 ]]; then
                # echo "$1=$2" " matched"
                key_match=1
            fi
        done
    fi

    if [[ "$1" == condition.*.filter.* ]] && [[ "$key_match" == 1 ]]; then
       all_filter+=("$2")
    fi

    if [[ "$1" == condition.*.replacer.* ]] && [[ "$key_match" == 1 ]]; then
       all_replacer+=("$rflag")
    fi

    if [[ "$1" == condition.*.adder.* ]] && [[ "$key_match" == 1 ]]; then
       all_adder+=("$2")
    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 compileflag() {
    cflag="link"
}

function echolist() {
    index=0
    for i in "$@"; do
        let index+=1
        echo "$index:$i"
    done
}


function filterparam() {
    local len_param=$1
    local len_filter=$2
    # echo "len_param:$len_param len_filter:$len_filter"

    newparam=()

    for ((i = 3; $i < $(($len_param + 3)); i++)); do
        findfilter=0
        skipflag=0
        ii=$((i + 1))
        param_pair=(${!i%%=*})
        if [[ ${!i%%=*} != ${!i} ]]; then
            param_pair+=(${!i#*=})
        fi
        # echo "param_pair:${#param_pair[@]}  ${param_pair[@]}    ${!i%%=*} not equal ${!i}"
        # echo "i:${!i} param_pair:${#param_pair[@]} ${param_pair[@]}  "

        for ((j = $(($len_param + 3)); $j < $(($len_param + $len_filter + 3)); j++)); do
            IFS='=' read -ra filter_pair <<<${!j}
            # echo "param_pair:${#param_pair[@]} ${param_pair[@]}  " "filter_pair:${#filter_pair[@]} ${filter_pair[@]}  "
            if [[ ${param_pair[0]} == ${filter_pair[0]} ]]; then
                case ${#filter_pair[@]} in
                1)
                    findfilter=1
                    break
                    ;;
                2)
                    # echo "param_pair len ${#param_pair[@]} "
                    case ${#param_pair[@]} in
                    1)
                        if [[ $ii -ge $(($len_param + 3)) ]]; then
                            break
                        fi
                        if [[ ${!ii} == ${filter_pair[1]} ]]; then
                            findfilter=1
                            skipflag=1
                            break
                        fi
                        ;;
                    2)
                        # printf "param_pair:${param_pair[1]} filter_pair:${filter_pair[1]} \n"
                        if [[ ${param_pair[1]} == ${filter_pair[1]} ]]; then
                            findfilter=1
                            break
                        fi
                        ;;
                    esac
                    ;;
                esac
            fi
        done
        # echo "findfilter:$findfilter skipflag:$skipflag"
        if [ $findfilter -eq 0 ]; then
            newparam+=(${!i})
        fi

        if [ $skipflag -eq 1 ]; then
            ((i++))
        fi
    done
    echo " ${newparam[@]}"
}

function replaceparam() {
    local len_param=$1
    local len_replace=$2
    # echo "len_param:$len_param len_replace:$len_replace"

    newparam=()

    for ((i = 3; $i < $(($len_param + 3)); i++)); do
        findreplace=0
        skipflag=0
        ii=$((i + 1))

        parameq=0
        param_pair=(${!i%%=*})
        if [[ ${!i%%=*} != ${!i} ]]; then
            param_pair+=(${!i#*=})
            parameq=1
        else
            if [[ $ii -ge $(($len_param + 3)) ]]; then
                param_pair+=("")
            else
                param_pair+=(${!ii})
            fi
        fi

        # echo "param_pair:${#param_pair[@]}  ${param_pair[@]}    ${!i%%=*} not equal ${!i}" " parameq  $parameq"
        for ((j = $(($len_param + 3)); $j < $(($len_param + $len_replace + 3)); j += 2)); do
            # echo "j:$j"
            jj=$((j + 1))
            IFS='=' read -ra replace_pair <<<${!j}
            # echo "param_pair:${#param_pair[@]} ${param_pair[@]}  " "replace_pair:${#replace_pair[@]} ${replace_pair[@]}  "
            if [[ ${param_pair[0]} == ${replace_pair[0]} ]]; then
                # echo "320replace_pair:${#replace_pair[@]} ${replace_pair[@]}  "
                case ${#replace_pair[@]} in
                1)
                    # echo "323jj:$jj  ${!jj}"
                    case ${!jj} in
                    *=*)
                        IFS='=' read -ra strsjj <<<${!jj}
                        newparam+=(${strsjj[0]}\=${strsjj[1]})
                        findreplace=1
                        break
                        ;;
                    *)
                        # replace & to space
                        tmpjj="${!jj//\&/ }"
                        if [[ $parameq -eq 1 ]]; then
                            newparam+=($tmpjj=${param_pair[1]})
                        else
                            newparam+=($tmpjj)
                        fi

                        findreplace=1
                        break
                        ;;
                    esac
                ;;
                2)
                    # echo "364replace_pair:${#replace_pair[@]} ${replace_pair[0]} ${replace_pair[1]} !jj ${!jj}" "param_pair[1]:${param_pair[1]}"
                    # case ${replace_pair[1]} in
                    # \* | ${param_pair[1]})
                    if [[ ${param_pair[1]} == ${replace_pair[1]} ]]; then
                        # echo "399 enter  param_pair[1] ${param_pair[1]} replace_pair[1] ${replace_pair[1]} jj ${!jj}"
                        case ${!jj} in
                        *#\*\*)
                            # echo "369enter here jj:${!jj}, ii:${param_pair[1]}"
                            IFS='#' read -ra strsjj <<<${!jj}
                            IFS=',' read -ra strsii <<<${param_pair[1]}
                            for str in ${strsii[@]}; do
                                if [[ -n "$str" ]]; then
                                    newparam+=(${strsjj[0]}${str})
                                fi
                            done
                            findreplace=1
                            skipflag=$((1-$parameq))
                            break
                            ;;
                        *#\*)
                            IFS='#' read -ra strsjj <<<${!jj}
                            # echo "strsjj:${#strsjj[@]} ${strsjj[@]}  ii:${param_pair[1]}"
                            newparam+=(${strsjj[0]}${param_pair[1]})
                            findreplace=1
                            skipflag=$((1-$parameq))
                            break
                            ;;
                        *#*)
                            IFS='#' read -ra strsjj <<<${!jj}
                            newparam+=(${strsjj[0]}${strsjj[1]})
                            findreplace=1
                            skipflag=$((1-$parameq))
                            break
                            ;;
                        *=\*\*)
                            IFS='=' read -ra strsjj <<<${!jj}
                            IFS=',' read -ra strsii <<<${param_pair[1]}
                            for str in ${strsii[@]}; do
                                if [[ -n "$str" ]]; then
                                    newparam+=(${strsjj[0]})
                                    newparam+=($str)
                                fi
                            done
                            findreplace=1
                            skipflag=$((1-$parameq))
                            break
                            ;;
                        *=\*)
                            # echo "405here  param_pair[1]: ${param_pair[1]}"
                            IFS='=' read -ra strsjj <<<${!jj}

                            if [[ $parameq -eq 1 ]]; then
                                newparam+=(${strsjj[0]}=${param_pair[1]})
                            else
                                newparam+=(${strsjj[0]})
                                newparam+=(${param_pair[1]})
                            fi

                            findreplace=1
                            skipflag=$((1-$parameq))
                            break
                            ;;
                        *=*)
                            IFS='=' read -ra strsjj <<<${!jj}
                            # if [[ $parameq -eq 1 ]]; then
                            #     newparam+=(${strsjj[0]}=${strsjj[1]})
                            # else
                            #     newparam+=(${strsjj[0]})
                            #     newparam+=(${strsjj[1]})
                            # fi
                            newparam+=(${strsjj[0]}=${strsjj[1]})

                            findreplace=1
                            skipflag=$((1-$parameq))
                            break
                            ;;
                        *\&*)
                            IFS='&' read -ra strsjj <<<${!jj}
                            # if [[ $parameq -eq 1 ]]; then
                            #     newparam+=(${strsjj[0]}=${strsjj[1]})
                            # else
                            #     newparam+=(${strsjj[0]})
                            #     newparam+=(${strsjj[1]})
                            # fi
                            newparam+=(${strsjj[0]} ${strsjj[1]})

                            findreplace=1
                            skipflag=$((1-$parameq))
                            break
                            ;;
                        *)
                            # echo "enter here486"
                            newparam+=(${!jj})
                            findreplace=1
                            skipflag=$((1-$parameq))
                            break
                            ;;
                        esac
                    fi
                ;;
                esac
            fi
        done
        if [ $findreplace -eq 0 ]; then
            newparam+=(${!i})
        fi

        if [ $skipflag -eq 1 ]; then
            i=$((i + 1))
        fi
    done
    echo " ${newparam[@]}"
}

INPUT=$(cat $(dirname $0)/conf_gfortran.json)
INPUT_LENGTH="${#INPUT}"
parse "" "" <<<"${INPUT}"

params=()
CompilerFlag="link"
IsLinkFlag="yes"
IsCompileFlag="no"


# if params in wrapperskipstrings, set wrapperskipflag to 1
for i in $*; do
    for j in ${wrapperskipstrings[@]}; do
        if [[ $i == $j ]]; then
            wrapperskipflag=1
            break
        fi
    done
done

# "nvcc -c -o test.o test.cu "  is compile
# "nvcc -o test test.o "  is link
# "nvcc -o test test.cu "  is compile and link

FindOFlag="no"
for i in $*; do
    if [[ $FindOFlag == "yes" ]]; then
        FindOFlag="no"
        continue
    fi

    if [[ $i == *.mc ]] || [[ $i == *.cu ]] || [[ $i == *.cc ]] || [[ $i == *.c ]] || [[ $i == *.cpp ]] || [[ $i == *.C ]] ; then
        IsCompileFlag="yes"
    fi

    if [[ $i == -c ]] || [[ $i == -dc ]]; then
        IsCompileFlag="yes"
        IsLinkFlag="no"
        break
    fi

    if [[ $i == -o ]]; then
        FindOFlag="yes"
    fi
done

if [[ $IsCompileFlag == "yes" ]] && [[ $IsLinkFlag == "yes" ]]; then
    CompilerFlag="compile_and_link"
elif [[ $IsCompileFlag == "yes" ]]; then
    CompilerFlag="compile"
elif [[ $IsLinkFlag == "yes" ]]; then
    CompilerFlag="link"
else
    :
fi






# echo "listp:${#listp[@]} ${listp[@]}"

params+=("")
params+=("")
params+=("${all_adder[@]}")
if [[ "$CompilerFlag" == "compile" ]]; then
    params+=("${compile_adder[@]}")
fi
if [[ "$CompilerFlag" == "compile_and_link" ]]; then
    params+=("${compile_and_link_adder[@]}")
fi

filters=(${all_filter[@]})
replacers=(${all_replacer[@]})
if [[ "$CompilerFlag" == "compile" ]]; then
    filters+=(${compile_filter[@]})
    replacers+=(${compile_replacer[@]})
fi
if [[ "$CompilerFlag" == "link" ]]; then
    filters+=(${link_filter[@]})
    replacers+=(${link_replacer[@]})
fi
if [[ "$CompilerFlag" == "compile_and_link" ]]; then
    filters+=(${compile_and_link_filter[@]})
    replacers+=(${compile_and_link_replacer[@]})
fi



# for((i=0;i<${#replacers[@]};i++))
# do
#     echo "${replacers[i]}"
#     flag="${replacers[i]// /&}"
#     flag="${replacers[i]//\"/&}"
#     replacers[i]=${flag}
# done

# for((i=0;i<${#replacers[@]};i++))
# do
#     echo "new:" "${replacers[i]}"
# done

# filterparam ${#listp[@]}  ${#filters[@]} ${listp[@]}  ${filters[@]}
listp=(`filterparam ${#listp[@]}  ${#filters[@]} ${listp[@]}  ${filters[@]}`)
# echo "listp:" ${listp[@]}

# replaceparam ${#listp[@]} ${#replacers[@]} ${listp[@]} ${replacers[@]}
listp=(`replaceparam ${#listp[@]}  ${#replacers[@]} ${listp[@]}  ${replacers[@]}`)
# echo ${listp[@]}


# echo "listp:" "${listp[@]}"

if [ -z "${MXCC_PATH}" ]; then
    if [ -n "${MACA_CLANG_PATH}" ] && [ -f "${MACA_CLANG_PATH}/mxcc" ]; then
        MXCC_PATH=${MACA_CLANG_PATH}/mxcc
    else
        if [ -n "${MACA_PATH}" ] && [ -f "${MACA_PATH}/mxgpu_llvm/bin/mxcc" ]; then
            MXCC_PATH=${MACA_PATH}/mxgpu_llvm/bin/mxcc
        else
            MXCC_PATH=/opt/maca/mxgpu_llvm/bin/mxcc
        fi
    fi
fi

barxflag=0
for i in "${listp[@]}"; do
    sizei=${#i}
    spaceleft=${i%%#*}
    eqleft=${i%%=*}
    eqright=${i#*=}
    val=${i}

    quoteflag=0

    if [ $barxflag -eq 1 ]; then
        params[1]="${i}"
        barxflag=0
        continue
    fi


    if [[ $i == "-x"  ]]; then
        params[0]="-x"
        barxflag=1
        continue
    fi

    if [[ $i =~ \= ]]; then
        if [[ $eqright == *[\=\#]* ]]; then
            val="${eqleft}=\"${eqright}\""
            quoteflag=1
        fi
    fi

    # if [[ $i =~ \# ]]; then
    #     if [[ $quoteflag -eq 0 ]]; then
    #         val="\"${i}\""
    #     fi
    # fi

    if [[ $i =~ "nvccsign_space" ]]; then
        val="\"${i}\""
    fi
    params+=(${val})
done

if [[ "$CompilerFlag" == "link" ]] || [[ "$CompilerFlag" == "compile_and_link" ]]  ; then
    params+=("-lmccompiler")
fi

if [[ "$CompilerFlag" == "link" ]]; then
    params+=("${link_adder[@]}")
fi



strlistp="${params[@]}"
# strlistp="${strlistp//\#/ }"
strlistp="${strlistp//nvccsign_space/ }"
strlistp="${strlistp//nvccsign_quote/\\\"}"
strlistp="${strlistp//nvccsign_dollar/\\\$}"
strlistp="${strlistp//nvccsign_lparenthesis/\\(}"
strlistp="${strlistp//nvccsign_rparenthesis/\\)}"

# read env: LDFLAGS for conda
if [ -z "${LDFLAGS}" ]; then
    :
else
    if [ -z "${CONDA_EXE}" ]; then
        :
    else
        if [[ "$CompilerFlag" == "link" ]] || [[ "$CompilerFlag" == "compile_and_link" ]]  ; then
        strlistp="${strlistp} ${LDFLAGS}"
        fi
    fi
fi

mkdir -p $CUBRIDGE_HOME/_nvcc
name=$(basename $0)
# if wrapperskipflag is 1, skip warpper
if [ $wrapperskipflag -eq 1 ]; then
    ${name}_wrapper  $*
    exit 0
fi

debug_echo "nvcc: $nvccdate json: $jsondate $CompilerFlag"

# if cflag==link then use mxcc
if [[ "$CompilerFlag" == "link" ]] || [[ "$CompilerFlag" == "compile_and_link" ]]  ; then
    cmdstr="${name}_wrapper   ${strlistp}"
else
    cmdstr="${name}_wrapper  ${strlistp}"
fi


TFILE="$CUBRIDGE_HOME/_nvcc/nvcc.$$.sh"
echo $cmdstr >$TFILE
#(${MACA_PATH}/tools/cu-bridge/bin/gomxccbin $TFILE ${TFILE}_go)
debug_echo "$(cat ${TFILE})"
source ${TFILE}
retv=$?

rm $TFILE
exit $retv


