#autoload

# Complete the arguments of the current command according to the
# descriptions given as arguments to this function.

local long cmd="$words[1]" descr mesg subopts opt usecc autod
local oldcontext="$curcontext"

long=$argv[(I)--]
if (( long )); then
  local name tmp tmpargv

  if [[ long -eq 1 ]]; then
    tmpargv=()
  else
    tmpargv=( "${(@)argv[1,long-1]}" )
  fi

  name=${~words[1]}
  if [[ "$name" != /* ]]; then
    tmp="$PWD/$name"
  fi
  name="_args_cache_${name}"
  name="${name//[^a-zA-Z0-9_]/_}"

  if (( ! ${(P)+name} )); then
    local iopts sopts pattern tmpo cur cache
    typeset -U lopts

    cache=()

    # We have to build a new long-option cache, get the `-i' and
    # `-s' options.

    set -- "${(@)argv[long+1,-1]}"

    iopts=()
    sopts=()
    while [[ "$1" = -[is]* ]]; do
      if [[ "$1" = -??* ]]; then
        tmp="${1[3,-1]}"
        cur=1
      else
        tmp="$2"
	cur=2
      fi
      if [[ "$tmp[1]" = '(' ]]; then
	tmp=( ${=tmp[2,-2]} )
      else
	tmp=( "${(@P)tmp}" )
      fi
      if [[ "$1" = -i* ]]; then
        iopts=( "$iopts[@]" "$tmp[@]" )
      else
        sopts=( "$sopts[@]" "$tmp[@]" )
      fi
      shift cur
    done

    # Now get the long option names by calling the command with `--help'.
    # The parameter expansion trickery first gets the lines as separate
    # array elements. Then we select all lines whose first non-blank
    # character is a hyphen. Since some commands document more than one
    # option per line, separated by commas, we convert commas int
    # newlines and then split the result again at newlines after joining 
    # the old array elements with newlines between them. Then we select
    # those elements that start with two hyphens, remove anything up to
    # those hyphens and anything from the space or comma after the
    # option up to the end. 

    lopts=("--${(@)^${(@)${(@)${(@M)${(@ps:\n:j:\n:)${(@)${(@M)${(@f)$(${~words[1]} --help 2>&1)//\[--/
--}:#[ 	]#-*}//,/
}}:#[ 	]#--*}#*--}%%[], ]*}:#}")

    # Now remove all ignored options ...

    while (( $#iopts )); do
      lopts=( ${lopts:#$~iopts[1]} )
      shift iopts
    done

    # ... and add "same" options

    while (( $#sopts )); do
      lopts=( $lopts ${lopts/$sopts[1]/$sopts[2]} )
      shift 2 sopts
    done

    # Then we walk through the descriptions plus a few builtin ones.

    set -- "$@" '*=FILE*:file:_files' \
           '*=(DIR|PATH)*:directory:_files -/' '*: :'

    while (( $# )); do

      # First, we get the pattern and the action to use and take them
      # from the positional parameters.

      pattern="${${${(M)1#*[^\\]:}[1,-2]}//\\\\:/:}"
      descr="${1#${pattern}}"
      shift

      # We get all options matching the pattern and take them from the
      # list we have built. If no option matches the pattern, we
      # continue with the next.

      tmp=("${(@M)lopts:##$~pattern}")
      lopts=("${(@)lopts:##$~pattern}")

      (( $#tmp )) || continue

      opt=''

      # If there are option strings with a `[=', we take these get an
      # optional argument.

      tmpo=("${(@M)tmp:#*\[\=*}")
      if (( $#tmpo )); then
        tmp=("${(@)tmp:#*\[\=*}")
        tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}")

        if [[ "$descr" = ::* ]]; then
	  cache=( "$cache[@]" "${(@)^tmpo}=${descr}" )
        else
	  cache=( "$cache[@]" "${(@)^tmpo}=:${descr}" )
        fi
      fi

      # Descriptions with `=': mandatory argument.

      tmpo=("${(@M)tmp:#*\=*}")
      if (( $#tmpo )); then
        tmp=("${(@)tmp:#*\=*}")
        tmpo=("${(@)${(@)tmpo%%\=*}//[^a-z0-9-]}")

        if [[ "$descr" = ::* ]]; then
	  cache=( "$cache[@]" "${(@)^tmpo}=${descr[2,-1]}" )
        else
	  cache=( "$cache[@]" "${(@)^tmpo}=${descr}" )
        fi
      fi

      # Everything else is just added as a option without arguments.

      if (( $#tmp )); then
        tmp=("${(@)tmp//[^a-zA-Z0-9-]}")
	cache=( "$cache[@]" "$tmp[@]" )
      fi
    done
    eval "${name}=( \"\${(@)cache:# #}\" )"
  fi
  set -- "$tmpargv[@]" "${(@P)name}"
fi

subopts=()
while [[ "$1" = -(O*|C) ]]; do
  case "$1" in
  -C) usecc=yes; shift ;;
  -O) subopts=( "${(@P)2}" ); shift 2 ;;
  *)  subopts=( "${(@P)1[3,-1]}" ); shift ;;
  esac
done

_style -s options auto-description autod

if comparguments -i "$autod" "$@"; then
  local nm="$compstate[nmatches]" action noargs aret expl local
  local next direct odirect equal single match matched ws tmp1 tmp2 tmp3
  local opts subc prefix suffix
  local origpre="$PREFIX" origipre="$IPREFIX"

  if comparguments -D descr action; then
    comparguments -C subc
    curcontext="${oldcontext}:$subc"

    if comparguments -O next direct odirect equal; then
      opts=yes
      _tags arguments options
    else
      _tags arguments
    fi
  else
    if comparguments -a; then
      noargs='no more arguments'
    else
      noargs='no arguments'
    fi
    comparguments -O next direct odirect equal || return 1

    opts=yes
    _tags options
  fi

  while true; do
    while _tags; do
      if [[ -n "$matched" ]] || _requested arguments; then
        _description expl "$descr"

        if [[ "$action" = -\>* ]]; then
          comparguments -W line opt_args
          state="${${action[3,-1]##[ 	]#}%%[ 	]#}"
	  if [[ -n "$usecc" ]]; then
	    curcontext="${oldcontext}:$subc"
	  else
	    context="$subc"
	  fi
          compstate[restore]=''
          aret=yes
        else
          if [[ -z "$local" ]]; then
            local line
            typeset -A opt_args
            local=yes
          fi

          comparguments -W line opt_args

          if [[ "$action" = \ # ]]; then

            # An empty action means that we should just display a message.

            [[ -n "$matched" ]] && compadd -n -Q -S '' -s "$SUFFIX" - "$PREFIX"
            mesg="$descr"

          elif [[ "$action" = \(\(*\)\) ]]; then

            # ((...)) contains literal strings with descriptions.

            eval ws\=\( "${action[3,-3]}" \)

            _describe "$descr" ws -M "$match" "$subopts[@]"

          elif [[ "$action" = \(*\) ]]; then

            # Anything inside `(...)' is added directly.

            compadd "$subopts[@]" "$expl[@]" - ${=action[2,-2]}
          elif [[ "$action" = \{*\} ]]; then

            # A string in braces is evaluated.

            eval "$action[2,-2]"

          elif [[ "$action" = \ * ]]; then

            # If the action starts with a space, we just call it.

            ${(e)=~action}
          else

            # Otherwise we call it with the description-arguments built above.

            action=( $=action )
            ${(e)action[1]} "$subopts[@]" "$expl[@]" ${(e)~action[2,-1]}
          fi
        fi
      fi

      if [[ -z "$matched" ]] && _requested options &&
          { ! _style options prefix-needed ||
            [[ "$origpre" = [-+]* ]] } ; then
	local prevpre="$PREFIX" previpre="$IPREFIX"

	PREFIX="$origpre"
	IPREFIX="$origipre"

        comparguments -M match
	
        if comparguments -s single; then

          _description expl option

          if [[ "$single" = direct ]]; then
	    compadd "$expl[@]" -QS '' - "${PREFIX}${SUFFIX}"
          elif [[ "$single" = next ]]; then
	    compadd "$expl[@]" -Q - "${PREFIX}${SUFFIX}"
          elif [[ "$single" = equal ]]; then
	    compadd "$expl[@]" -QqS= - "${PREFIX}${SUFFIX}"
          else
	    tmp1=( "$next[@]" "$direct[@]" "$odirect[@]" "$equal[@]" )
	    tmp3=( "${(M@)tmp1:#[-+]?[^:]*}" )
	    tmp1=( "${(M@)tmp1:#[-+]?(|:*)}" )
	    tmp2=( "${PREFIX}${(@M)^${(@)${(@)tmp1%%:*}#[-+]}:#?}" )

            _describe -o option \
                      tmp1 tmp2 -Q -S '' -- \
		      tmp3 -Q
          fi
          single=yes
        else
          next=( "$next[@]" "$odirect[@]" )
          _describe -o option \
            next -Q -M "$match" -- \
            direct -QS '' -M "$match" -- \
            equal -QqS= -M "$match"
        fi
	PREFIX="$prevpre"
	IPREFIX="$previpre"
      fi
    done
    if [[ -n "$opts" && -z "$aret$matched" &&
          nm -eq compstate[nmatches] ]]; then

      PREFIX="$origpre"
      IPREFIX="$origipre"

      prefix="${PREFIX#*\=}"
      suffix="$SUFFIX"
      PREFIX="${PREFIX%%\=*}"
      SUFFIX=''
      compadd -M "$match" -D equal - "${(@)equal%%:*}"

      if [[ $#equal -eq 1 ]]; then
        PREFIX="$prefix"
	SUFFIX="$suffix"
	IPREFIX="${IPREFIX}${equal[1]%%:*}="
	matched=yes

	comparguments -L "${equal[1]%%:*}" descr action subc
	curcontext="${oldcontext}:$subc"

	_tags arguments

	continue
      fi
    fi
    break
  done

  [[ -z "$aret" || -z "$usecc" ]] && curcontext="$oldcontext"

  [[ -n "$aret" ]] && return 300

  [[ -n "$mesg" ]] && _message "$mesg"
  [[ -n "$noargs" ]] && _message "$noargs"

  # Set the return value.

  [[ nm -ne "$compstate[nmatches]" ]]
else
  return 1
fi
