#compdef ml

# Main dispatcher
_ml()
{

  local curcontext="$curcontext" state line arguments opt descript arg w optState cmd
  local -A opt_args optT cmdT

  arguments=(
    '(-h --help)'{--help,-h}'[display usage info]'
    '(- *)'{--version,-v}'[module command version]'
    '(-t --terse)'{--terse,-t}'[display avail, list and spider output in short format]'
    '(-d --default)'{--default,-d}'[List default modules only when used with avail]'
    '(-D --debug)'{--debug,-D}'[Send program tracing information to stderr]'
    '(-w --width)'{--width,-w}'[use given width for max term width]'
    '(-q --quiet)'{--quiet,-q}'[disable verbose messages]'
    '(-s --style)'{--style,-s}'[avail output style]'
    '--mt[display the module table]'
    '(-r --regexp)'{--regexp,-r}'[lua regular expression matching]'
    '--ignore_cache[Treat the cache file(s) as out-of-date]'
    '--Verbose[show generated module command]'
    )

  for arg in $arguments; do
    opt=${${arg#*\)}%%\[*}
    descript=${${arg#*\[}%%\]*}
    optT[$opt]=$descript
  done

  _ml_loaded_modules_negated
  _ml_available_modules

  local _ml_cmds

  _ml_cmds=(
    "help:print the usage of each sub-command"
    "unload:remove a module from the shell environment"
    "rm:remove a module from the shell environment"
    "swap:swap loaded a loaded module with another module"
    "spider:List all possible modules"
    "show:display information about a module"
    "list:list loaded modules"
    "load:load a module"
    "try-load:load a module, no warnings if not found"
    "avail:list all available modules"
    "restore:load a module collection"
    "use:add a directory to MODULEPATH"
    "unuse:remove a directory from MODULEPATH"
    "purge:unload all loaded modules"
    "refresh:refresh all non-persistent components of loaded modules"
    "whatis:display module information"
    "keyword:search for a given keyword in modules"
    "update:reload all loaded modules"
    "mcc:module collection contents"
    "describe:module collection contents"
    "disable:disable a module collection"
  )

  for i in $_ml_cmds; do
    cmd=${i%%:*}
    cmdT[$cmd]=1
  done


  #  previous        current      choices         pattern
  # ---------------------------------------------------------
  # ml -o1           <nothing>     cmds             10
  #                    A*          availM
  # ---------------------------------------------------------
  # ml -o1             -           -opts            11
  #                                -loadM
  # ---------------------------------------------------------
  # ml -o1 -M1       <nothing>     availM           20
  #                    A*
  # ---------------------------------------------------------
  # ml -o1 -M1         -           -loadM           21
  # ---------------------------------------------------------
  # ml -o1  M1       <nothing>     availM           20
  #                    A*
  # ---------------------------------------------------------
  # ml -o1  M1         -           -loadM           21
  # ---------------------------------------------------------
  # ml -o1  cmd        *           cmd-specific     30
  # ---------------------------------------------------------

  cmd=""

  pattern=10

  for ((i=2; i < $#words; i++ )); do
    w=$words[$i]
    firstC=$w[1]
    #echo "\$words[$i]: \"$w\" firstC: \"$firstC\"" >> $HOME/t/ml.log
    if   [ -n "$optT[$w]"  ]; then
      pattern=10
    elif [ "$firstC" = "-" ]; then
      pattern=20
      break;
    elif [ "$cmdT[$w]" = 1 ]; then
      pattern=30
      cmd=$w
      break;
    fi
  done

  w=$words[$CURRENT]
  firstC=$w[1]

  if [ "$firstC" = "-" ]; then
    ((pattern++))
  fi

  case $pattern in
    10)
      cmdsPavail=($_ml_cmds $_available_modules)
      _describe -t commands 'ml commands' cmdsPavail  || compadd "$@"
      ;;
    11)
      _arguments                 \
        $arguments               \
        $_loaded_modules_negated
      ;;
    20)
      compadd "$@" -a -- _available_modules
      ;;
    21)
      compadd "$@" -a -- _loaded_modules_negated
      ;;
    30|31)
      case $cmd in
        add|try-load) cmd="load";;
        rm) cmd="unload";;
        display) cmd="show";;
      esac

      local update_policy
      curcontext="${curcontext%:*:*}:ml-${cmd}:"
      zstyle -s ":completion:${curcontext}:" cache-policy update_policy
      _call_function ret _ml_$cmd || _message 'no more arguments'
      ;;
  esac
}


_ml_loaded_modules_negated()
{
  _loaded_modules_negated=(${$(module -q -t --no_redirect list 2>&1 > /dev/null | sed ' /^ *$/d; /:$/d; s#/*$##g; s|^|-|g;')})
}

_ml_loaded_modules()
{
  _loaded_modules=(${$(module -q -t  --no_redirect list 2>&1 > /dev/null | sed ' /^ *$/d; /:$/d; s#/*$##g;')})
}



# Fills the available modules cache
_ml_available_modules()
{
  if [[ -n $MODULEPATH ]];  then
    _available_modules=(${$(module -q -t  --no_redirect avail 2>&1 > /dev/null | sed ' /:$/d; s/(@.*)//g; s#/*$##g;')})
  fi
}

# Fills the available modules cache
_ml_spider_list()
{
  _spider_list=(${$(module -q -t --no_redirect spider 2>&1 > /dev/null)})
}


_ml_unload()
{
  _ml_loaded_modules
  compadd "$@" -a -- _loaded_modules
}

_ml_restore()
{
  _savelist_list=(${$(module -q -t --no_redirect savelist 2>&1 > /dev/null)})
  compadd "$@" -a -- _savelist_list
}

_ml_mcc()
{
  _savelist_list=(${$(module -q -t --no_redirect savelist 2>&1 > /dev/null)})
  compadd "$@" -a -- _savelist_list
}

_ml_describe()
{
  _savelist_list=(${$(module -q -t --no_redirect savelist 2>&1 > /dev/null)})
  compadd "$@" -a -- _savelist_list
}

_ml_disable()
{
  _savelist_list=(${$(module -q -t --no_redirect savelist 2>&1 > /dev/null)})
  compadd "$@" -a -- _savelist_list
}

_ml_help()
{
  _ml_available_modules
  compadd "$@" -a -- _available_modules
}

_ml_swap()
{
  if [[ $words[$CURRENT-1] == "swap" ]]; then
    _ml_loaded_modules
    compadd "$@" -a -- _loaded_modules
  else
    _ml_available_modules
    compadd "$@" -a -- _available_modules
  fi
}

# Completion function for display|show
_ml_show()
{
  _ml_available_modules
  compadd "$@" -a -- _available_modules
}

# Completion function for load
_ml_load()
{
  _ml_available_modules
  compadd "$@" -a -- _available_modules
}

# Completion function for use
_ml_use()
{
  _arguments -s \
    '(-a --append)'{--append,-a}'[append the directories instead of prepending]' \
    '*:directory:_files -/'
}

# Completion function for unuse
_ml_unuse()
{
  compadd "$@" -- ${=MODULEPATH//:/ }
}

# Completion function for whatis
_ml_whatis()
{
  _ml_available_modules
  compadd "$@" -a -- _available_modules
}

_ml_spider()
{
  _ml_spider_list
  compadd "$@" -a -- _spider_list
}


_ml "$@"

# Local Variables:
# mode: shell-script
# indent-tabs-mode: nil
# End:
