#compdef cbsd

# Define all known commands first

typeset -ga _cbsd_subcmds=()

_cbsd_subcmds=(
  'help: [any] cbsd help'
  'bcheckpoint: [bhyve] bhyve checkpoint'
  'bclone: [bhyve] bhyve cloning'
  'bconfig: [bhyve] Configure for bhyve'
  'bcontrol-tui: [bhyve] Ncurses based control for bhyve'
  'bcreate: [bhyve] Create bhyve from config file'
  'bdescr: [bhyve] Show or modify bhyve description'
  'bexport: [bhyve] Export bhyve into image'
  'bimport: [bhyve] Import bhyve from image'
  'blogin: [bhyve] Exec login into bhyve'
  'bls: [bhyve] List bhyve and status'
  'border: [bhyve] List bhyve run order'
  'border-tui: [sys] Ncurses based bhyve order editor'
  'bpause: [bhyve] Pause and resume for bhyve domain (send STOP/CONT signal to bhyve vm process)'
  'bregister: [bhyve] Register bhyve records to SQLite from ASCii config or re-populate ASCii config from SQLite'
  'bremove: [bhyve] Destroy bhyve'
  'brename: [bhyve] Rename bhyve'
  'brestart: [bhyve] bhyve bstop bstart sequence'
  'bset: [bhyve] Modify parameter for bhyve'
  'bsetup-tui: [bhyve] Ncurses based setup for bhyve-arg'
  'bsnapshot: [bhyve] bhyve snapshot management'
  'bstart: [bhyve] Start bhyve domain'
  'bstop: [bhyve] Stop bhyve domain'
  'bunregister: [bhyve] Register bhyve records to SQLite from ASCii config or re-populate ASCii config from SQLite'
  'j2prepare: [jail] Prepare remote node for accepting jail via j2slave'
  'j2slave: [jail] Transfer jail as slave jail to remote node'
  'jailscp: [jail] get put file to remove nodes'
  'jbackup: [sys] Backup jail to slave node with slave status'
  'jcleanup: [jail] Force unmount and cleanup for offline jail'
  'jclone: [jail] Jail cloning'
  'jcoldmigrate: [jail] Cold migrate (with save status) jail to remote node, set local jail as slave'
  'jconfig: [jail] Configure for jail'
  'jcontrol-tui: [jail] Ncurses based control for jail'
  'jcreate: [jail] Create jail from config file. If jail exist and autorestart=1 - jset will be used to update params.'
  'jdescr: [jail] Show or modify jail description'
  'jexec: [jail] Execution for command inside jail'
  'jexport: [jail] Export jail into image'
  'jget: [jail] Get info related to jail'
  'jimport: [jail] Import jail from image'
  'jlogin: [jail] Exec login into jail'
  'jls: [jail] List and show status of jails'
  'jmkrcconf: [jail] Create ascii rc.conf for jail'
  'jmkrctlconf: [jail] Import or export from/to ascii rctl.conf from/to SQLite3 tables for jail'
  'jorder: [jail] List jail run order'
  'jorder-tui: [sys] Ncurses based jail order editor'
  'jpause: [jail] Pause and resume for jail (send STOP/CONT signal to jail processes)'
  'jrclone: [jail] Clone jail to remote machine'
  'jrctl: [jail] Set or flush resource limit for jail'
  'jrctl-tui: [jail] Dialog based UI for RACCR/RCTL'
  'jregister: [jail] Register jail records to SQLite from ASCii config or re-populate ASCii config from SQLite'
  'jremove: [jail] Destroy jail'
  'jrename: [jail] Rename jail'
  'jrestart: [jail] jail jstop jstart sequence'
  'jset: [jail] Modify parameter for jail'
  'jsetup-tui: [jail] Ncurses based setup for jail-arg'
  'jsnapshot: [jail] Jail snapshot management'
  'jstart: [jail] Start jail'
  'jstatus: [jail] Return jail ID in output and jail existance as error code (0: no jail, 1: jail exist)'
  'jstop: [jail] Stop jail'
  'jswmode: [jail] Jail switch mode between master/slave'
  'junregister: [jail] Register jail records to SQLite from ASCii config or re-populate ASCii config from SQLite'
  'jupgrade: [jail] Upgrade jail base data when baserw=1'
  'jwhereis: [jail] Return node for jname'
  'vconfig: [virtualbox] Configure for jail'
  'vcontrol-tui: [jail] Ncurses based control for VM'
  'vcreate: [virtualbox] Create jail from config file'
  'vls: [virtualbox] List jail and status'
  'vremove: [jail] Destroy jail'
  'vset: [virtualbox] Modify parameter for jail'
  'vsetup-tui: [virtualbox] Ncurses based setup for jail-arg'
  'vstart: [virtualbox] Start jail'
  'vstop: [virtualbox] Stop jail'
  'xconfig: [xen] Configure XEN domain'
  'xcreate: [xen] Create jail from config file'
  'xls: [xen] List jail and status'
  'xremove: [xen] Destroy XEN domain'
  'xset: [xen] Modify parameter for jail'
  'xsetup-tui: [xen] Ncurses based setup for jail-arg'
  'xstart: [xen] Start jail'
  'xstop: [xen] Stop jail'
)

# Sub-command functions
# should be _cbsd-*()

_cbsd-jexec() {
  case "$CURRENT-${words[3]}" in
  3-*) # cbsd jexec <>
    _alternative \
      'jname:jname:_values -w \
        "jname" \
        "jname[jail where to jexec]:jname:_values jname $(__cbsd_jls)"' \
      'help:help:(--help)' &&
      ret=0
  ;;
  4-jname*) # cbsd jexec jname=jname <>
    # fix _users to use the passwd file from the jail
    if [[ "${words[3]}" != 'jname=\*' ]]; then
      local userdirs u d
      typeset -A userdirs
      userdis=()
      cbsd jexec "${words[3]}" grep -vE '\^\(\#\|\$\)' /etc/passwd |
        while IFS=: read -r u _ _ _ _ d _ ; do
          userdirs+=( "$u" "$d" )
        done
      # TODO: if we don't pick user=, we should expand to commands inside the jail
      # code from https://github.com/zsh-users/zsh/blob/master/Completion/BSD/Command/_jexec
    fi
    _alternative \
      'user:user:_values -w \
        "user" \
        "user[whom to impersonate]:user:_users"' \
      'command:command:_normal' &&
      ret=0
  ;;
  4-*) # cbsd jexec --help
    _message "$(cbsd jexec --help)" &&
    ret=0
  ;;
  *) # cbsd jexec jname=jname { user=user | command  } [...]
    # TODO: we should expand to commands inside the jail
    # code from https://github.com/zsh-users/zsh/blob/master/Completion/BSD/Command/_jexec
      _normal
  ;;
  esac
}

# Private functions

function __cbsd_jls() {
  # Returns a \n seperated-list of jails, depending on the subcommand we used
  case "$words[2]" in
    jexec) # We only show active jails, in which we can jexec, plus '*'
      cbsd jls display=status,jname alljails=1 | awk '{ if ($1 ~ "On") print $2 }'
      printf '%s\n' '\\\*'
    ;;
    *) : ;;
  esac
}

_cbsd () {
  local ret=1
  local cbsdcmd

  if ((CURRENT == 2)); then
    _describe -t subcommands cbsd _cbsd_subcmds && ret=0
  else
    cbsdcmd=${words[2]}
    _call_function ret _cbsd-${cbsdcmd}
  fi
  return ret
}

_cbsd "$@"
