function _comp_contains() {
	# Takes two strings containing space seperated words and checks if one of the
    # words in the second list matches one in the first.
    # E.g.: `_comp_contains "test test2" "no nope test"` returns true.
    #
    # Parameters
    #   list_lookup     (string)        Space seperated words to search in
    #   list_search     (string)        Space seperated words to search
    #
    # Returns
    #   retval          (int)           Error / Success
    #
    # Side effects
    #   None
    #
    for word in $2; do
        if [[ $1 =~ (^|[[:space:]])${word}($|[[:space:]]) ]]; then
            return 0
        fi
    done;

    return 1
}

function _string_filter() {
    # Takes the name of a variable that contains the current option list as a string.
    # Iterates over the current command line and removes all options that are already
    # present.
    #
    # A second string of space seperated words can be passed
    # that are excluded from filtering. This is useful, when certain options are allowed
    # to appear multiple times.
    #
    # Parameters
    #   opts            (string)        Space seperated words to filter (call by ref)
    #   exclude         (string)        Space seperated words to exclude
    #
    # Returns
    #   reval           (int)           Error / Succes
    #
    # Side effects
    #   filtered option list is stored in first variable (passwd by ref)
    #
    local Opts=$1 filter exclude cur count

    _get_comp_words_by_ref cur
    filter=" ${!Opts} "
    exclude=$2
    count=0

    # iterate over each word inside the current command line
    for var in ${COMP_LINE}; do
        # exclude the current word to allow full specified options to be space completed
        # the current word may be a partial word that matches a previously used flag.  In that case
        # only exclude the first occurance of the current word.
	if [[ "$var" == "$cur" ]] && [[ "$count" == 0 ]]; then
        count=+1
        continue;
        # exclude words from the exclusion list
        elif _comp_contains "$exclude" $var; then
            continue
        # otherwise remove the word from the filter list
        else
            # remove actual option
            filter=( "${filter/ ${var} / }" )
        fi
    done

    # _comp_upvars -v $Opts "$filter" - starting in v2.12, use _comp_upvars
    _upvars -v $Opts "$filter"
}

