#compdef ffmpeg

local curcontext="$curcontext" state line expl
typeset -A opt_args

(( $+functions[_ffmpeg_presets] )) || _ffmpeg_presets() {
    local presets
    presets=(~/.ffmpeg/*.ffpreset(:t:r) "$FFMPEG_DATADIR"/*.ffpreset(:t:r))
    _wanted ffmpeg-presets expl 'select preset' compadd -a presets
}

(( $+functions[_ffmpeg_acodecs] )) || _ffmpeg_acodecs() {
    local acodecs
    acodecs=(copy ${${(M)${(f)"$(_call_program audio-codecs $words[1] -codecs 2>/dev/null)"}:#[[:space:]][D[:space:]][E[:space:]]A[S[:space:]][D[:space:]][T[:space:]][[:space:]][^[:space:]]##*}//(#b)????????([^[:space:]]##)*/$match[1]})
    _wanted ffmpeg-audio-codecs expl 'force audio codec (''copy'' to copy stream)' compadd -a acodecs
}

(( $+functions[_ffmpeg_vcodecs] )) || _ffmpeg_vcodecs() {
    local vcodecs
    vcodecs=(copy ${${(M)${(f)"$(_call_program video-codecs $words[1] -codecs 2>/dev/null)"}:#[[:space:]][D[:space:]][E[:space:]]V[S[:space:]][D[:space:]][T[:space:]][[:space:]][^[:space:]]##*}//(#b)????????([^[:space:]]##)*/$match[1]})
    _wanted ffmpeg-video-codecs expl 'force video codec (''copy'' to copy stream)' compadd -a vcodecs
}

(( $+functions[_ffmpeg_scodecs] )) || _ffmpeg_scodecs() {
    local scodecs
    scodecs=(copy ${${(M)${(f)"$(_call_program video-codecs $words[1] -codecs 2>/dev/null)"}:#[[:space:]][D[:space:]][E[:space:]]S[S[:space:]][D[:space:]][T[:space:]][[:space:]][^[:space:]]##*}//(#b)????????([^[:space:]]##)*/$match[1]})
    _wanted ffmpeg-video-codecs expl 'force video codec (''copy'' to copy stream)' compadd -a scodecs
}

(( $+functions[_ffmpeg_formats] )) || _ffmpeg_formats() {
    local formats
    formats=(${(ou)${=${(s:,:)${${(M)${(f)"$(_call_program formats $words[1] -formats 2>/dev/null)"}:#[[:space:]][D[:space:]][E[:space:]][[:space:]][^[:space:]]##*}//(#b)????([^[:space:]]##)*/$match[1]}}}})
    _wanted ffmpeg-formats expl 'force format' compadd -a formats
}

(( $+functions[_ffmpeg_pix_fmts] )) || _ffmpeg_pix_fmts() {
    local pix_fmts
    _wanted ffmpeg-pix-fmts expl 'pixel format' compadd "$@" - \
        ${${${(M)${(f)"$(_call_program formats $words[1] -pix_fmts 2>/dev/null)"}:#[I.][O.][H.][P.][B.] [^=[:space:]]*}#* }%% *}
}

(( $+functions[_ffmpeg_bsfs] )) || _ffmpeg_bsfs() {
    local bsfs
    bsfs=(${${(f)"$(_call_program bsfs $words[1] -bsfs 2>/dev/null)"}:#*:})
    _wanted ffmpeg-bsfs expl 'set bitstream filter' compadd -a bsfs
}

typeset -A _ffmpeg_flags

(( $+functions[_ffmpeg_flag_options] )) || _ffmpeg_flag_options() {
    local expl
    _wanted options expl 'select flags' compadd -S '' -- {-,+}${^flag_options}
}

(( $+functions[_ffmpeg_more_flag_options] )) || _ffmpeg_more_flag_options() {
    compset -p $1 && _ffmpeg_flag_options
}

(( $+functions[_ffmpeg_new_flag_options] )) || _ffmpeg_new_flag_options() {
    compset -P '*' && _ffmpeg_flag_options
}

(( $+functions[_ffmpeg_flags] )) || _ffmpeg_flags() {
    local -a flag_options
    eval "flag_options=(\${=_ffmpeg_flags[$1]})"

    local match mbegin mend
    integer ret=1

    if [[ $PREFIX = (#b)(*)[-+]([^-+]#) ]]; then
        if [[ -n ${flag_options[(R)$match[2]]} ]]; then
            _ffmpeg_new_flag_options && ret=0
        fi
        if [[ -n ${flag_options[(R)$match[2]?*]} ]]; then
            _ffmpeg_more_flag_options ${#match[1]} && ret=0
        fi
    else
        _ffmpeg_flag_options && ret=0
    fi

    return ret
}

(( $+functions[_ffmpeg_register_lastopt_values] )) || _ffmpeg_register_lastopt_values() {
    if (( lastopt_takesargs )); then
        lastopt+=":$lastopt_description:"
        if (( $#lastopt_values )); then
            if [[ $lastopt_type == flags ]]; then
                lastopt="*$lastopt"
                flagtype=${${lastopt%%:*}#-}
                lastopt+="->$flagtype"
                _ffmpeg_flags[$flagtype]="${lastopt_values[*]}"
            else
                lastopt+="(${lastopt_values[*]})"
            fi
        fi
    fi
    _ffmpeg_argspecs+=$lastopt
}

local -a _ffmpeg_argspecs
{
    local lastopt
    local lastopt_description
    local lastopt_takesargs
    local lastopt_type
    local -a lastopt_values

    _call_program options $words[1] -h 2>/dev/null | while IFS=$'\n' read -r; do
        if [[ $REPLY == -* ]]; then
            [[ -n $lastopt ]] && _ffmpeg_register_lastopt_values
            lastopt=${REPLY%%[[:space:]]*}
            lastopt_description=${REPLY##-[^[:space:]]##[[:space:]]##}
            if [[ $lastopt_description == (#b)'<'(?##)'>'* ]]; then
                lastopt_type=$match[1]
                lastopt_description=${lastopt_description##<[^[:space:]]##>[[:space:]]##[^[:space:]]##[[:space:]]#}
                if [[ -z $lastopt_description ]]; then
                    lastopt_description=$lastopt
                fi
                lastopt_description=${lastopt_description//:/\\:}
            elif [[ $lastopt_description == [^[:space:]]##[[:space:]][[:space:]]* ]]; then
                local example=${lastopt_description%% *}
                example=${example//:/\\:}
                lastopt_description=${lastopt_description##[^[:space:]]##[[:space:]]##}
                lastopt_description=${lastopt_description//:/\\:}
                if [[ $example == filename ]]; then
                    lastopt_takesargs=0
                    lastopt+=":$lastopt_description:_files"
                elif [[ $lastopt == -[asv]pre ]]; then
                    lastopt_takesargs=0
                    lastopt="*$lastopt"
                    lastopt+=": :_ffmpeg_presets"
                elif [[ $lastopt == -acodec ]]; then
                    lastopt_takesargs=0
                    lastopt+=": :_ffmpeg_acodecs"
                elif [[ $lastopt == -vcodec ]]; then
                    lastopt_takesargs=0
                    lastopt+=": :_ffmpeg_vcodecs"
                elif [[ $lastopt == -scodec ]]; then
                    lastopt_takesargs=0
                    lastopt+=": :_ffmpeg_scodecs"
                elif [[ $lastopt == -f ]]; then
                    lastopt_takesargs=0
                    lastopt="*$lastopt"
                    lastopt+=": :_ffmpeg_formats"
                elif [[ $lastopt == -pix_fmt ]]; then
                    lastopt_takesargs=0
                    lastopt="*$lastopt"
                    lastopt+=":set pixel format:_ffmpeg_pix_fmts"
                elif [[ $example == bitstream_filter ]]; then
                    lastopt_takesargs=0
                    lastopt+=": :_ffmpeg_bsfs"
                else
                    lastopt_takesargs=1
                    lastopt_description+=" ($example)"
                fi
            else
                lastopt_takesargs=0
                if [[ $lastopt == -vfilters ]]; then
                    lastopt+=": :->vfilters"
                fi
            fi
            lastopt_values=()
        elif [[ $REPLY == ' '* ]]; then
            REPLY=${REPLY##[[:space:]]##}
            REPLY=${REPLY%%[[:space:]]##*}
            lastopt_takesargs=1
            lastopt_values+=$REPLY
        fi
    done
    [[ -n $lastopt ]] && _ffmpeg_register_lastopt_values
}

_arguments -C -S \
    "${_ffmpeg_argspecs[@]}" \
    '*:output file:_files' \
    && return

[[ "$state" == "vfilters" ]] &&
    _values -s , -S = 'video filters' \
    'aspect:set aspect ratio (rational number X\:Y or decimal number):' \
    'crop:crop input video (x\:y\:width\:height):' \
    'format: :_sequence -s : _ffmpeg_pix_fmts' \
    'noformat: :_sequence -s : _ffmpeg_pix_fmts' \
    'null' \
    'pad:add pads to the input image (width\:height\:x\:y\:color_string):' \
    'pixelaspect:set pixel aspect ratio (rational number X\:Y or decimal number):' \
    'scale:scale input video (width\:height):' \
    'slicify:output slice height ("random" or a number of pixels):' \
    'unsharp:luma_x\:luma_y\:luma_amount\:chroma_x\:chroma_y\:chroma_amount:' \
    'vflip' \
    'buffer' \
    'nullsrc' \
    'nullsink' \
    && return

[[ -n $state && -n $_ffmpeg_flags[$state] ]] &&
    _ffmpeg_flags $state && return

return 1
