# bash completion for dpll(8)                             -*- shell-script -*-

# Get all the optional commands for dpll
_dpll_get_optional_commands()
{
    local object=$1; shift

    local filter_options=""
    local options="$(dpll $object help 2>&1 \
        | command sed -n -e "s/^.*dpll $object //p" \
        | cut -d " " -f 1)"

    # Remove duplicate options from "dpll $OBJECT help" command
    local opt
    for opt in $options; do
        if [[ $filter_options =~ $opt ]]; then
            continue
        else
            filter_options="$filter_options $opt"
        fi
    done

    echo $filter_options
}

# Complete based on given word
_dpll_direct_complete()
{
    local device_id pin_id value

    case $1 in
        device_id)
            value=$(dpll -j device show 2>/dev/null \
                    | jq '.device[].id' 2>/dev/null)
            ;;
        pin_id)
            value=$(dpll -j pin show 2>/dev/null \
                    | jq '.pin[].id' 2>/dev/null)
            ;;
        module_name)
            value=$(dpll -j device show 2>/dev/null \
                    | jq -r '.device[]."module-name"' 2>/dev/null \
                    | sort -u)
            ;;
    esac

    echo $value
}

# Handle device subcommands
_dpll_device()
{
    local command=$1

    case $command in
        show)
            case $prev in
                id)
                    COMPREPLY=( $( compgen -W \
                        "$(_dpll_direct_complete device_id)" -- "$cur" ) )
                    return 0
                    ;;
                *)
                    COMPREPLY=( $( compgen -W "id" -- "$cur" ) )
                    return 0
                    ;;
            esac
            ;;
        set)
            case $prev in
                id)
                    COMPREPLY=( $( compgen -W \
                        "$(_dpll_direct_complete device_id)" -- "$cur" ) )
                    return 0
                    ;;
                phase-offset-monitor)
                    COMPREPLY=( $( compgen -W "enable disable true false 0 1" -- "$cur" ) )
                    return 0
                    ;;
                phase-offset-avg-factor)
                    # numeric value, no completion
                    return 0
                    ;;
                *)
                    COMPREPLY=( $( compgen -W "id phase-offset-monitor \
                        phase-offset-avg-factor" -- "$cur" ) )
                    return 0
                    ;;
            esac
            ;;
        id-get)
            case $prev in
                module-name)
                    COMPREPLY=( $( compgen -W \
                        "$(_dpll_direct_complete module_name)" -- "$cur" ) )
                    return 0
                    ;;
                clock-id)
                    # numeric value, no completion
                    return 0
                    ;;
                type)
                    COMPREPLY=( $( compgen -W "pps eec" -- "$cur" ) )
                    return 0
                    ;;
                *)
                    COMPREPLY=( $( compgen -W "module-name clock-id type" \
                        -- "$cur" ) )
                    return 0
                    ;;
            esac
            ;;
    esac
}

