"=============================================================================
" FILE: bg.vim
" AUTHOR: Shougo Matsushita <Shougo.Matsu@gmail.com>
" License: MIT license  {{{
"     Permission is hereby granted, free of charge, to any person obtaining
"     a copy of this software and associated documentation files (the
"     "Software"), to deal in the Software without restriction, including
"     without limitation the rights to use, copy, modify, merge, publish,
"     distribute, sublicense, and/or sell copies of the Software, and to
"     permit persons to whom the Software is furnished to do so, subject to
"     the following conditions:
"
"     The above copyright notice and this permission notice shall be included
"     in all copies or substantial portions of the Software.
"
"     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
"     OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
"     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
"     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
"     CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
"     TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
"     SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
" }}}
"=============================================================================

let s:manager = vimshell#util#get_vital().import('Vim.Buffer')

let s:command = {
      \ 'name' : 'bg',
      \ 'kind' : 'execute',
      \ 'description' : 'bg [{option}...] {command}',
      \}
function! s:command.execute(commands, context) abort "{{{
  " Execute command in background.
  if empty(a:commands)
    return
  endif

  let commands = a:commands
  let [commands[0].args, options] = vimshell#parser#getopt(commands[0].args, {
        \ 'arg=' : ['--encoding', '--split', '--filetype'],
        \ }, {
        \ '--encoding' : vimshell#interactive#get_default_encoding(a:commands),
        \ '--split' : g:vimshell_split_command,
        \ '--filetype' : 'background',
        \ })

  if empty(commands[0].args)
    return
  endif

  if len(commands[0].args) == 1
        \ && (vimshell#util#is_windows() || !executable(commands[0].args[0]))
    let ext = fnamemodify(commands[0].args[0], ':e')
    if has_key(g:vimshell_execute_file_list, ext)
      " Use g:vimshell_execute_file_list
      let commands[0].args =
            \ [g:vimshell_execute_file_list[ext], commands[0].args[0]]
    endif
  endif

  " Background execute.
  if exists('b:interactive') &&
        \ !empty(b:interactive.process) && b:interactive.process.is_valid
    " Delete zombie process.
    call vimshell#interactive#force_exit()
  endif

  " Encoding conversion.
  if options['--encoding'] != '' && options['--encoding'] != &encoding
    for command in commands
      call map(command.args, 'vimproc#util#iconv(v:val, &encoding, options["--encoding"])')
    endfor
  endif

  " Set environment variables.
  let environments_save = vimshell#util#set_variables({
        \ '$TERM' : g:vimshell_environment_term,
        \ '$TERMCAP' : 'COLUMNS=' . vimshell#helpers#get_winwidth(),
        \ '$VIMSHELL' : 1,
        \ '$COLUMNS' : vimshell#helpers#get_winwidth(),
        \ '$LINES' : g:vimshell_scrollback_limit,
        \ '$VIMSHELL_TERM' : 'background',
        \ '$EDITOR' : vimshell#helpers#get_editor_name(),
        \ '$GIT_EDITOR' : vimshell#helpers#get_editor_name(),
        \ '$PAGER' : g:vimshell_cat_command,
        \ '$GIT_PAGER' : g:vimshell_cat_command,
        \})

  " Initialize.
  let sub = vimproc#plineopen3(commands)

  " Restore environment variables.
  call vimshell#util#restore_variables(environments_save)

  " Set variables.
  let interactive = {
        \ 'type' : 'background',
        \ 'syntax' : &syntax,
        \ 'process' : sub,
        \ 'fd' : a:context.fd,
        \ 'encoding' : options['--encoding'],
        \ 'is_pty' : 0,
        \ 'command' : commands[0].args[0],
        \ 'cmdline' : join(commands[0].args),
        \ 'echoback_linenr' : 0,
        \ 'stdout_cache' : '',
        \ 'stderr_cache' : '',
        \ 'width' : vimshell#helpers#get_winwidth(),
        \ 'height' : g:vimshell_scrollback_limit,
        \ 'hook_functions_table' : {},
        \}

  " Input from stdin.
  if interactive.fd.stdin != ''
    call interactive.process.stdin.write(
          \ vimshell#interactive##read(a:context.fd))
  endif
  call interactive.process.stdin.close()

  return vimshell#commands#bg#init(a:commands, a:context, options, interactive)
endfunction"}}}
function! s:command.complete(args) abort "{{{
  return vimshell#complete#helper#command_args(a:args)
endfunction"}}}

function! vimshell#commands#bg#define() abort
  return s:command
endfunction

function! vimshell#commands#bg#init(commands, context, options, interactive) abort "{{{
  " Save current directiory.
  let cwd = getcwd()

  let [new_pos, old_pos] = vimshell#helpers#split(a:options['--split'])

  let args = ''
  for command in a:commands
    let args .= join(command.args)
  endfor

  let loaded = s:manager.open('bg-'.substitute(args,
        \ '[<>|]', '_', 'g') .'@'.(bufnr('$')+1), 'silent edit')
  if !loaded
    call vimshell#echo_error(
          \ '[vimshell] Failed to open Buffer.')
    return
  endif

  let [new_pos[2], new_pos[3]] = [bufnr('%'), getpos('.')]

  " Common.
  call vimshell#init#_default_settings()

  " For bg.
  setlocal nomodifiable
  let &filetype = a:options['--filetype']

  let b:interactive = a:interactive

  call vimshell#cd(cwd)

  " Set syntax.
  syn region   InteractiveError   start=+!!!+ end=+!!!+
        \ contains=InteractiveErrorHidden oneline
  if v:version >= 703
    " Supported conceal features.
    syn match   InteractiveErrorHidden            '!!!' contained conceal
  else
    syn match   InteractiveErrorHidden            '!!!' contained
  endif
  hi def link InteractiveErrorHidden Error

  augroup vimshell
    autocmd BufDelete,VimLeavePre <buffer>
          \ call vimshell#interactive#hang_up(expand('<afile>'))
    autocmd BufWinEnter,WinEnter <buffer> call s:event_bufwin_enter()
  augroup END

  nnoremap <buffer><silent> <Plug>(vimshell_interactive_execute_line)
        \ :<C-u>call <SID>on_execute()<CR>
  nnoremap <buffer><silent> <Plug>(vimshell_interactive_interrupt)
        \ :<C-u>call vimshell#interactive#hang_up(bufname('%'))<CR>
  nnoremap <buffer><silent> <Plug>(vimshell_interactive_exit)
        \ :<C-u>call vimshell#interactive#quit_buffer()<CR>

  nmap <buffer><CR>      <Plug>(vimshell_interactive_execute_line)
  nmap <buffer><C-c>     <Plug>(vimshell_interactive_interrupt)
  nmap <buffer>q         <Plug>(vimshell_interactive_exit)

  call s:on_execute()

  noautocmd call vimshell#helpers#restore_pos(old_pos)

  if has_key(a:context, 'is_single_command') && a:context.is_single_command
    call vimshell#next_prompt(a:context, 0)
    noautocmd call vimshell#helpers#restore_pos(new_pos)
    stopinsert
  endif
endfunction"}}}

function! s:on_execute() abort "{{{
  setlocal modifiable
  echo 'Running command.'
  call vimshell#interactive#execute_process_out(mode() ==# 'i')
  redraw
  echo ''
  setlocal nomodifiable
endfunction"}}}
function! s:on_exit() abort "{{{
  if !b:interactive.process.is_valid
    call vimshell#util#delete_buffer()
  endif
endfunction "}}}
function! s:event_bufwin_enter() abort "{{{
  if has('conceal')
    setlocal conceallevel=3
    setlocal concealcursor=nvi
  endif
endfunction"}}}