#compdef lsmod modinfo modprobe rmmod insmod

local curcontext="$curcontext" expl state line modules ign args ret=1
local -r modules_dir=/lib/modules

args=(
  '(-)'{-V,--version}'[print version]'
  '(-)'{-h,--help}'[print help text]'
)

case "$service" in
  lsmod) _arguments -s "$args[@]" && return ;;
  
  modinfo)
    _arguments -s -C "$args[@]" \
      '(-)'{-a,--author}"[display the module's author]" \
      '(-)'{-d,--description}"[display the module's description]" \
      '(-)'{-l,--license}"[display the module's license]" \
      '(-)'{-n,--filename}"[display the module's filename]" \
      '(-)'{-p,--parameters}'[display the typed parameters that a module may support]' \
      '(-)'{-F,--field}"[display only selected module's information]:module_field:(
        alias author depends description filename license parm)" \
      '(-)-k[use modules from a different kernel version]:kernel_version:(
        $(echo $modules_dir/*(/\:t)))' \
      '1:module file:->all_modules' && ret=0
  ;;

  modprobe)
    ign='-h --help -V --version -c --showconfig'
    _arguments -s -C "$args[@]" \
      "(-a --all $ign)"{-a,--all}'[load all matching modules]' \
      '(-)'{-c,--showconfig}'[show current configuration]' \
      "(-d --debug $ign)"{-d,--debug}'[print debug info]' \
      "(-k --autoclean $ign)"{-k,--autoclean}'[set autoclean]' \
      "(-n --show $ign)"{-n,--show}"[don't actually perform action]" \
      "(-q --quiet $ign)"{-q,--quiet}"[don't complain about insmod failures]" \
      "(-s --syslog $ign)"{-s,--syslog}'[report via syslog instead of stderr]' \
      "(* -t --type $ign)"{-t,--type}'[module type]:module type' \
      "(-v --verbose $ign)"{-v,--verbose}'[print all commands as executed]' \
      '(-C --config)'{-C,--config}'[specify config file]:config file:_files' \
      "(-r --remove -l --list -t --type -a --all $ign)"{-r,--remove}'[remove module (stacks)]' \
      "(* -l --list -r --remove $ign)"{-l,--list}'[list matching modules]' \
      "(-c $ign)1:modules:->loadable_modules" \
      "(-c -l --list -t --type $ign)*:params:->params" && ret=0
      
      [[ -n $state ]] && (( $+opt_args[-r] )) && state=loaded_modules        
  ;;

  rmmod)
    _arguments -s -C "$args[@]" \
      '(-a --all)'{-a,--all}'[remove all unused autocleanable modules]' \
      '(-e --persist)'{-e,--persist}'[save persistent data]' \
      '(-r --stacks)'{-r,--stacks}'[remove a module stack]' \
      '(-s --syslog)'{-s,--syslog}'[output to syslog]' \
      '(-v --verbose)'{-v,--verbose}'[be verbose]' \
      '*:loaded module:->loaded_modules' && ret=0
  ;;

  insmod)
    _arguments \
      '1:module file:_files' \
      '*:module parameters:' && ret=0
  ;;
esac 

case "$state" in
  loaded_modules|loadable_modules)
    if [[ -r /proc/modules ]]; then
     loaded_modules=(${${(f)"$(</proc/modules)"}%% *})
    elif [[ -x /sbin/lsmod ]]; then
     loaded_modules=(${${(f)"$(/sbin/lsmod)"}[2,-1]%% *})
    else
     return 1
    fi

    if [[ $state = loaded_modules ]]; then
        _wanted modules expl 'loaded module' compadd -a loaded_modules && return
        return ret
    fi
  ;&

  all_modules)
    local kver
    integer kver_idx
    if (( kver_idx=${words[(I)-k]} )); then
      kver=${words[kver_idx+1]}
    else
      kver=$(uname -r)
    fi
    modules=( $modules_dir/$kver/(*~source)/**/*(.:t:r) )

    if [[ $state = loadable_modules ]]; then
        modules=( ${modules:#(${(j:|:)~${=loaded_modules//_/-}})} )
    fi

    _tags files modules
    while _tags; do
      _requested files expl "module file"  _files -g '*.ko' && ret=0
      _requested modules expl module compadd -a modules && ret=0
    done
  ;;

  params)
    if compset -P '*='; then
      _message -e value 'parameter value'
    else
      local params
      params=( ${${(M)${(f)"$(_call_program module_parameter modinfo "$words[2]" 2>/dev/null)"}:#parm:*}##parm:[[:space:]]##} )
      if [[ $#params -eq 0 ]]; then
        _message -e parameter "This modules doesn't have parameters"
      else
        typeset -A val_args

        _values -S = -C -w 'module parameter' \
          ${${${(M)params:#*(:bool|\(bool\))}/:/[}/(bool| \(bool\))/]} \
          ${^${params:#*(:bool|\(bool\))}/:/[}"]:auto added argument: " && ret=0
      fi
    fi
  ;;
esac

return ret