# Handle pin subcommands
_dpll_pin()
{
    local command=$1

    case $command in
        show)
            case $prev in
                id)
                    COMPREPLY=( $( compgen -W \
                        "$(_dpll_direct_complete pin_id)" -- "$cur" ) )
                    return 0
                    ;;
                device)
                    COMPREPLY=( $( compgen -W \
                        "$(_dpll_direct_complete device_id)" -- "$cur" ) )
                    return 0
                    ;;
                *)
                    COMPREPLY=( $( compgen -W "id device" -- "$cur" ) )
                    return 0
                    ;;
            esac
            ;;
        set)
            case $prev in
                id|parent-device)
                    COMPREPLY=( $( compgen -W \
                        "$(_dpll_direct_complete device_id)" -- "$cur" ) )
                    return 0
                    ;;
                parent-pin|reference-sync)
                    COMPREPLY=( $( compgen -W \
                        "$(_dpll_direct_complete pin_id)" -- "$cur" ) )
                    return 0
                    ;;
                frequency|phase-adjust|esync-frequency|prio)
                    # numeric value, no completion
                    return 0
                    ;;
                direction)
                    COMPREPLY=( $( compgen -W "input output" -- "$cur" ) )
                    return 0
                    ;;
                state)
                    COMPREPLY=( $( compgen -W \
                        "connected disconnected selectable" -- "$cur" ) )
                    return 0
                    ;;
                *)
                    # Check if we are in nested context (after parent-device/parent-pin/reference-sync)
                    local i nested_keyword=""
                    for (( i=cword-1; i>0; i-- )); do
                        case "${words[i]}" in
                            parent-device)
                                nested_keyword="parent-device"
                                break
                                ;;
                            parent-pin|reference-sync)
                                nested_keyword="parent-pin"
                                break
                                ;;
                            id|frequency|phase-adjust|esync-frequency)
                                # Hit a top-level keyword, not in nested context
                                break
                                ;;
                        esac
                    done

                    if [[ "$nested_keyword" == "parent-device" ]]; then
                        COMPREPLY=( $( compgen -W "direction prio state" -- "$cur" ) )
                    elif [[ "$nested_keyword" == "parent-pin" ]]; then
                        COMPREPLY=( $( compgen -W "state" -- "$cur" ) )
                    else
                        COMPREPLY=( $( compgen -W "id frequency phase-adjust \
                            esync-frequency parent-device parent-pin reference-sync" \
                            -- "$cur" ) )
                    fi
                    return 0
                    ;;
            esac
            ;;
        id-get)
            case $prev in
                module-name)
                    COMPREPLY=( $( compgen -W \
                        "$(_dpll_direct_complete module_name)" -- "$cur" ) )
                    return 0
                    ;;
                clock-id)
                    # numeric value, no completion
                    return 0
                    ;;
                board-label|panel-label|package-label)
                    # string value, no completion
                    return 0
                    ;;
                type)
                    COMPREPLY=( $( compgen -W "mux ext synce-eth-port \
                        int-oscillator gnss" -- "$cur" ) )
                    return 0
                    ;;
                *)
                    COMPREPLY=( $( compgen -W "module-name clock-id \
                        board-label panel-label package-label type" \
                        -- "$cur" ) )
                    return 0
                    ;;
            esac
            ;;
    esac
}

# Handle monitor subcommand
_dpll_monitor()
{
    # monitor has no additional arguments
    return 0
}

# Complete any dpll command
_dpll()
{
    local cur prev words cword
    local opt='--Version --json --pretty'
    local objects="device pin monitor"

    _init_completion || return
    # Gets the word-to-complete without considering the colon as word breaks
    _get_comp_words_by_ref -n : cur prev words cword

    if [[ $cword -eq 1 ]]; then
        case $cur in
            -*)
                COMPREPLY=( $( compgen -W "$opt" -- "$cur" ) )
                return 0
                ;;
            *)
                COMPREPLY=( $( compgen -W "$objects help" -- "$cur" ) )
                return 0
                ;;
        esac
    fi

    # Deal with options
    if [[ $prev == -* ]]; then
        case $prev in
            -V|--Version)
                return 0
                ;;
            -j|--json)
                COMPREPLY=( $( compgen -W "--pretty $objects" -- "$cur" ) )
                return 0
                ;;
            *)
                COMPREPLY=( $( compgen -W "$objects" -- "$cur" ) )
                return 0
                ;;
        esac
    fi

    # Remove all options so completions don't have to deal with them.
    local i
    for (( i=1; i < ${#words[@]}; )); do
        if [[ ${words[i]::1} == - ]]; then
            words=( "${words[@]:0:i}" "${words[@]:i+1}" )
            [[ $i -le $cword ]] && cword=$(( cword - 1 ))
        else
            i=$(( ++i ))
        fi
    done

    local object=${words[1]}
    local command=${words[2]}
    local pprev=${words[cword - 2]}
    local prev=${words[cword - 1]}

    case $object in
        device|pin|monitor)
            if [[ $cword -eq 2 ]]; then
                COMPREPLY=( $( compgen -W "help" -- "$cur") )
                if [[ $object != "monitor" ]]; then
                    COMPREPLY+=( $( compgen -W \
                        "$(_dpll_get_optional_commands $object)" -- "$cur" ) )
                fi
            else
                "_dpll_$object" $command
            fi
            ;;
        help)
            return 0
            ;;
        *)
            COMPREPLY=( $( compgen -W "$objects help" -- "$cur" ) )
            ;;
    esac

} &&
complete -F _dpll dpll

# ex: ts=4 sw=4 et filetype=sh
