" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not modify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
  return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_gina#Vim#Buffer#Group#import() abort', printf("return map({'new': ''}, \"vital#_gina#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
let s:groups = {}

function! s:new(...) abort
  let options = extend({
        \ 'on_close_fail': v:null,
        \}, get(a:000, 0, {})
        \)
  let hash = sha256(reltimestr(reltime()))
  let s:groups[hash] = {
        \ 'add': function('s:_group_add'),
        \ 'close': function('s:_group_close'),
        \}
  let s:groups[hash].__hash = hash
  let s:groups[hash].__tabnr = v:null
  let s:groups[hash].__members = []
  let s:groups[hash].__on_close_fail = options.on_close_fail
  return s:groups[hash]
endfunction

function! s:_group_add(...) abort dict
  let options = extend({
        \ 'keep': 0,
        \ 'expr': '%',
        \}, get(a:000, 0, {})
        \)
  let bufnr = bufnr(options.expr)
  let winid = bufwinid(options.expr)
  let tabnr = tabpagenr()
  if self.__tabnr is# v:null
    let self.__tabnr = tabnr
  elseif tabnr != self.__tabnr
    throw printf(
          \ 'vital: Vim.Buffer.Group: %s',
          \ 'A buffer on a different tabpage cannot be added.'
          \)
  endif
  call add(self.__members, {
        \ 'bufnr': bufnr,
        \ 'winid': winid,
        \ 'options': options,
        \})
  execute printf('augroup vital_vim_buffer_group_%s', self.__hash)
  execute printf('autocmd! * <buffer=%d>', bufnr)
  execute printf('autocmd WinLeave <buffer=%d> call s:_on_WinLeave(''%s'')', bufnr, self.__hash)
  execute 'augroup END'
endfunction

function! s:_group_close() abort dict
  for member in self.__members
    if member.options.keep
      continue
    endif
    let winnr = win_id2win(member.winid)
    if winnr == 0 || getbufvar(member.bufnr, '&modified') || bufwinid(member.bufnr) != member.winid
      continue
    endif
    try
      execute printf('%dclose', winnr)
    catch /^Vim\%((\a\+)\)\=:E444/
      " E444: Cannot close last window may thrown but ignore that
      " Vim.Buffer.Group should NOT close the last window so ignore
      " this exception silently.
      if self.__on_close_fail isnot# v:null
        call call(self.__on_close_fail, [winnr, member], self)
      endif
    endtry
  endfor
endfunction


function! s:_on_WinLeave(hash) abort
  execute 'augroup vital_vim_buffer_group_temporal_' . a:hash
  execute 'autocmd! *'
  execute printf(
        \ 'autocmd WinEnter * nested call s:_on_WinEnter(''%s'', %d)',
        \ a:hash, winnr('$'),
        \)
  execute 'augroup END'
endfunction

function! s:_on_WinEnter(hash, nwin) abort
  execute 'augroup vital_vim_buffer_group_temporal_' . a:hash
  execute 'autocmd! *'
  execute 'augroup END'
  if winnr('$') < a:nwin
    call s:groups[a:hash].close()
  endif
endfunction
