"=============================================================================
" FILE: exrename.vim
" AUTHOR: Shougo Matsushita <Shougo.Matsu@gmail.com>
" EDITOR: Alisue <lambdalisue@hashnote.net>
"
" License: MIT license
"=============================================================================
let s:PREFIX = unite#util#is_windows() ? '[exrename]' : '*exrename*'
function! s:void(exrename) abort
endfunction
let s:VOID = function('s:void')

function! unite#exrename#create_buffer(candidates, ...) abort "{{{
  let options = extend({
        \ 'cwd': getcwd(),
        \ 'bufnr': bufnr('%'),
        \ 'buffer_name': '',
        \ 'post_rename_callback': s:VOID,
        \}, get(a:000, 0, {}))
  if options.cwd !~# '/$'
    " current working directory MUST end with a trailing slash
    let options.cwd .= '/'
  endif
  if options.buffer_name ==# ''
    let options.buffer_name = s:PREFIX
  else
    let options.buffer_name = s:PREFIX . ' - ' . options.buffer_name
  endif

  vsplit
  redraw
  execute 'edit' fnameescape(options.buffer_name)

  setlocal buftype=acwrite
  setlocal noswapfile
  setfiletype unite_exrename
  let b:exrename = options

  call unite#util#lcd(b:exrename.cwd)

  nnoremap <buffer><silent> q :<C-u>call <SID>exit(bufnr('%'))<CR>
  augroup unite-exrename
    autocmd! * <buffer>
    autocmd BufHidden <buffer> call s:exit(expand('<abuf>'))
    autocmd BufWriteCmd <buffer> call s:do_rename()
    autocmd CursorMoved,CursorMovedI <buffer> call s:check_lines()
  augroup END

  " Clean up the screen.
  silent % delete _
  silent! syntax clear uniteExrenameOriginal

  " validate candidates and register
  let unique_filenames = []
  let b:exrename.candidates = []
  let b:exrename.filenames = []
  let cnt = 1
  for candidate in a:candidates
    " make sure that the 'action__path' is absolute path
    if !s:is_absolute(candidate.action__path)
      let candidate.action__path = b:exrename.cwd . candidate.action__path
    endif
    " make sure that the 'action__path' exists
    if !filewritable(candidate.action__path)
          \ && !isdirectory(candidate.action__path)
      redraw
      echo candidate.action__path "does not exist. Skip."
      continue
    endif
    " make sure that the 'action__path' is unique
    if index(unique_filenames, candidate.action__path) != -1
      redraw
      echo candidate.action__path "is duplicated. Skip."
      continue
    endif
    " create filename
    let filename = candidate.action__path
    if stridx(filename, b:exrename.cwd) == 0
      let filename = filename[len(b:exrename.cwd) :]
    endif
    " directory should end with a trailing slash (to distinguish easily)
    if s:is_directory(candidate)
      let filename .= '/'
    endif

    execute 'syntax match uniteExrenameOriginal'
          \ '/'.printf('^\%%%dl%s$', cnt,
          \ escape(unite#util#escape_pattern(filename), '/')).'/'
    " register
    call add(unique_filenames, candidate.action__path)
    call add(b:exrename.candidates, candidate)
    call add(b:exrename.filenames, filename)
    let cnt += 1
  endfor
  " write filenames
  let [undolevels, &undolevels] = [&undolevels, -1]
  try
    call setline(1, b:exrename.filenames)
  finally
    let &undolevels = undolevels
  endtry
  setlocal nomodified
endfunction"}}}

function! s:is_absolute(path) abort "{{{
  return a:path =~# '^\%(\a\a\+:\)\|^\%(\a:\|/\)'
endfunction "}}}

function! s:is_directory(candidate) abort "{{{
  if has_key(a:candidate, 'vimfiler__is_directory')
    return a:candidate['vimfiler__is_directory']
  endif
  let kind = get(a:candidate, 'kind', '')
  return index(type(kind) != type([]) ? [kind] : kind, 'directory') >= 0
endfunction"}}}

function! s:do_rename() abort "{{{
  if line('$') != len(b:exrename.filenames)
    echohl Error | echo 'Invalid rename buffer!' | echohl None
    return
  endif

  " Rename files.
  let linenr = 1
  let max = line('$')
  while linenr <= max
    let filename = b:exrename.filenames[linenr - 1]

    redraw
    echo printf('(%'.len(max).'d/%d): %s -> %s',
          \ linenr, max, filename, getline(linenr))

    if filename !=# getline(linenr)
      let old_file = b:exrename.candidates[linenr - 1].action__path
      let new_file = unite#util#expand(getline(linenr))
      if new_file !~ '^\%(\a\a\+:\)\|^\%(\a:\|/\)'
        let new_file = b:exrename.cwd . new_file
      endif

      if unite#kinds#file#do_rename(old_file, new_file)
        " Rename error
        continue
      endif

      " update b:exrename
      let b:exrename.filenames[linenr - 1] = getline(linenr)
      let b:exrename.candidates[linenr - 1].action__path = new_file
    endif
    let linenr += 1
  endwhile

  redraw
  echo 'Rename done!'

  setlocal nomodified

  if b:exrename.post_rename_callback != s:VOID
    call b:exrename.post_rename_callback(b:exrename)
  endif
endfunction"}}}

function! s:exit(bufnr) abort "{{{
  if !bufexists(a:bufnr)
    return
  endif

  " Switch buffer.
  if winnr('$') != 1
    close
  else
    call s:custom_alternate_buffer()
  endif
  silent execute 'bdelete!' a:bufnr
endfunction "}}}

function! s:check_lines() abort "{{{
  if !exists('b:exrename')
    return
  endif

  if line('$') != len(b:exrename.filenames)
    echohl Error | echo 'Invalid rename buffer!' | echohl None
    return
  endif
endfunction "}}}

function! s:custom_alternate_buffer() abort "{{{
  if bufnr('%') != bufnr('#') && buflisted(bufnr('#'))
    buffer #
  endif

  let cnt = 0
  let pos = 1
  let current = 0
  while pos <= bufnr('$')
    if buflisted(pos)
      if pos == bufnr('%')
        let current = cnt
      endif

      let cnt += 1
    endif

    let pos += 1
  endwhile

  if current > cnt / 2
    bprevious
  else
    bnext
  endif
endfunction "}}}

" vim: foldmethod=marker