# bash command-line completion for virtualbox
#
# This version of bash completion was born due to the need of fast and easy
# access to the maze of commands and parameters VBoxManage provides. Based on
# Sebastian[1] script I've managed to improve it and adapt to newest stable
# version available in Gentoo Portage.
#
# [1] Sebastian T. Hafner <sonix@own-hero.net>
#
# [ ] guestproperty
# [ ] hostonlyif
# [ ] import
# [ ] metrics
# [ ] modifyhd
# [ ] modifyvm
# [ ] natnetwork

_VBoxManage() {
    local cur prev opts cmd subcommand tmp items name index result

    # Check the COMP_WORDS looking for name of the vm. If name contain space or
    # is enclosed in quotes, glue name together in variable name. Variable index
    # will hold the last index of COMP_WORDS array which contain the end of the
    # name.
    _find_item_name() {
        local idx=$1
        name=""

        while true
        do
            name="${name}${COMP_WORDS[$idx]}"
            [[ ${COMP_WORDS[$idx]} = *'"' ]] && break

            if [[ ${COMP_WORDS[$idx]} = '"'* || ${COMP_WORDS[$idx]} = *'\ ' ]]
            then
                idx=$((++idx))
                continue
            fi
            break
        done
        index=$idx
    }

    _get_excluded_items() {
        local i

        result=""
        for i in $@; do
            [[ " ${COMP_WORDS[@]} " == *" $i "* ]] && continue
            result="$result $i"
        done
    }

    # Generate registered hard disk files.
    # NOTE: This function may introduce some quirks, if there is a space or
    # other characters which usually are treated as IFS - like space. Pipe
    # character used in disk filename will ruin this completions.
    _hdd_comp() {
        local cur=$1
        local hdds
        local item

        hdds=$(VBoxManage list hdds | \
            grep -A 1 'normal (base)' | \
            grep "Location:" | \
            sed 's/Location:\s\+//' | \
            sed 's/\s/\\ /g' | \
            tr '\n' '|' | \
            sed 's/|$//')
        IFS='|' read -ra hdds <<< "$hdds"

        for item in "${hdds[@]}"
        do
            [[ ${item^^} == ${cur^^}* ]] && COMPREPLY+=("$item")
        done
    }

    _floppy_comp() {
        local cur=$1
        local floppies
        local item

        floppies=$(VBoxManage list floppies | \
            grep "Location:" | \
            sed 's/Location:\s\+//' | \
            sed 's/\s/\\ /g' | \
            tr '\n' '|' | \
            sed 's/|$//')
        IFS='|' read -ra floppies <<< "$floppies"

        for item in "${floppies[@]}"
        do
            [[ ${item^^} == ${cur^^}* ]] && COMPREPLY+=("$item")
        done
    }

    _dvds_comp() {
        local cur=$1
        local dvds
        local item

        dvds=$(VBoxManage list dvds | \
            grep "Location:" | \
            sed 's/Location:\s\+//' | \
            sed 's/\s/\\ /g' | \
            tr '\n' '|' | \
            sed 's/|$//')
        IFS='|' read -ra dvds <<< "$dvds"

        for item in "${dvds[@]}"
        do
            [[ ${item^^} == ${cur^^}* ]] && COMPREPLY+=("$item")
        done
    }

    # Complete registered VM names.
    # Issues are the same as in above function.
    _vms_comp() {
        local command=$1
        local cur=$2
        local vms
        local item

        compopt -o filenames
        vms=$(VBoxManage list $command | \
            awk -F ' {' '{ print $1 }' | \
            tr '\n' '|' | \
            sed 's/|$//' | \
            sed 's/"//g')
        IFS='|' read -ra vms <<< "$vms"
        for item in "${vms[@]}"
        do
            [[ ${item^^} == ${cur^^}* ]] && COMPREPLY+=("$item")
        done
    }

    _vms_state_comp() {
        local command=$1
        local cur=$2
        local vms
        local item

        compopt -o filenames

        vms=$(VBoxManage list vms -l | \
            egrep '^Name|State' | \
            egrep -B1 'State:\s+saved' | \
            grep Name |sed 's/Name:\s\+//' | \
            tr '\n' '|' | \
            sed 's/|$//' | \
            sed 's/"//g')
        IFS='|' read -ra vms <<< "$vms"
        for item in "${vms[@]}"
        do
            [[ ${item^^} == ${cur^^}* ]] && COMPREPLY+=("$item")
        done
    }

    _list_comp() {
        local cur=$1
        local list

        list=$(VBoxManage list | \
            grep '|' | \
            sed 's/\[.*\]//g'| \
            sed 's/VBoxManage list//' | \
            tr "\\n" " " | \
            sed 's/$/\n/' | \
            sed 's/\s\+//g' | \
            sed 's/|/ /g')
        COMPREPLY=( $(compgen -W "$list" -- ${cur}) )
    }

    _group_comp() {
        local cur=$1
        local list
        local item

        list=$(VBoxManage list groups | \
            tr '\n' '|' | \
            sed 's/|$//' | \
            sed 's/\s/\\ /g'| \
            sed 's/"//g')
        IFS='|' read -ra list <<< "$list"

        for item in "${list[@]}"
        do
            [[ ${item^^} == ${cur^^}* ]] && COMPREPLY+=("$item")
        done
    }

    _os_comp() {
        local cur=$1
        local list
        local item

        list=$(VBoxManage list ostypes | \
            egrep ^ID: | \
            sed 's/ID:\s\+//' | \
            tr '\n' '|' | \
            sed 's/|$//')
        IFS='|' read -ra list <<< "$list"

        for item in "${list[@]}"
        do
            [[ ${item^^} == ${cur^^}* ]] && COMPREPLY+=("$item")
        done
    }

    _dhcp_comp() {
        local cur=$1
        local list
        local item

        list=$(VBoxManage list dhcpservers | \
            grep NetworkName: | \
            sed 's/NetworkName:\s\+//' | \
            sed 's/\s/\\ /g'| \
            tr '\n' '|' | \
            sed 's/|$//')
        IFS='|' read -ra list <<< "$list"

        for item in "${list[@]}"
        do
            [[ ${item^^} == ${cur^^}* ]] && COMPREPLY+=("$item")
        done
    }

    _hostonlyif_comp() {
        local cur=$1
        local list
        local item

        list=$(VBoxManage list hostonlyifs | \
            egrep ^Name: | \
            sed 's/Name:\s\+//' | \
            sed 's/\s/\\ /g'| \
            tr '\n' '|' | \
            sed 's/|$//')
        IFS='|' read -ra list <<< "$list"

        for item in "${list[@]}"
        do
            [[ ${item^^} == ${cur^^}* ]] && COMPREPLY+=("$item")
        done
    }

    _bandwidthctl_comp() {
        local rules cur=$1
        local item

        _find_item_name 2
        rules=$(VBoxManage bandwidthctl "${name//\\/}" \
            list --machinereadable | \
            awk -F ',' '{print $1}' | \
            awk -F '=' '{print $2}' | \
            tr '\n' '|' | \
            sed 's/|$//' | \
            sed 's/\s/\\ /g')
        IFS='|' read -ra rules <<< "$rules"

        for item in "${rules[@]}"
        do
            [[ ${item^^} == ${cur^^}* ]] && COMPREPLY+=("$item")
        done
    }

    _snapshot_comp() {
        local snap cur=$1
        local item

        _find_item_name 2
        snap=$(VBoxManage snapshot "${name//\\/}" \
            list | \
            grep UUID |
            awk -F ': ' '{print $2}' | \
            sed 's/ (.*//' | \
            tr '\n' '|' | \
            sed 's/|$//' | \
            sed 's/\s/\\ /g')
        IFS='|' read -ra snap <<< "$snap"

        for item in "${snap[@]}"
        do
            [[ ${item^^} == ${cur^^}* ]] && COMPREPLY+=("$item")
        done
    }

    _webcam_comp() {
        local devs cur=$1
        local item

        _find_item_name 2
        devs=$(VBoxManage controlvm "${name//\\/}" \
            webcam list | \
            tr '\n' ' ' | \
            sed 's/|s$//')
        read -ra devs <<< "$devs"

        for item in "${devs[@]}"
        do
            [[ ${item^^} == ${cur^^}* ]] && COMPREPLY+=("$item")
        done
    }

    _webcam_avail_comp() {
        local devs cur=$1
        local item

        _find_item_name 2
        devs=$(VBoxManage list webcams | \
            grep dev | \
            tr '\n' ' ' | \
            sed 's/|s$//')
        read -ra devs <<< "$devs"

        for item in "${devs[@]}"
        do
            [[ ${item^^} == ${cur^^}* ]] && COMPREPLY+=("$item")
        done
    }

    _list_comp() {
        local list
        list=$(VBoxManage list | \
            grep '|' | \
            sed 's/\[.*\]//g'| \
            sed 's/VBoxManage list//' | \
            tr "\\n" " " | \
            sed 's/$/\n/' | \
            sed 's/\s\+//g' | \
            sed 's/|/ /g')
        COMPREPLY=( $(compgen -W "$list" -- ${cur}) )
    }

    _sharedfolder_comp() {
        local vm=$1
        local cur=$2
        local folders
        local item

        folders=$(VBoxManage showvminfo ${vm} --machinereadable | \
            grep SharedFolderName | \
            awk -F= '{print $2}' | \
            sed 's/\s/\\ /g'| \
            tr '\n' '|' | \
            sed 's/|$//' | \
            sed 's/"//g')
        IFS='|' read -ra folders <<< "$folders"

        for item in "${folders[@]}"
        do
            [[ ${item^^} == ${cur^^}* ]] && COMPREPLY+=("$item")
        done
    }


    COMP_WORDBREAKS=${COMP_WORDBREAKS//|/}  # remove pipe from comp word breaks
    COMPREPLY=()

    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"
    lastbutone="${COMP_WORDS[COMP_CWORD-2]}"
    if [[ COMP_CWORD -ge 2 ]]; then
        cmd="${COMP_WORDS[1]}"
        if [[ $cmd == "-q" ]]; then
                cmd="${COMP_WORDS[2]}"
        fi
    fi

    # all possible commands for the VBoxManage
    opts=$(VBoxManage -q help | \
        egrep -o "^\s\s[a-z]+ " | \
        grep -v VBoxManage | \
        awk '{print $1}'| \
        sort | \
        uniq)

    if [[ ${cur} == "-q" || ${COMP_CWORD} -eq 1 ]] ; then
        COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
        return 0
    fi

    case "${cmd}" in
        adoptstate)
            _find_item_name 2
            COMPREPLY=()
            [[ -z "${name}" ]] &&
                _vms_state_comp ${cur}
            ;;
        bandwidthctl)
            local items=(add set remove list)
            if [[ ${prev} == ${cmd} ]]; then
                _vms_comp vms ${cur}
            else
                _find_item_name 2
                subcommand=${COMP_WORDS[$((index+1))]}
                if [[ " ${items[@]} " == *" $subcommand "* ]]; then
                    case "${subcommand}" in
                        add)
                            items=(--type --limit)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        set)
                            if [[ ${prev} == "set" ]]; then
                                _bandwidthctl_comp ${cur}
                            else
                                [[ " ${COMP_WORDS[@]} " != *" --limit "* ]] && \
                                    COMPREPLY=( $(compgen -W "--limit" -- \
                                    ${cur}) )
                            fi
                            ;;
                        remove)
                            if [[ ${prev} == "remove" ]]; then
                                _bandwidthctl_comp ${cur}
                            fi
                            ;;
                        list)
                            if [[ ${prev} == "list" ]]; then
                                COMPREPLY=( $(compgen -W "--machinereadable" \
                                    -- ${cur}) )
                            fi
                            ;;
                    esac
                    case "${prev}" in
                        --type)
                            COMPREPLY=( $(compgen -W "disk network" -- ${cur}) )
                            ;;
                    esac
                else
                    [[ ${#COMPREPLY[@]} -eq 0 ]] && \
                        COMPREPLY=( $(compgen -W "${items[*]}" -- ${cur}) )
                fi
            fi
            ;;
        clonehd)
            if [[ ${prev} == ${cmd} ]]; then
                _hdd_comp ${cur}
            else
                _find_item_name 2
                items=(--format --variant --existing)
                _get_excluded_items "${items[@]}"
                COMPREPLY=( $(compgen -W "$result" -- ${cur}) )

                case "${prev}" in
                    --format)
                        COMPREPLY=( $(compgen -W "VDI VMDK VHD RAW" -- ${cur}) )
                        ;;
                    --variant)
                        COMPREPLY=( $(compgen -W "Standard Fixed Split2G Stream
                        ESX" -- ${cur}) )
                        ;;
                esac
            fi
            ;;
        clonevm)
            if [[ ${prev} == ${cmd} ]]; then
                _vms_comp vms ${cur}
            else
                _find_item_name 2
                items=(--snapshot --mode --options --name --groups --basefolder
                --uuid --register)
                _get_excluded_items "${items[@]}"
                COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                case "${prev}" in
                    --snapshot)
                        COMPREPLY=()
                        _snapshot_comp ${cur}
                        ;;
                    --mode)
                        COMPREPLY=( $(compgen -W "machine machineandchildren
                        all" -- ${cur}) )
                        ;;
                    --options)
                        COMPREPLY=( $(compgen -W "link keepallmacs keepnatmacs
                        keepdisknames" -- ${cur}) )
                        ;;
                    --groups)
                        COMPREPLY=()
                        _group_comp ${cur}
                        ;;
                    --basefolder)
                        COMPREPLY=( $(compgen -o dirnames -- ${cur}) )
                        ;;
                esac
            fi
            ;;
        closemedium)
            if [[ ${prev} == ${cmd} ]]; then
                COMPREPLY=( $(compgen -W "disk dvd floppy" -- ${cur}) )
            else
                case "${prev}" in
                    disk)
                        _hdd_comp ${cur}
                        ;;
                    dvd)
                        _dvds_comp ${cur}
                        ;;
                    floppy)
                        _floppy_comp ${cur}
                        ;;
                    *)
                        items=(--delete)
                        _get_excluded_items "${items[@]}"
                        COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                        ;;
                esac
            fi
            ;;
        controlvm)
            if [[ ${prev} == ${cmd} ]]; then
                _vms_comp runningvms ${cur}
            else
                local items=(acpipowerbutton acpisleepbutton clipboard
                cpuexecutioncap draganddrop guestmemoryballoon
                keyboardputscancode natpf1 nic1 nicpromisc1 nicproperty1
                nictrace1 nictracefile1 natpf2 nic2 nicpromisc2 nicproperty2
                nictrace2 nictracefile2 natpf3 nic3 nicpromisc3 nicproperty3
                nictrace3 nictracefile3 natpf4 nic4 nicpromisc4 nicproperty4
                nictrace4 nictracefile4 natpf5 nic5 nicpromisc5 nicproperty5
                nictrace5 nictracefile5 natpf6 nic6 nicpromisc6 nicproperty6
                nictrace6 nictracefile6 natpf7 nic7 nicpromisc7 nicproperty7
                nictrace7 nictracefile7 natpf8 nic8 nicpromisc8 nicproperty8
                nictrace8 pause plugcpu poweroff reset resume savestate
                screenshotpng setcredentials setlinkstate1 setlinkstate2
                setlinkstate3 setlinkstate4 setlinkstate5 setlinkstate6
                setlinkstate7 setlinkstate8 setvideomodehint teleport unplugcpu
                usbattach usbdetach vcpenabled vcpscreens vrde vrdeport
                vrdeproperty vrdevideochannelquality webcam)

                _find_item_name 2
                subcommand=${COMP_WORDS[$((index+1))]}

                if [[ " ${items[@]} " == *" $subcommand "* ]]; then
                    case "${subcommand}" in
                        nictracefile[1-8])
                            [[ ${prev} == "nictracefile"* ]] && \
                                COMPREPLY=( $(compgen -f -- ${cur}) )
                            ;;
                        nictrace[1-8])
                            [[ ${prev} == "nictrace"* ]] && \
                                COMPREPLY=( $(compgen -W "on off" -- ${cur}) )
                            ;;
                        nicpromisc[1-8])
                            [[ ${prev} == "nicpromisc"* ]] && \
                                COMPREPLY=( $(compgen -W "deny allow-vms
                                allow-all" -- ${cur}) )
                            ;;
                        nic[1-8])
                            [[ ${prev} == "nic"* ]] && \
                                COMPREPLY=( $(compgen -W "null nat bridged intnet
                                hostonly generic natnetwork" -- ${cur}) )
                            ;;
                        natpf[1-8])
                            [[ ${prev} == "natpf"* ]] && \
                                COMPREPLY=( $(compgen -W "delete tcp
                                udp" -- ${cur}) )
                            ;;
                        setlinkstate[1-8])
                            [[ ${prev} == "setlinkstate"* ]] && \
                                COMPREPLY=( $(compgen -W "on off" -- ${cur}) )
                            ;;
                        clipboard)
                            [[ ${prev} == "clipboard" ]] && \
                                COMPREPLY=( $(compgen -W "disabled hosttoguest
                                guesttohost bidirectional" -- ${cur}) )
                            ;;
                        draganddrop)
                            [[ ${prev} == "draganddrop" ]] && \
                                COMPREPLY=( $(compgen -W "disabled
                                hosttoguest" -- ${cur}) )
                            ;;
                        vrde|vcpenabled)
                            [[ ${prev} == "vrde" ||
                            ${prev} == "vcpenabled" ]] && \
                                COMPREPLY=( $(compgen -W "on off" -- ${cur}) )
                            ;;
                        vcpscreens)
                            [[ ${prev} == "vcpscreens" ]] && \
                                COMPREPLY=( $(compgen -W "all none" -- ${cur}) )
                            ;;
                        setcredentials)
                            tmp=(--passwordfile --allowlocallogon)
                            _get_excluded_items "${tmp[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        teleport)
                            tmp=(--host --port --maxdowntime --passwordfile
                            --password)
                            _get_excluded_items "${tmp[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        webcam)
                            [[ ${prev} == "webcam" ]] && \
                                COMPREPLY=( $(compgen -W "attach detach
                                list" -- ${cur}) )
                            [[ ${prev} == "detach" ]] && \
                                _webcam_comp ${cur}
                            [[ ${prev} == "attach" ]] && \
                                _webcam_avail_comp ${cur}
                            ;;
                    esac
                else
                    [[ ${#COMPREPLY[@]} -eq 0 ]] && \
                            COMPREPLY=( $(compgen -W "${items[*]}" -- ${cur}) )
                fi
            fi
            ;;
        convertfromraw)
            local items=(--format --variant --uuid)

            if [[ ${prev} == ${cmd} ]]; then
                COMPREPLY=( $(compgen -W "${items[*]}" -- ${cur}) )
            else
                _get_excluded_items "${items[@]}"
                COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                case "${prev}" in
                    --format)
                        COMPREPLY=( $(compgen -W "VDI VMDK VHD" -- ${cur}) )
                        ;;
                    --variant)
                        COMPREPLY=( $(compgen -W "Standard Fixed Split2G Stream
                        ESX" -- ${cur}) )
                        ;;
                esac
            fi
            ;;
        createhd)
            items=(--filename --size --sizebyte --diffparent --format --variant)
            if [[ ${prev} == ${cmd} ]]; then
                COMPREPLY=( $(compgen -W "${items[*]}" -- ${cur}) )
            else
                [[ " ${COMP_WORDS[@]} " == *" --size "* ||
                    " ${COMP_WORDS[@]} " == *" --sizebyte "* ]] &&
                    items=(--filename --diffparent --format --variant)
                _get_excluded_items "${items[@]}"
                COMPREPLY=( $(compgen -W "$result" -- ${cur}) )

                case "${prev}" in
                    --filename)
                        COMPREPLY=( $(compgen -- ${cur}) )
                        ;;
                    --diffparent)
                        COMPREPLY=()
                        _hdd_comp ${cur}
                        ;;
                    --format)
                        COMPREPLY=( $(compgen -W "VDI VMDK VHD" -- ${cur}) )
                        ;;
                    --variant)
                        COMPREPLY=( $(compgen -W "Standard Fixed Split2G Stream
                        ESX" -- ${cur}) )
                        ;;
                esac
            fi
            ;;
        createvm)
            items=(--name --groups --ostype --register --basefolder --uuid)
            if [[ ${prev} == ${cmd} ]]; then
                COMPREPLY=( $(compgen -W "${items[*]}" -- ${cur}) )
            else
                _get_excluded_items "${items[@]}"
                COMPREPLY=( $(compgen -W "$result" -- ${cur}) )

                case "${prev}" in
                    --groups)
                        COMPREPLY=()
                        _group_comp ${cur}
                        ;;
                    --ostype)
                        COMPREPLY=()
                        _os_comp ${cur}
                        ;;
                    --basefolder)
                        COMPREPLY=( $(compgen -o dirnames -- ${cur}) )
                        ;;
                    --variant)
                        COMPREPLY=( $(compgen -W "Standard Fixed Split2G Stream
                        ESX" -- ${cur}) )
                        ;;
                esac
            fi
            ;;
        debugvm)
            items=(dumpguestcore info injectnmi log logdest logflags osdetect
            osinfo getregisters setregisters show statistics)
            if [[ ${prev} == ${cmd} ]]; then
                _vms_comp runningvms ${cur}
            else
                _find_item_name 2
                subcommand=${COMP_WORDS[$((index+1))]}
                if [[ " ${items[@]} " == *" $subcommand "* ]]; then
                    case "${subcommand}" in
                        dumpguestcore)
                            _get_excluded_items "--filename"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        log|logdest|logflags)
                            items=(--release --debug)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        getregisters|setregisters)
                            _get_excluded_items "--cpu"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        show)
                            items=(--human-readable --sh-export --sh-eval
                            --cmd-set)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        statistics)
                            items=(--reset --pattern)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                    esac
                    _get_excluded_items "--descriptions"
                    COMPREPLY+=( $(compgen -W "$result" -- ${cur}) )
                else
                    [[ "${prev}" == "--filename" ]] && \
                        COMPREPLY=( $(compgen -- ${cur}) )
                    [[ ${#COMPREPLY[@]} -eq 0 ]] && \
                        COMPREPLY=( $(compgen -W "${items[*]}" -- ${cur}) )
                fi
            fi
            ;;
        dhcpserver)
            items=(add modify remove)
            subcommand=${COMP_WORDS[2]}
            if [[ " ${items[@]} " == *" $subcommand "* ]]; then
                case "${subcommand}" in
                    add|modify)
                        items=(--ip --netmask --lowerip --upperip)

                        [[ " ${COMP_WORDS[@]} " != *" --ifname"* &&
                            " ${COMP_WORDS[@]} " != *" --netname"* ]] &&
                            items+=(--netname --ifname)

                        [[ " ${COMP_WORDS[@]} " != *" --enable"* &&
                            " ${COMP_WORDS[@]} " != *" --disable"* ]] &&
                            items+=(--enable --disable)

                        _get_excluded_items "${items[@]}"
                        COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                        ;;
                    remove)
                        case "${prev}" in
                            --netname)
                                COMPREPLY=()
                                _dhcp_comp ${cur}
                                ;;
                            --ifname)
                                COMPREPLY=()
                                _hostonlyif_comp ${cur}
                                ;;
                        esac

                        if [[ " ${COMP_WORDS[@]} " != *" --ifname"* &&
                            " ${COMP_WORDS[@]} " != *" --netname"* ]]; then
                            items=(--netname --ifname)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                        fi
                        ;;
                esac
            else
                [[ ${#COMPREPLY[@]} -eq 0 ]] && \
                    COMPREPLY=( $(compgen -W "${items[*]}" -- ${cur}) )
            fi
            ;;
        discardstate)
            _find_item_name 2
            COMPREPLY=()
            [[ -z "${name}" ]] &&
                _vms_state_comp ${cur}
            ;;
        "export")
            items=( --manifest --iso --options --vsys)
            if [[ ${prev} == ${cmd} ]]; then
                _vms_comp vms ${cur}
            else
                [[ " ${COMP_WORDS[@]} " != *" -o "* &&
                    " ${COMP_WORDS[@]} " != *" --output "* ]] &&
                    items+=(-o --output)
                [[ " ${COMP_WORDS[@]} " != *" --legacy09 "* &&
                    " ${COMP_WORDS[@]} " != *" --ovf09 "* &&
                    " ${COMP_WORDS[@]} " != *" --ovf10 "* &&
                    " ${COMP_WORDS[@]} " != *" --ovf20 "* ]] &&
                    items+=(--legacy09 --ovf09 --ovf10 --ovf20)
                [[ " ${COMP_WORDS[@]} " == *" --vsys "* ]] &&
                    items+=(--product --producturl --vendor --vendorurl
                    --version --description --eula --eulafile)
                _get_excluded_items "${items[@]}"
                COMPREPLY=( $(compgen -W "$result" -- ${cur}) )

                case "${prev}" in
                    --options)
                        COMPREPLY=( $(compgen -W "manifest iso nomacs
                        nomacsbutnat" -- ${cur}) )
                        ;;
               esac
                [[ ${#COMPREPLY[@]} -eq 0 ]] && \
                    COMPREPLY=( $(compgen -W "${items[*]}" -- ${cur}) )
            fi
            ;;
        extpack)
            items=(install uninstall cleanup)
            subcommand=${COMP_WORDS[2]}
            if [[ " ${items[@]} " == *" $subcommand "* ]]; then
                case "${subcommand}" in
                    install)
                        _get_excluded_items "--replace"
                        COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                        ;;
                    uninstall)
                        _get_excluded_items "--force"
                        COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                        ;;
                    cleanup)
                        COMPREPLY=()
                        ;;
                    --replace)
                        COMPREPLY=()
                        ;;
                esac
            else
                [[ ${#COMPREPLY[@]} -eq 0 ]] && \
                    COMPREPLY=( $(compgen -W "${items[*]}" -- ${cur}) )
            fi
            ;;
        getextradata)
            if [[ ${prev} == ${cmd} ]]; then
                COMPREPLY=( $(compgen -W "global" -- ${cur}) )
                _vms_comp vms ${cur}
            else
                _get_excluded_items "enumerate"
                COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
            fi
            ;;
        guestcontrol)
            local items=(execute copyfrom copyto cp createdir createdirectory
            mkdir md removedir removedirectory rmdir removefile rm ren rename mv
            createtemp createtemporary mktemp list process pskill kill session
            stat updateadditions watch)

            if [[ ${prev} == ${cmd} ]]; then
                _vms_comp runningvms ${cur}
            else
                _find_item_name 2
                subcommand=${COMP_WORDS[$((index+1))]}
                if [[ " ${items[@]} " == *" $subcommand "* ]]; then
                    case "${subcommand}" in
                        exec|execute)
                            items=(--image --username --passwordfile --password
                            --domain --verbose --timeout --environment
                            --wait-exit --wait-stdout --wait-stderr --dos2unix
                            --unix2dos)

                            [[ " ${COMP_WORDS[@]} " == *" --password "* ||
                                " ${COMP_WORDS[@]} " == *" --passwordfile "* ]] &&
                                items=(--image --username --domain  --verbose
                                --timeout --environment --wait-exit --wait-stdout
                                --wait-stderr --dos2unix --unix2dos)

                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        copyfrom|copyto|cp)
                            items=(--username --passwordfile --password
                            --domain --verbose --dryrun --follow --recursive)
                            [[ " ${COMP_WORDS[@]} " == *" --password "* ||
                                " ${COMP_WORDS[@]} " == *" --passwordfile "* ]] &&
                                items=(--username --domain --verbose --dryrun
                                --follow --recursive)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        createdirectory|createdir|mkdir|md)
                            items=(--username --passwordfile --password
                            --domain --verbose --parents --mode)
                            [[ " ${COMP_WORDS[@]} " == *" --password "* ||
                                " ${COMP_WORDS[@]} " == *" --passwordfile "* ]] &&
                                items=(--username --domain --verbose --parents
                                --mode)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        removedir|removedirectory|rmdir)
                            items=(--username --domain --verbose)
                            [[ " ${COMP_WORDS[@]} " != *" --password "* &&
                                " ${COMP_WORDS[@]} " != *" --passwordfile "* ]] &&
                                items+=(--passwordfile --password)
                            [[ " ${COMP_WORDS[@]} " != *" --recursive "* &&
                                " ${COMP_WORDS[@]} " != *" -R "* &&
                                " ${COMP_WORDS[@]} " != *" -r "* ]] &&
                                items+=(--recursive -R -r)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        removefile|rm|rename|ren|mv)
                            items=(--username --domain --verbose)
                            [[ " ${COMP_WORDS[@]} " != *" --password "* &&
                                " ${COMP_WORDS[@]} " != *" --passwordfile "* ]] &&
                                items+=(--passwordfile --password)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        createtemporary|createtemp|mktemp)
                            items=(--username --domain --verbose --directory
                            --secure --tmpdir --mode)
                            [[ " ${COMP_WORDS[@]} " != *" --password "* &&
                                " ${COMP_WORDS[@]} " != *" --passwordfile "* ]] &&
                                items+=(--passwordfile --password)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        list)
                            items=(--verbose)
                            [[ " ${COMP_WORDS[@]} " != *" all "* &&
                                " ${COMP_WORDS[@]} " != *" sessions  "* &&
                                " ${COMP_WORDS[@]} " != *" processes "* &&
                                " ${COMP_WORDS[@]} " != *" files "* ]] &&
                                items+=(all sessions processes files)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        process)
                            if [[ " ${COMP_WORDS[@]} " == *" process kill "* ]];
                            then
                                items=(--verbose)
                                [[ " ${COMP_WORDS[@]} " != *" --session-name "* &&
                                    " ${COMP_WORDS[@]} " != *" --session-id "* ]] &&
                                    items+=(--session-id --session-name)
                                _get_excluded_items "${items[@]}"
                                COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            else
                                _get_excluded_items "kill"
                                COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            fi
                            ;;
                        pskill|pkill|kill)
                            items=(--verbose)
                            [[ " ${COMP_WORDS[@]} " != *" --session-name "* &&
                                " ${COMP_WORDS[@]} " != *" --session-id "* ]] &&
                                items+=(--session-id --session-name)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        session)
                            _get_excluded_items "close"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        stat)
                            if [[ "${cur}" == "stat" ]]; then
                                COMPREPLY=( $(compgen -- ${cur}) )
                            else
                                items=(--username --domain --verbose)
                                [[ " ${COMP_WORDS[@]} " != *" --password "* &&
                                    " ${COMP_WORDS[@]} " != *" --passwordfile "* ]] &&
                                    items+=(--passwordfile --password)
                                _get_excluded_items "${items[@]}"
                                COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            fi
                            ;;
                        updateadditions)
                            items=(--source --verbose --wait-start)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        watch)
                            _get_excluded_items "--verbose"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                    esac
                    case "${prev}" in
                        close)
                            items=(--verbose)
                            [[ " ${COMP_WORDS[@]} " != *" --session-name "* &&
                                " ${COMP_WORDS[@]} " != *" --session-id "* &&
                                " ${COMP_WORDS[@]} " != *" --all "* ]] &&
                                items+=(--session-id --session-name --all)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        --image)
                            COMPREPLY=( $(compgen -- ${cur}) )
                            ;;
                        --tmpdir)
                            COMPREPLY=( $(compgen -o dirnames -- ${cur}) )
                            ;;
                        --source)
                            compopt -o nospace
                            COMPREPLY=( $(compgen -o plusdirs -f -X '!*.iso' \
                                -- ${cur}) )
                            [[ ${#COMPREPLY[@]} = 1 && \
                                "${COMPREPLY[0]}" != *".iso" ]] && \
                                COMPREPLY[0]="${COMPREPLY[0]}/"
                            ;;
                    esac
                else
                    [[ ${#COMPREPLY[@]} -eq 0 ]] && \
                        COMPREPLY=( $(compgen -W "${items[*]}" -- ${cur}) )
                fi
            fi
            ;;
        guestproperty)
            ;;
        hostonlyif)
            ;;
        import)
            ;;
        list)
            if [[ ${prev} == ${cmd} ]]; then
                _list_comp ${cur}
            else
                case "${prev}" in
                    --long|-l)
                        COMPREPLY=()
                        ;;
                    *)
                        COMPREPLY=( $(compgen -W "-l --long" -- ${cur}) )
                        ;;
                esac
            fi
            ;;
        metrics)
            ;;
        modifyhd)
            ;;
        modifyvm)
            ;;
        natnetwork)
            ;;
        registervm)
            if [[ ${prev} == ${cmd} ]]; then
                bind 'set mark-directories on'
                compopt -o nospace
                IFS=$'\n'
                COMPREPLY=( $(compgen -o plusdirs -f -X '!*.vbox' -- ${cur}) )
                [[ ${#COMPREPLY[@]} = 1 && "${COMPREPLY[0]}" != *".vbox" ]] && \
                    COMPREPLY[0]="${COMPREPLY[0]}/"
            fi
            ;;
        setextradata)
            if [[ ${prev} == ${cmd} ]]; then
                COMPREPLY=( $(compgen -W "global" -- ${cur}) )
                _vms_comp vms ${cur}
            fi
            ;;
        setproperty)
            items=(machinefolder hwvirtexclusive vrdeauthlibrary
            websrvauthlibrary vrdeextpack autostartdbpath loghistorycount
            defaultfrontend)
            subcommand=${COMP_WORDS[2]}
            if [[ "${prev}" == "${cmd}" ]]; then
                COMPREPLY=( $(compgen -W "${items[*]}" -- ${cur}) )
            else
                case "${prev}" in
                    machinefolder)
                        COMPREPLY=( $(compgen -o dirnames -- ${cur}) )
                        _get_excluded_items "default"
                        COMPREPLY+=( $(compgen -W "$result" -- ${cur}) )
                        ;;
                    hwvirtexclusive)
                        COMPREPLY+=( $(compgen -W "on off" -- ${cur}) )
                        ;;
                    websrvauthlibrary)
                        COMPREPLY+=( $(compgen -W "default null" -- ${cur}) )
                        ;;
                    vrdeextpack)
                        COMPREPLY+=( $(compgen -W "default null" -- ${cur}) )
                        ;;
                    autostartdbpath)
                        COMPREPLY=( $(compgen -o dirnames -- ${cur}) )
                        COMPREPLY+=( $(compgen -W "null" -- ${cur}) )
                        ;;
                    defaultfrontend)
                        COMPREPLY=( $(compgen -W "null" -- ${cur}) )
                        ;;
                esac
            fi
            ;;
        sharedfolder)
            items=(add remove)
            subcommand=${COMP_WORDS[2]}
            case "${prev}" in
                add|remove)
                    _vms_comp vms ${cur}
                    ;;
                --hostpath)
                    COMPREPLY=( $(compgen -o dirnames -- ${cur}) )
                    ;;
                --name)
                    if [[ ${subcommand} == "remove" ]]; then
                        _find_item_name 3
                        _sharedfolder_comp "${name}" "${cur}"
                    fi
                    ;;
            esac
            if [[ ${#COMPREPLY[@]} -eq 0 ]]; then
                case "${subcommand}" in
                    add)
                        items=(--name --hostpath --transient --readonly
                        --automount)
                        _get_excluded_items "${items[@]}"
                        COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                        ;;
                    remove)
                        items=(--name --transient)
                        _get_excluded_items "${items[@]}"
                        COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                        ;;
                esac
            fi

            [[ ${#COMPREPLY[@]} -eq 0 ]] && \
                COMPREPLY=( $(compgen -W "${items[*]}" -- ${cur}) )
            ;;
        showhdinfo)
            if [[ ${prev} == ${cmd} ]]; then
                _hdd_comp ${cur}
            fi
            ;;
        showvminfo)
            if [[ ${prev} == ${cmd} ]]; then
                _vms_comp vms ${cur}
            else
                if [[ " ${COMP_WORDS[@]} " == *" --log "* ]]; then
                    COMPREPLY=()
                elif [[ " ${COMP_WORDS[@]} " == *" --details "* ||
                    " ${COMP_WORDS[@]} " == *" --machinereadable "*  ]]; then
                    local items=(--details --machinereadable)
                    _get_excluded_items "${items[@]}"
                    COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                else
                    local items=(--details --machinereadable --log)
                    _get_excluded_items "${items[@]}"
                    COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                fi
            fi
            ;;
        snapshot)
            items=(take delete restore restorecurrent edit list showvminfo)
            if [[ ${prev} == ${cmd} ]]; then
                _vms_comp vms ${cur}
            else
                _find_item_name 2
                subcommand=${COMP_WORDS[$((index+1))]}
                if [[ " ${items[@]} " == *" $subcommand "* ]]; then
                    case "${subcommand}" in
                        take)
                            items=(--description --live)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        delete|restore|showvminfo)
                            _snapshot_comp ${cur}
                            ;;
                        restorecurrent)
                            COMPREPLY=()
                            ;;
                        edit)
                            if [[ ${prev} == "edit" &&
                                ${#COMP_WORDS[@]} == 5 ]]; then
                                _snapshot_comp ${cur}
                                COMPREPLY+=("--current")
                            else
                                items=(--name --description)
                                _get_excluded_items "${items[@]}"
                                COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            fi
                            ;;
                        list)
                            items=(--details --machinereadable)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                    esac
                else
                    [[ ${#COMPREPLY[@]} -eq 0 ]] && \
                        COMPREPLY=( $(compgen -W "${items[*]}" -- ${cur}) )
                fi
            fi
            ;;
        startvm)
            if [[ ${prev} == ${cmd} ]]; then
                _vms_comp vms ${cur}
            elif [[ "${prev}" == "--type" ]]; then
                COMPREPLY=( $(compgen -W "gui sdl headless" -- ${cur}) )
            else
                local items=(--type)
                _get_excluded_items "${items[@]}"
                COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
            fi
            ;;
        storageattach)
            if [[ ${prev} == ${cmd} ]]; then
                _vms_comp vms ${cur}
            else
                _find_item_name 2
                local items=(--storagectl --port --device --type --medium --mtype
                --comment --setuuid --setparentuuid --passthrough --tempeject
                --nonrotational --discard --bandwidthgroup --forceunmount
                --server --target --tport --lun --encodedlun --username
                --password --initiator --intnet)
                _get_excluded_items "${items[@]}"
                COMPREPLY=( $(compgen -W "$result" -- ${cur}) )

                case "${prev}" in
                    --type)
                        COMPREPLY=( $(compgen -W "dvddrive hdd fdd" -- ${cur}) )
                        ;;
                    --medium)
                        COMPREPLY=()
                        local tmp=(none emptydrive additions)
                        _hdd_comp ${cur}
                        _floppy_comp ${cur}
                        _dvds_comp ${cur}
                        for item in "${tmp[@]}"
                        do
                            [[ ${item^^} == ${cur^^}* ]] && COMPREPLY+=("$item")
                        done
                        ;;
                    --mtype)
                        COMPREPLY=( $(compgen -W "normal writethrough immutable
                        shareable readonly multiattach" -- ${cur}) )
                        ;;
                    --passthrough|--tempeject|--nonrotational|--discard)
                        COMPREPLY=( $(compgen -W "on off" -- ${cur}) )
                        ;;
                esac
            fi
            ;;
        storagectl)
            local items=(--name --add --controller --portcount --hostiocache
            --bootable --remove)
            if [[ ${prev} == ${cmd} ]]; then
                _vms_comp vms ${cur}
            else
                _get_excluded_items "${items[@]}"
                COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                case "${prev}" in
                    --add)
                        COMPREPLY=( $(compgen -W "ide sata scsi floppy
                        sas" -- ${cur}) )
                        ;;
                    --controller)
                        COMPREPLY=( $(compgen -W "LSILogic LSILogicSAS BusLogic
                        IntelAHCI PIIX3 PIIX4 ICH6 I82078" -- ${cur}) )
                        ;;
                    --bootable|--hostiocache)
                        COMPREPLY=( $(compgen -W "on off" -- ${cur}) )
                        ;;
                esac
            fi
            ;;
        unregistervm)
            if [[ ${prev} == ${cmd} ]]; then
                _vms_comp vms ${cur}
            else
                local items=(--delete)
                _get_excluded_items "${items[@]}"
                COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
            fi
            ;;
        usbfilter)
            if [[ COMP_CWORD -ge 3 ]]; then
                subcommand="${COMP_WORDS[2]}"
                if [[ $subcommand == "${cmd}" ]]; then
                    subcommand="${COMP_WORDS[3]}"
                fi
            fi

            if [[ ${prev} == ${cmd} ]]; then
                COMPREPLY=( $(compgen -W "add modify remove" -- ${cur}) )
            else
                case "${prev}" in
                    --target)
                        _vms_comp vms ${cur}
                        COMPREPLY+=( $(compgen -W "global" -- ${cur}) )
                        ;;
                    --action)
                        COMPREPLY=( $(compgen -W "ignore hold" -- ${cur}) )
                        ;;
                    --active|--remote)
                        COMPREPLY=( $(compgen -W "yes no" -- ${cur}) )
                        ;;
                esac
                if [[ ${#COMPREPLY[@]} -eq 0 ]]; then
                    case "${subcommand}" in
                        add|modify)
                            local items=(--target --name --action --active
                            --vendorid --productid --revision --manufacturer
                            --product --remote --serialnumber --maskedinterfaces)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                        remove)
                            local items=(--target)
                            _get_excluded_items "${items[@]}"
                            COMPREPLY=( $(compgen -W "$result" -- ${cur}) )
                            ;;
                    esac
                fi
            fi
            ;;
    esac
}
complete -o default -F _VBoxManage VBoxManage

# vim: set ft=sh tw=80 sw=4 et :