_corelight-update()
{
    local cur prev
    COMPREPLY=()
    _get_comp_words_by_ref cur prev
    prev2="${COMP_WORDS[COMP_CWORD-2]}"
    prev3="${COMP_WORDS[COMP_CWORD-3]}"
    local level1_opts="-b -f -o -v -version -help add import remove show update encrypt"
    local show_opts="-policies -policy -global -network -json -file -help"
    local show_defaultOpts="-policy -global"
    local show_jsonOpts="-json"
    local show_defaultJsonOpts="-default -json"
    local show_policyOpts="-json -file"
    local update_opts="-global -global-settings -network-settings -policy -file"
    local update_opts2="-file -global -policy"
    local update_fileOpts="-file -default"
    local update_defaultJsonOpts="-default -json"
    local add_opts="-policies"
    local import_opts="-f -file -policy -v0.23 -help"
    local remove_opts=" -all-policies -policies -policy"

    case "${prev}" in
        # '-help' does not have any options
        -help)
            return 0
            ;;
        # -file has to same options for all flags
        -file)
            _filedir
            return 0
            ;;
        # 'encrypt' does not have any options
        encrypt)
            return 0
            ;;
        # '-version' does not have any options
        -version)
            return 0
            ;;
    esac

    # show options
    if [[ ${COMP_LINE} =~ (^|[[:space:]])"show"($|[[:space:]]) ]] ; then
        # "show -policies" does not have any options
        if [[ ${COMP_LINE} =~ (^|[[:space:]])"-policies"($|[[:space:]]) ]] ; then
            return 0
        fi
        _string_filter show_opts
        # if "show -policy" don't provide -network or -global as options
        if [[ ${COMP_LINE} =~ (^|[[:space:]])"-policy"($|[[:space:]]) ]] ; then
            _string_filter show_policyOpts
            case "${prev}" in
                -policy)
                    local policies=$(for x in `corelight-update show -policies | awk '$0 !~ "Configured Polic"'`; do echo ${x} ; done )" -default"
                    COMPREPLY=( $(compgen -W "${policies}" -- ${cur}) )
                    return 0
                    ;;
            esac
            COMPREPLY=($(compgen -W "${show_policyOpts}" -- ${cur}) )
            return 0
        fi
        # The only usable option for "show -network" is -json
        if [[ ${COMP_LINE} =~ (^|[[:space:]])"-network"($|[[:space:]]) ]] ; then
            _string_filter show_jsonOpts
            COMPREPLY=($(compgen -W "${show_jsonOpts}" -- ${cur}) )
            return 0
        fi
        if [[ ${COMP_LINE} =~ (^|[[:space:]])"-global"($|[[:space:]]) ]] ; then
            _string_filter show_defaultJsonOpts
            COMPREPLY=($(compgen -W "${show_defaultJsonOpts}" -- ${cur}) )
            return 0
        fi
        # If none of the above match, show all the "show" options
        COMPREPLY=($(compgen -W "${show_opts}" -- ${cur}) )
        return 0
    fi

    # update options
    if [[ ${COMP_LINE} =~ (^|[[:space:]])"update"($|[[:space:]]) ]] ; then
        case "${prev}" in
            # '-network-settings' does not have any options
            -network-settings)
                return 0
                ;;
            # '-global-settings' does not have any options
            -global-settings)
                return 0
                ;;
            # -default does not have any options
            -default)
                return 0
                ;;
        esac

        # "-policy", "-network" and "-global" are mutually exclusive
        if [[ ${COMP_LINE} =~ (^|[[:space:]])"-policy"($|[[:space:]]) ]] ; then
            case "${prev}" in
                -policy)
                    local policies=$(for x in `corelight-update show -policies | awk '$0 !~ "Configured Polic"'`; do echo ${x} ; done )
                    COMPREPLY=( $(compgen -W "${policies}" -- ${cur}) )
                    return 0
                    ;;
            esac
            _string_filter update_fileOpts
            COMPREPLY=($(compgen -W "${update_fileOpts}" -- ${cur}) )
            return 0
        fi
        # '-global' only has a '-file' option
        if [[ ${COMP_LINE} =~ (^|[[:space:]])"-global"($|[[:space:]]) ]] ; then
            _string_filter update_fileOpts
            COMPREPLY=($(compgen -W "${update_fileOpts}" -- ${cur}) )
            return 0
        fi
        # only 'update -policy' and 'update -global' have a '-file' option
        if [[ ${COMP_LINE} =~ (^|[[:space:]])"-file"($|[[:space:]]) ]] ; then
            _string_filter update_opts2
            case "${prev}" in
                -policy)
                    local policies=$(for x in `corelight-update show -policies | awk '$0 !~ "Configured Polic"'`; do echo ${x} ; done )
                    COMPREPLY=( $(compgen -W "${policies}" -- ${cur}) )
                    return 0
                    ;;
                -global)
                    return 0
                    ;;
            esac
            COMPREPLY=($(compgen -W "${update_opts2}" -- ${cur}) )
            return 0
        fi

        # If none of the above match, show all the "update" options
        _string_filter update_opts
        COMPREPLY=($(compgen -W "${update_opts}" -- ${cur}) )
        return 0
    fi

    # import options
    if [[ ${COMP_LINE} =~ (^|[[:space:]])"import"($|[[:space:]]) ]] ; then
        _string_filter import_opts
        case "${prev}" in
            -f)
                COMPREPLY=( $(compgen -W "${import_opts}" -- ${cur}) )
                return 0
                ;;
            -file)
                _filedir
                return 0
                ;;
            # -policy does not have any suggested options, it needs the name of a policy to import
            # -help does not have any options
            -policy | -help)
                return 0
                ;;
        esac

        COMPREPLY=($(compgen -W "${import_opts}" -- ${cur}) )
        return 0
    fi

    # add options
    if [[ ${COMP_LINE} =~ (^|[[:space:]])"add"($|[[:space:]]) ]] ; then
        # add currently only has a single option
        _string_filter add_opts
        COMPREPLY=($(compgen -W "${add_opts}" -- ${cur}) )
        return 0
    fi

    # remove options
    if [[ ${COMP_LINE} =~ (^|[[:space:]])"remove"($|[[:space:]]) ]] ; then
        _string_filter remove_opts
        case "${prev}" in
            # -all-policies does not have any options
            -all-policies)
                return 0
                ;;
        esac
        # if "remove -policy" or "remove -policies"
        if [[ ${COMP_LINE} =~ (^|[[:space:]])"-polic" ]] ; then
            _get_comp_words_by_ref cur
            if [[ ${cur} =~ "-polic" ]] ; then
                COMPREPLY=($(compgen -W "${remove_opts}" -- ${cur}) )
                return 0
            fi

            # multiple policies can be removed at one time, "remove -policies" should only suggest each policy name once
            local policies=$(for x in `corelight-update show -policies | awk '$0 !~ "Configured Polic"'`; do echo ${x} ; done )
            local outOpts
            for i in ${policies}; do
                # exclude the current word to allow full specified options to be space completed
                # the current word may be a partial word that matches a previously used flag.  In that case
                # only exclude the first occurance of the current word.
                local count=0
                for d in ${COMP_LINE}; do
                    if [[ "$d" == "$cur" ]] && [[ "$count" == 0 ]]; then
                        count=+1
                        continue;
                    fi
                    if [[ $i = $d ]]; then
                        continue 2;
                    fi
                done
                outOpts+=" $i "
            done
            COMPREPLY=( $(compgen -W "${outOpts}" -- ${cur}) )
            return 0
        fi

        COMPREPLY=($(compgen -W "${remove_opts}" -- ${cur}) )
        return 0
    fi

    # run once options
    if [[ ${COMP_LINE} =~ (^|[[:space:]])"-o"($|[[:space:]]) ]] ; then
        # multiple package bundles can be built at one time, "-b" should only suggest each policy name once
        local policies=$(for x in `corelight-update show -policies | awk '$0 !~ "Configured Polic"'`; do echo ${x} ; done )
        _get_comp_words_by_ref cur
        local outOpts
        for i in ${policies}; do
            # exclude the current word to allow full specified options to be space completed
            # the current word may be a partial word that matches a previously used flag.  In that case
            # only exclude the first occurance of the current word.
            local count=0
            for d in ${COMP_LINE}; do
                if [[ "$d" == "$cur" ]] && [[ "$count" == 0 ]]; then
                    count=+1
                    continue;
                fi
                if [[ $i = $d ]]; then
                    continue 2;
                fi
            done
            outOpts+=" $i "
        done
        COMPREPLY=( $(compgen -W "${outOpts}" -- ${cur}) )
        return 0
    fi

    # build and push a package bundle options
    if [[ ${COMP_LINE} =~ (^|[[:space:]])"-b"($|[[:space:]]) ]] ; then
        # multiple package bundles can be built at one time, "-b" should only suggest each policy name once
        local policies=$(for x in `corelight-update show -policies | awk '$0 !~ "Configured Polic"'`; do echo ${x} ; done )
        _get_comp_words_by_ref cur
        local outOpts
        for i in ${policies}; do
            # exclude the current word to allow full specified options to be space completed
            # the current word may be a partial word that matches a previously used flag.  In that case
            # only exclude the first occurance of the current word.
            local count=0
            for d in ${COMP_LINE}; do
                if [[ "$d" == "$cur" ]] && [[ "$count" == 0 ]]; then
                    count=+1
                    continue;
                fi
                if [[ $i = $d ]]; then
                    continue 2;
                fi
            done
            outOpts+=" $i "
        done
        COMPREPLY=( $(compgen -W "${outOpts}" -- ${cur}) )
        return 0
    fi

    # force push options
    if [[ ${COMP_LINE} =~ (^|[[:space:]])"-f"($|[[:space:]]) ]] ; then
        # multiple package bundles can be built at one time, "-b" should only suggest each policy name once
        local policies=$(for x in `corelight-update show -policies | awk '$0 !~ "Configured Polic"'`; do echo ${x} ; done )
        _get_comp_words_by_ref cur
        local outOpts
        for i in ${policies}; do
            # exclude the current word to allow full specified options to be space completed
            # the current word may be a partial word that matches a previously used flag.  In that case
            # only exclude the first occurance of the current word.
            local count=0
            for d in ${COMP_LINE}; do
                if [[ "$d" == "$cur" ]] && [[ "$count" == 0 ]]; then
                    count=+1
                    continue;
                fi
                if [[ $i = $d ]]; then
                    continue 2;
                fi
            done
            outOpts+=" $i "
        done
        COMPREPLY=( $(compgen -W "${outOpts}" -- ${cur}) )
        return 0
    fi

    _string_filter level1_opts
    COMPREPLY=($(compgen -W "${level1_opts}" -- ${cur}) )
    return 0

}
complete -F _corelight-update corelight-update
