#-----------------------------------------------------------------------------
#
#  TSDuck - The MPEG Transport Stream Toolkit
#  Copyright (c) 2005-2023, Thierry Lelegard
#  All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are met:
#
#  1. Redistributions of source code must retain the above copyright notice,
#     this list of conditions and the following disclaimer.
#  2. Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
#  THE POSSIBILITY OF SUCH DAMAGE.
#
#-----------------------------------------------------------------------------
#
#  Bash completion for TSDuck commands.
#
#-----------------------------------------------------------------------------

# All TSDuck commands (automatically updated by makefile).
__ts_cmds=(tsanalyze tsbitrate tscharset tscmp tscrc32 tsdate tsdektec tsdump tsecmg tseit tsemmg tsfclean tsfixcc tsftrunc tsgenecm tshides tslatencymonitor tslsdvb tsp tspacketize tspcap tspcontrol tspsi tsresync tsscan tssmartcard tsstuff tsswitch tstabcomp tstabdump tstables tsterinfo tstestecmg tsvatek tsversion tsxml)

# A filter to remove CR on Windows.
[[ $OSTYPE == cygwin || $OSTYPE == msys ]] && __ts_lines() { dos2unix; } || __ts_lines() { cat; }

# Assign COMPREPLY with the expansion of files or directories.
# Syntax: __ts_compgen_fd {-f|-d} current-value
__ts_compgen_fd()
{
    # Path expansion.
    local saved="$IFS"
    IFS=$'\n'
    COMPREPLY=($(compgen $1 -- "$2"))
    IFS="$saved"
    local i name
    for ((i=0; i<${#COMPREPLY[@]}; i++)); do
        name=${COMPREPLY[$i]//\\/\\\\}
        name=${name// /\\ }
        name=${name//\(/\\(}
        name=${name//)/\\)}
        name=${name//\[/\\[}
        name=${name//]/\\]}
        if [[ -d "${COMPREPLY[$i]}" ]]; then
            COMPREPLY[$i]="$name/"
        else
            COMPREPLY[$i]="$name "
        fi
    done
    compopt -o nospace 2>/dev/null
}

# Completion function for all TSDuck commands.
_tsduck()
{
    local cmd="$1"
    local curword="$2"
    local prevword="$3"
    local prevchar="${COMP_LINE:$(($COMP_POINT-1)):1}"

    # Filter spurious invocations.
    [[ -z $cmd ]] && return

    # If the current word is a variable reference, ignore command syntax, just expand variable names.
    if [[ $curword == \$* ]]; then
        COMPREPLY=($(compgen -W "$(env | sed -e '/^[A-Za-z0-9_]*=/!d' -e 's/=.*$//' -e 's/^/\\\$/')" -- "$curword"))
        return
    fi

    # All available options for this command.
    local cmdopts=$($cmd --help=options 2>/dev/null | __ts_lines | sed -e '/^@/d' -e '/:/!d' -e 's/:.*$//')

    # Check if previous option is a plugin introducer (ie. need a plugin name).
    if [[ $prevword == -I || $prevword == -P || $prevword == -O ]]; then
        if [[ $cmdopts == *$prevword* ]]; then
            # This type of plugin is supported by the command, get possible plugin names.
            COMPREPLY=($(compgen -W "$($cmd --list-plugins=names$prevword 2>/dev/null | __ts_lines)" -- "$curword"))
            return
        else
            COMPREPLY=("{unknown-option$prevword}")
            return
        fi
    fi

    # Get the last plugin introducer, if any (ie.check if we are in a plugin argument list).
    local plopt plname opt i
    for ((i=$(($COMP_CWORD-1)); $i>0; i--)); do
        opt="${COMP_WORDS[$i]}"
        if [[ $opt == -I || $opt == -P || $opt == -O ]]; then
            plopt=$opt
            plname=${COMP_WORDS[$(($i+1))]}
            break
        fi
    done
    if [[ -n $plopt && -n $plname ]]; then
        # We are in a plugin argument list.
        if [[ $cmdopts == *$plopt* ]] && ($cmd --list-plugins=names$plopt | __ts_lines | grep -q "^$plname\$"); then
            # This plugin is supported by the command, use it as base command.
            cmd="$cmd $plopt $plname"
            cmdopts=$($cmd --help=options 2>/dev/null | __ts_lines | sed -e '/^@/d' -e '/:/!d' -e 's/:.*$//')
        else
            COMPREPLY=("{unknown-plugin$plopt-$plname}")
            return
        fi
    fi

    # Check if we are in an option, the value of an option, etc.
    local val aftereq
    if [[ $prevword == -* && $prevchar == = && -z $curword ]]; then
        # At end of "--option="
        aftereq=true
        opt="$prevword"
        val=
    elif [[ $COMP_CWORD -gt 2 && ${COMP_WORDS[$(($COMP_CWORD-2))]} == -* && $prevword == = ]]; then
        # At end of "--option=val"
        aftereq=true
        opt="${COMP_WORDS[$(($COMP_CWORD-2))]}"
        val="$curword"
    elif [[ $curword == -* ]]; then
        # Current word is an option name without value, get all possible matching options.
        COMPREPLY=($(compgen -W "$cmdopts" -- "$curword"))
        return
    elif [[ $prevword == -* ]]; then
        # Previous word is an option, current word is an option value or a parameter.
        opt="$prevword"
        val="$curword"
    else
        # Current word is a parameter.
        opt=@
        val="$curword"
    fi

    # Completion for an option value, check option validity.
    if [[ $opt != @ ]]; then
        case $(tr <<<$cmdopts ' ' '\n' | grep "^$opt" | wc -l | sed -e 's/ //g') in
            1)  # Just one possible option, continue later.
                ;;
            0)  # Unknown option.
                COMPREPLY=("{unknown-option$opt}")
                return
                ;;
            *)  # Several options match that prefix.
                COMPREPLY=("{ambiguous-option$opt}")
                return
                ;;
        esac
    fi

    # Get syntax for that option.
    local syntax=$($cmd --help=options 2>/dev/null | __ts_lines | sed -e '/:/!d' -e "/^$opt/!d" -e 's/^[^:]*://')

    # If there is no possible value for that option, this is a parameter.
    # If the value is optional but not inside "--option=value", there is no value, this is a parameter.
    if [[ $syntax == bool* || ( $syntax == opt:* && -z $aftereq ) ]]; then
        opt=@
        syntax=$($cmd --help=options 2>/dev/null | __ts_lines | sed -e '/^@:/!d' -e 's/^@://')
    fi

    syntax=${syntax/#opt:/}
    case $syntax in
        enum:*)
            # Expand enumeration values.
            syntax=${syntax/#enum:/}
            COMPREPLY=($(compgen -W "${syntax//,/ }" -- "$val"))
            ;;
        file)
            # Expand files names or intermediate directories.
            __ts_compgen_fd -f "$val"
            ;;
        directory)
            # Expand directories (intermediate or final).
            __ts_compgen_fd -d "$val"
            ;;
        *)
            # Other types (eg. integers), no auto-completion possible.
            COMPREPLY=()
            ;;
    esac
}

# Declare completions for all TSDuck commands.
# Do not change the order of the following two lines, guess why...
[[ $OSTYPE == cygwin || $OSTYPE == msys ]] && complete -F _tsduck ${__ts_cmds[*]/%/.exe}
complete -F _tsduck ${__ts_cmds[*]}
