let s:Git = vital#gina#import('Git')
let s:Path = vital#gina#import('System.Filepath')
let s:Store = vital#gina#import('System.Store')


function! gina#complete#commit#any(arglead, cmdline, cursorpos) abort
  let candidates = ['']
  let candidates += gina#complete#commit#branch(a:arglead, a:cmdline, a:cursorpos)
  if !empty(a:arglead)
    let candidates += gina#complete#commit#hashref(a:arglead, a:cmdline, a:cursorpos)
  endif
  return s:filter(a:arglead, candidates)
endfunction

function! gina#complete#commit#branch(arglead, cmdline, cursorpos) abort
  let git = gina#core#get_or_fail()
  let slug = eval(s:Store.get_slug_expr())
  let store = s:Store.of(s:Git.resolve(git, 'config'))
  let candidates = store.get(slug, [])
  if empty(candidates)
    let candidates = s:get_available_branches(git, ['--all'])
    call store.set(slug, candidates)
  endif
  return s:filter(a:arglead, candidates)
endfunction

function! gina#complete#commit#local_branch(arglead, cmdline, cursorpos) abort
  let git = gina#core#get_or_fail()
  let slug = eval(s:Store.get_slug_expr())
  let store = s:Store.of(s:Git.resolve(git, 'config'))
  let candidates = store.get(slug, [])
  if empty(candidates)
    let candidates = s:get_available_branches(git, [])
    call store.set(slug, candidates)
  endif
  return s:filter(a:arglead, candidates)
endfunction

function! gina#complete#commit#remote_branch(arglead, cmdline, cursorpos) abort
  let git = gina#core#get_or_fail()
  let slug = eval(s:Store.get_slug_expr())
  let store = s:Store.of(s:Git.resolve(git, 'config'))
  let candidates = store.get(slug, [])
  if empty(candidates)
    let candidates = s:get_available_branches(git, ['--remotes'])
    call store.set(slug, candidates)
  endif
  return s:filter(a:arglead, candidates)
endfunction

function! gina#complete#commit#hashref(arglead, cmdline, cursorpos) abort
  let git = gina#core#get_or_fail()
  let slug = eval(s:Store.get_slug_expr())
  let store = s:Store.of(s:Git.resolve(git, 'config'))
  let candidates = store.get(slug, [])
  if empty(candidates)
    let candidates = s:get_available_commits(git, [])
    call store.set(slug, candidates)
  endif
  return s:filter(a:arglead, candidates)
endfunction


" Public ---------------------------------------------------------------------
function! s:filter(arglead, candidates) abort
  return gina#util#filter(a:arglead, a:candidates)
endfunction

function! s:get_available_commits(git, args) abort
  let args = ['log', '--pretty=%h'] + a:args
  let result = gina#process#call(a:git, args)
  if result.status
    return []
  endif
  return result.stdout
endfunction

function! s:get_available_branches(git, args) abort
  let args = ['branch', '--no-color', '--list'] + a:args
  let result = gina#process#call(a:git, args)
  if result.status
    return []
  endif
  let candidates = filter(copy(result.stdout), 'v:val !~# ''^.* -> .*$''')
  call map(candidates, 'matchstr(v:val, ''^..\zs.*$'')')
  call map(candidates, 'substitute(v:val, ''^remotes/'', '''', '''')')
  return ['HEAD'] + filter(candidates, '!empty(v:val)')
endfunction