scriptencoding utf-8

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

let s:SLUG = eval(s:Store.get_slug_expr())


function! gina#component#status#staged() abort
  let git = gina#core#get()
  if empty(git)
    return ''
  endif
  let c = get(s:status_count(git), 'staged', 0)
  return c == 0 ? '' : (c . '')
endfunction

function! gina#component#status#unstaged() abort
  let git = gina#core#get()
  if empty(git)
    return ''
  endif
  let c = get(s:status_count(git), 'unstaged', 0)
  return c == 0 ? '' : (c . '')
endfunction

function! gina#component#status#conflicted() abort
  let git = gina#core#get()
  if empty(git)
    return ''
  endif
  let c = get(s:status_count(git), 'conflicted', 0)
  return c == 0 ? '' : (c . '')
endfunction

function! gina#component#status#preset(...) abort
  let git = gina#core#get()
  if empty(git)
    return ''
  endif
  let kind = get(a:000, 0, 'ascii')
  return call('s:preset_' . kind, [])
endfunction


" Private --------------------------------------------------------------------
function! s:get_store(git) abort
  let ref = get(s:Git.ref(a:git, 'HEAD'), 'path', 'HEAD')
  let store = s:Store.of([
        \ s:Git.resolve(a:git, 'index'),
        \ s:Git.resolve(a:git, ref),
        \])
  return store
endfunction

function! s:status_count(git) abort
  let store = s:get_store(a:git)
  let status_count = store.get(s:SLUG, {})
  if !empty(status_count)
    return status_count
  endif
  if !exists('s:status_job')
    let pipe = gina#process#pipe#store()
    let pipe.__on_exit = pipe.on_exit
    let pipe.on_exit = funcref('s:status_on_exit', [store])
    let s:status_job = gina#process#open(a:git, [
          \ 'status',
          \ '--porcelain',
          \ '--ignore-submodules',
          \], pipe)
  endif
  return {
        \ 'staged': 0,
        \ 'unstaged': 0,
        \ 'conflicted': 0,
        \}
endfunction

function! s:status_on_exit(store, exitval) abort dict
  call self.__on_exit(a:exitval)
  silent! unlet! s:status_job
  if a:exitval
    return
  endif
  let status_count = {
        \ 'staged': 0,
        \ 'unstaged': 0,
        \ 'conflicted': 0,
        \}
  for record in self.stdout
    let sign = record[:1]
    if sign =~# '^\%(DD\|AU\|UD\|UA\|DU\|AA\|UU\)$'
      let status_count.conflicted += 1
    elseif sign ==# '??' || sign ==# '!!'
      continue
    else
      if sign =~# '^\S.$'
        let status_count.staged += 1
      endif
      if sign =~# '^.\S$'
        let status_count.unstaged += 1
      endif
    endif
  endfor
  call a:store.set(s:SLUG, status_count)
endfunction

function! s:preset_ascii() abort
  let staged = gina#component#status#staged()
  let unstaged = gina#component#status#unstaged()
  let conflicted = gina#component#status#conflicted()
  let staged = empty(staged) ? '' : ('<' . staged)
  let unstaged = empty(unstaged) ? '' : ('>' . unstaged)
  let conflicted = empty(conflicted) ? '' : ('x' . conflicted)
  return join(filter([staged, unstaged, conflicted], '!empty(v:val)'))
endfunction

function! s:preset_fancy() abort
  let staged = gina#component#status#staged()
  let unstaged = gina#component#status#unstaged()
  let conflicted = gina#component#status#conflicted()
  let staged = empty(staged) ? '' : ('«' . staged)
  let unstaged = empty(unstaged) ? '' : ('»' . unstaged)
  let conflicted = empty(conflicted) ? '' : ('×' . conflicted)
  return join(filter([staged, unstaged, conflicted], '!empty(v:val)'))
endfunction

" NOTE:
" Tracked files might be changed (unstaged) so remove cache when 'modified'
" event has called.
function! s:on_modified(...) abort
  let git = gina#core#get()
  if empty(git)
    return
  endif
  let store = s:get_store(git)
  call store.remove(s:SLUG)
endfunction

if !exists('s:subscribed')
  let s:subscribed = 1
  call gina#core#emitter#subscribe(
        \ 'modified',
        \ function('s:on_modified')
        \)
endif