# vim:ft=bash
# vim:ft=sh

__contains_word () {
    local w word=$1; shift
    for w in "$@"; do
        [[ $w = "$word" ]] && return
    done
}

__get_accelerators() {
    local accels=()

    BUS_PATH="/sys/class/enyx_hfp_bus"

    [ ! -d "$BUS_PATH" ] && return

    for bus in "$BUS_PATH"/*; do
        local alias="$bus/bus_alias" index="$bus/index"
        [ -e "$alias" ] && accels+=("$(cat "$alias")") && continue
        [ -e "$index" ] && accels+=("$(cat "$index")") && continue
    done

    echo "${accels[@]}"
}

__find_comp_word() {
    local commands

    commands=("$@")

    for ((i=0; i < COMP_CWORD; i++)); do
        if __contains_word "${COMP_WORDS[i]}" "${commands[@]}"; then
            echo "${COMP_WORDS[i]}"
            return
        fi
    done
}

__complete_common_args() {
    local prev="$1"

    case "$prev" in
        -a|--accelerator)
            readarray -t COMPREPLY < \
                <(compgen -W "$(__get_accelerators)" -- "${cur}")
            return 0
            ;;
        -h|--help)
            return 0
            ;;
    esac

    return 1
}

__handle_common_opts() {
    local cur="$1"
    local common_opts="-a --accelerator -j --json -h --help"

    case "$cur" in
        -*)
        readarray -t -O "${#COMPREPLY[@]}" COMPREPLY < \
            <(compgen -W "${common_opts}" -- "${cur}")
        return
        ;;
    esac
}

__get_board_model() {
    [[ -z "${accelerator}" ]] && return
    enyx-bsp board --json --accelerator "${accelerator}" show | jq -r '.data.Accelerator."Board model"'
}

__find_commnand_line_accelerator() {
    local acc_next
    local accelerator=0
    for word in "${COMP_WORDS[@]}"; do
        case "$word" in
            --accelerator | -a)
                acc_next=true;
                ;;
            *)
                if [ -n "$acc_next" ]; then
                    accelerator="${word}"
                    break
                fi
                ;;
        esac
    done

    echo "$accelerator"
}

__complete_allowed_board_set() {
    local accelerator
    accelerator="$(__find_commnand_line_accelerator)"

    local model
    model="$(__get_board_model)"

    local allowed_sets=()
    case "$model" in
        xilinx_u50 | xilinx_u55c)
            allowed_sets+=("transceiver-speed")
            ;;
        enyx_fpb2 | bittware_xupp3r | bittware_xupvv8)
            allowed_sets+=("boot-image")
            ;;
    esac

    readarray -t COMPREPLY < \
        <(compgen -W "${allowed_sets[*]}" -- "${cur}")
}

__complete_transceiver_speed() {
    local transceiver_speeds="1G 10G"

    local keyword
    keyword="$1"

    local accelerator
    accelerator="$(__find_commnand_line_accelerator)"

    local model
    model="$(__get_board_model)"

    local keyword_index
    for ((i=0; i < COMP_CWORD; i++)); do
        if [[ "${COMP_WORDS[i]}" == "$keyword" ]]; then
            keyword_index="$i"
        fi
    done

    local cur_offset=$((COMP_CWORD - keyword_index))

    case "$model" in
        xilinx_u50)
            if [[ "$cur_offset" -le 1 ]]; then
                readarray -t COMPREPLY < \
                    <(compgen -W "${transceiver_speeds}" -- "${cur}")
                return
            fi
            ;;
        xilinx_u55c)
            if [[ "$cur_offset" -le 2 ]]; then
                readarray -t COMPREPLY < \
                    <(compgen -W "${transceiver_speeds}" -- "${cur}")
                return
            fi
            ;;
    esac
}

__find_core_names() {
    [[ -z "${accelerator}" ]] && return
    enyx-bsp accelerator --json --accelerator "${accelerator}" show core-tree | jq '.. | select(.Core?) | .Core'
}

__find_core_addresses() {
    [[ -z "${accelerator}" ]] && return
    [[ -z "${core_name}" ]] && return
    enyx-bsp accelerator --json --accelerator "${accelerator}" show core-tree | jq --arg core_name "${core_name}" '.. | select(.Core? == $core_name) | ."Address range" | sub("-.+";"")'
}

__find_core_registers() {
    [[ -z "${accelerator}" ]] && return
    [[ -z "${core_name}" ]] && return
    enyx-bsp accelerator --json --accelerator "${accelerator}" show core-tree -r | jq --arg core_name "${core_name}" '.. | select(.Core? == $core_name) | ."registers"[] | ."Register name"'
}

__get_reg_args() {
    local acc_next=false
    local core_next=false;
    local addr_next=false;
    local reg_next=false;
    for word in "${words[@]}"; do
        case "$word" in
            --accelerator | -a)
                acc_next=true;
                ;;
            -*)
                ;;
            register)
                ;;
            read | write)
                core_next=true;
                ;;
            *)
                if "$acc_next"; then
                    accelerator="${word}"
                    acc_next="false"
                elif "${reg_next}"; then
                    reg="${word}"
                    break
                elif "$addr_next"; then
                    core_addr="${word}"
                    addr_next=false;
                    reg_next=true;
                elif "$core_next"; then
                    core_name="${word}"
                    core_next=false;
                    addr_next=true;
                fi
                ;;
        esac
    done
    [[ -z "${accelerator}" ]] && return
    local -a accelerators
    readarray -t accelerators < <(__get_accelerators)
    if [[ ! " ${accelerators[*]} " =~  ${accelerator}  ]]; then
        # if accelerator is not a known accelerator, don't use it
        accelerator=""
    fi
    [[ -z "${core_name}" ]] && return
    local -a cores
    readarray -t cores < <(__find_core_names)
    if [[ ! " ${cores[*]} " =~  \"${core_name}\"  ]]; then
	# if core is not a known core, don't use it
	core_name=""
    fi
    [[ -z "${core_addr}" ]] && return
    local -a addresses
    readarray -t addresses < <(__find_core_addresses)
    if [[ ! " ${addresses[*]} " =~  \"${core_addr}\"  ]]; then
	# if address is not a known address, don't use it
	core_addr=""
    fi
    local -a registers
    readarray -t registers < <(__find_core_registers)
    if [[ ! " ${registers[*]} " =~  \"${reg}\"  ]]; then
	# if reg is not a known reg, don't use it
	reg=""
    fi
}

_enyx-bsp-accelerator() {
    local cur prev words
    _init_completion || return

    local cmd;
    local show;
    local reg_op
    local commands=(list show register)
    local show_info=(core-tree streams all)
    local reg_cmd=(read write)
    local reg_opts="-d --descriptions"
    local core_tree_opts="-r --display-registers -d --descriptions -h --help"

    cmd="$(__find_comp_word "${commands[@]}")"

    __complete_common_args "$prev" && return

    case "$cmd" in
        show)
            show="$(__find_comp_word "${show_info[@]}")"
            case "$show" in
                core-tree)
                    case $prev in
                        -d|--descriptions)
                            compopt -o default
                            return
                            ;;
                    esac

                    case $cur in
                        -*)
                            readarray -t COMPREPLY < \
                                <(compgen -W "$core_tree_opts" -- "${cur}")
                            ;;
                    esac
                    ;;
                "")
                    if [ "${cur:0:1}" != "-" ]; then
                        readarray -t COMPREPLY < \
                            <(compgen -W "${show_info[*]}" -- "${cur}")
                        return
                    fi
            esac
            ;;
        register)
            case $prev in
                -d|--descriptions)
                    compopt -o default
                    return
                    ;;
            esac

            reg_op="$(__find_comp_word "${reg_cmd[@]}")"
            case "${reg_op}" in
                "")
                    if [ "${cur:0:1}" != "-" ]; then
                        readarray -t COMPREPLY < \
                            <(compgen -W "${reg_cmd[*]}" -- "${cur}")
                        return
                    fi
                    ;;
                 read|write)
                    if [[ "${cur:0:1}" != "-" ]]; then
                        local core_name core_addr reg accelerator
                        __get_reg_args
                        if [[ -z "${core_name}" ]]; then
                            local -a cores
                            readarray -t cores < <(__find_core_names)
                            readarray -t COMPREPLY < \
                                <(compgen -W "${cores[*]}" -- "${cur}")
                            return
                        elif [[ -z "${core_addr}" ]]; then
                            local -a addresses
                            readarray -t addresses < <(__find_core_addresses)
                            readarray -t COMPREPLY < \
                                <(compgen -W "${addresses[*]}" -- "${cur}")
                            return
                        elif [[ -z "${reg}" ]]; then
                            local -a registers
                            readarray -t registers < <(__find_core_registers)
                            readarray -t COMPREPLY < \
                                <(compgen -W "${registers[*]}" -- "${cur}")
                            return
                        fi
                    fi
            esac

            case $cur in
                -*)
                    readarray -t COMPREPLY < \
                        <(compgen -W "${reg_opts}" -- "${cur}")
                    ;;
            esac
            ;;
        "")
            if [ "${cur:0:1}" != "-" ]; then
                readarray -t COMPREPLY < \
                    <(compgen -W "${commands[*]}" -- "${cur}")
                return
            fi
    esac

    __handle_common_opts "$cur"
}

_enyx-bsp-firmware() {
    local cur prev
    _init_completion || return

    local cmd
    local commands=(show flash warm-reset check reload-at-reboot)
    local flash_opts="-w --warm-reset -h --help --no-reload --backup -b"
    local check_opts="--backup -b"

    cmd="$(__find_comp_word "${commands[@]}")"

    __complete_common_args "$prev" && return

    case "$cmd" in
        check)
            case $cur in
                -*)
                    readarray -t COMPREPLY < \
                        <(compgen -W "$check_opts" -- "${cur}")
                    ;;
                *)
                    compopt -o default
                    return
                    ;;
            esac
            ;;
        flash)
            case $cur in
                -*)
                    readarray -t COMPREPLY < \
                        <(compgen -W "$flash_opts" -- "${cur}")
                    ;;
                *)
                    compopt -o default
                    return
                    ;;
            esac
            ;;
        "")
            if [ "${cur:0:1}" != "-" ]; then
                readarray -t COMPREPLY < \
                    <(compgen -W "${commands[*]}" -- "${cur}")
                return
            fi
            ;;
    esac

    __handle_common_opts "$cur"
}

_enyx-bsp-board() {
    local cur prev
    _init_completion || return

    local cmd
    local commands=(show setup set)
    local show_info=(info flash transceivers all boot-image)
    local set_params=(boot-image transceiver-speed)
    local boot_image_vals="primary backup"

    cmd="$(__find_comp_word "${commands[@]}")"

    __complete_common_args "$prev" && return

    case "$cmd" in
        show)
            show="$(__find_comp_word "${show_info[@]}")"
            case "$show" in
                "")
                    if [ "${cur:0:1}" != "-" ]; then
                        readarray -t COMPREPLY < \
                            <(compgen -W "${show_info[*]}" -- "${cur}")
                        return
                    fi
            esac
            ;;
        set)
            set="$(__find_comp_word "${set_params[@]}")"
            case "$set" in
                "")
                    if [ "${cur:0:1}" != "-" ]; then
                        __complete_allowed_board_set
                        return
                    fi
                    ;;
                boot-image)
                    if [ "${cur:0:1}" != "-" ]; then
                        readarray -t COMPREPLY < \
                            <(compgen -W "${boot_image_vals}" -- "${cur}")
                        return
                    fi
                    ;;
                transceiver-speed)
                    if [ "${cur:0:1}" != "-" ]; then
                        __complete_transceiver_speed "transceiver-speed"
                        return
                    fi
                    ;;
            esac
            ;;
        "")
            if [ "${cur:0:1}" != "-" ]; then
                readarray -t COMPREPLY < \
                    <(compgen -W "${commands[*]}" -- "${cur}")
                return
            fi
            ;;
    esac

    __handle_common_opts "$cur"
}

_enyx-bsp-memory() {
    local cur prev
    _init_completion || return

    local cmd
    local commands=(show test)
    local test_opts="-b --begin -e --end -l --loops -t --timeout \
        --enable-random-access --enable-random-byte-access"

    cmd="$(__find_comp_word "${commands[@]}")"

    __complete_common_args "$prev" && return

    case "$cmd" in
        show) ;;
        test)
            case $cur in
                -*)
                    readarray -t COMPREPLY < \
                        <(compgen -W "$test_opts" -- "${cur}")
                    ;;
            esac
            ;;
        "")
            if [ "${cur:0:1}" != "-" ]; then
                readarray -t COMPREPLY < \
                    <(compgen -W "${commands[*]}" -- "${cur}")
                return
            fi
            ;;
    esac

    __handle_common_opts "$cur"
}

_enyx-bsp() {
    local cur=${COMP_WORDS[COMP_CWORD]}
    local first=${COMP_WORDS[1]}
    local commands=(accelerator board firmware memory)
    local opts="-h --help"

    case "$first" in
        firmware)
            _enyx-bsp-firmware
            return
            ;;
        accelerator)
            _enyx-bsp-accelerator
            return
            ;;
        board)
            _enyx-bsp-board
            return
            ;;
        memory)
            _enyx-bsp-memory
            return
            ;;
    esac

    [ "$COMP_CWORD" -gt 1 ] && return

    case "$cur" in
        -*)
            readarray -t -O "${#COMPREPLY[@]}" COMPREPLY < \
                <(compgen -W "${opts}" -- "${cur}")
            return
            ;;
        *)
            readarray -t COMPREPLY < <(compgen -W "${commands[*]}" -- "${cur}")
            return
            ;;
    esac
}

complete -F _enyx-bsp-accelerator enyx-bsp-accelerator
complete -F _enyx-bsp-board enyx-bsp-board
complete -F _enyx-bsp-firmware enyx-bsp-firmware
complete -F _enyx-bsp-memory enyx-bsp-memory
complete -F _enyx-bsp enyx-bsp
