" ___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#Window#import() abort', printf("return map({'focus_buffer': '', 'focus_window': ''}, \"vital#_gina#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
let s:t_string = type('')
let s:DEFAULT_OPTIONS = {
      \ 'range': 'tabpage',
      \}

function! s:focus_window(expr, ...) abort
  let options = extend(copy(s:DEFAULT_OPTIONS), a:0 ? a:1 : {})
  let winid = s:_find_nearest_window_winid(a:expr, options.range)
  if winid == 0
    return v:null
  endif
  let guard = copy(s:guard)
  let guard.winid = win_getid()
  if winid != guard.winid
    call win_gotoid(winid)
  endif
  return guard
endfunction

function! s:focus_buffer(expr, ...) abort
  let options = extend(copy(s:DEFAULT_OPTIONS), a:0 ? a:1 : {})
  let winid = s:_find_nearest_buffer_winid(a:expr, options.range)
  if winid == 0
    return v:null
  endif
  let guard = copy(s:guard)
  let guard.winid = win_getid()
  if winid != guard.winid
    call win_gotoid(winid)
  endif
  return guard
endfunction


" Guard ----------------------------------------------------------------------
let s:guard = {}

function! s:guard.restore() abort
  let winid = self.winid
  if winid != win_getid()
    call win_gotoid(winid)
  endif
endfunction


" Private --------------------------------------------------------------------
function! s:_find_nearest_window_winid(expr, range) abort
  let ntabpages = tabpagenr('$')
  if a:range ==# 'tabpage' || ntabpages == 1
    return win_getid(type(a:expr) == s:t_string ? winnr(a:expr) : a:expr)
  endif
  let Distance = function('s:_distance', [tabpagenr()])
  for tabpagenr in sort(range(1, ntabpages), Distance)
    let winnr = type(a:expr) == s:t_string
          \ ? tabpagewinnr(tabpagenr, a:expr)
          \ : a:expr
    if winnr > 0 && winnr <= tabpagewinnr(tabpagenr, '$')
      return win_getid(winnr, tabpagenr)
    endif
  endfor
  return 0
endfunction

function! s:_find_nearest_buffer_winid(expr, range) abort
  let bufnr = type(a:expr) == s:t_string ? bufnr(a:expr) : a:expr
  let ntabpages = tabpagenr('$')
  if a:range ==# 'tabpage' || ntabpages == 1
    return win_getid(bufwinnr(bufnr))
  endif
  let Distance = function('s:_distance', [tabpagenr()])
  for tabpagenr in sort(range(1, ntabpages), Distance)
    let s:base = tabpagewinnr(tabpagenr)
    let buflist = tabpagebuflist(tabpagenr)
    for winnr in sort(range(1, len(buflist)), Distance)
      if buflist[winnr - 1] == bufnr
        return win_getid(winnr, tabpagenr)
      endif
    endfor
  endfor
  return 0
endfunction

function! s:_distance(base, a, b) abort
  return abs(a:a - a:base) - abs(a:b - a:base)
endfunction
