#!/usr/bin/env python
import os, re, sys, time
import argparse

try:
    import neovim
except:
    try:
        os.execvp('nvim', sys.argv)
    except:
        os.execvp('vim', sys.argv)
    finally:
        exit(0)

# Get a list of buffers that haven't been deleted. `nvim.buffers` includes
# buffers that have had `:bdelete` called on them and aren't in the buffer
# list, so we have to filter those out.
def get_listed_buffers(nvim):
    # print(*(buf.number for buf in nvim.buffers))
    return set(buf.number for buf in nvim.buffers \
        if nvim.eval('buflisted(%d)' % buf.number))

pid = os.getpid()
ppid =os.getppid()
pgid = os.getpgid(pid)
sid = os.getsid(pid)
# print('pid:', pid, 'pgid:', pgid, 'ppid:', ppid, 'sid:', sid)

# print(sys.argv)
argparser = argparse.ArgumentParser()
argparser.add_argument('-W', '--WIN', dest='win', help='window to execute cmd')
argparser.add_argument('-C', '--CMD', dest='cmd', action='append', help='cmd to specify the window')
argparser.add_argument('-D', '--DIFF', dest='diff', action='store_true', help='diff the files')
argparser.add_argument('-S', '--SPLIT', dest='split', help='how to split, None or s or v')
argparser.add_argument('-A', '--ATTACH', dest='attach', action='store_true', help='attach to an existing instance')
myargs, argv = argparser.parse_known_args()
# For now, treat all arguments that don't start with - or + as filenames. This
# is good enough to recognize '-f' and `+11`, which is all this script really
# needs right now.


if not myargs.attach and 'NVIM_LISTEN_ADDRESS' not in os.environ:
    # If we aren't running inside a `:terminal`, just exec nvim.
    os.execvp('nvim', [sys.argv[0]] + argv)
    exit(0)

address_file = None
if myargs.attach:
    if not argv:
        exit(1)
    address_file = os.path.expanduser('~/.vim/custom/tmp/NVIM_LISTEN_ADDRESS.txt')
    nvim_socket = None
    if os.path.exists(address_file):
        for addr in open(address_file):
            addr = addr.rstrip('\n')
            if os.path.exists(addr):
                nvim_socket = addr
                break
    # print(address_file, nvim_socket)
else:
    nvim_socket = os.environ['NVIM_LISTEN_ADDRESS']


try:
    nvim = neovim.attach('socket', path=nvim_socket)
    print('embed')
    isembed = True
except:
    if myargs.attach:
        print(f'cannot attach to {nvim_socket}, maybe no server is running.\nExecute :Serve in the server')
        exit(2)

    print('socket does not exist, creating new instance and listen to', nvim_socket)
    os.execvp('nvim', [sys.argv[0]] + argv)
    exit(0)

existing_buffers = get_listed_buffers(nvim)
if myargs.cmd is not None:
    for cmd in myargs.cmd:
        if cmd:
            nvim.command(cmd)

filenames = [
    re.sub(' ', '\ ', os.path.abspath(arg))
        for arg in argv if not arg[0] in ['-', '+']
]
newfiles = [f for f in filenames if not nvim.call('buflisted', f)]
print('allfiles:', filenames)
print('newfiles:', newfiles)

if myargs.win is not None:
    nvim.command(myargs.win + 'wincmd w')

if myargs.diff:
    nvim.command('tabnew')
elif myargs.split is None:
    if myargs.win is None:
        nvim.command('split')
elif myargs.split == 's':
    nvim.command('split')
elif myargs.split == 'v':
    nvim.command('vsplit')
elif myargs.split == 't':
    nvim.command('tab split')
else:
    pass

if address_file:
    nvim.command('arglocal | args %s' % ' ' .join(filenames))
    if myargs.diff:
        nvim.command('vert all')
        nvim.command('windo diffthis')
    exit(0)

if pid == pgid:
    if filenames:
        nvim.command('args %s' % ' '.join(filenames))
    else:
        nvim.command('enew')
else:  # if pid != pgid, then it belongs to another process group
    # def opened_by_other(filename):
    #     regexes = [
    #         r'.git/COMMIT_EDITMSG$',
    #         r'.git/rebase-merge/git-rebase-todo$',
    #         r'ipython_edit_.*\.py',
    #         r'/crontab$',
    #     ]
    #     return any(re.search(regex, filename) for regex in regexes)
    print('nvim launched by other process')

    assert len(filenames) == 1, 'should open one file at a time'
    nvim.command('args %s' % ' '.join(filenames))
    all_buffers = get_listed_buffers(nvim)
    new_buffers = all_buffers.difference(existing_buffers)

    channel_id = nvim.channel_id
    buffer = nvim.eval('bufnr(argv()[0])')
    nvim.command('setlocal bufhidden=delete')
    nvim.command('autocmd! BufDelete <buffer={}> write | call rpcnotify({}, "Finished", bufname(""))'.format(buffer, channel_id))

    if 0:
        msg = nvim.next_message() # block until `rpcnotify` is called
        # msg is either of
        # - ('reqeust', name, args, response)  (response is a msgpack_rpc.async_session.Response object)
        # - ('notification', name, args)
        # if it is 'request', call response.send(value, error=False) to respond
        assert msg[0] == 'notification' and msg[1] == 'Finished', 'something unexpected happened'
    else:
        # alternatively:
        # this is an example to demonstrate how to write onreqest, it will never be invoked
        def onrequest(name, args):
            if name == 'Finished':
                raise neovim.ErrorResponse('please send Finished through rpcnotify')
            else:
                raise ValueError('unknown method:', name)
            return 'no response'

        def onnotification(name, args):
            assert name == 'Finished' and len(args) == 1
            print('Finished: ', args[0])
            open_buffers = [window.buffer.number for window in nvim.windows]
            stay_open = any([buffer in open_buffers for buffer in new_buffers])
            if not stay_open:
                nvim.stop_loop()
        nvim.run_loop(onrequest, onnotification)
    nvim.command('argdel *')
print('nvim exited')



