#!/usr/bin/env python
# encoding: utf-8

"""Wrapper functionality around the functions we need from Vim."""

import re

import vim  # pylint:disable=import-error
from vim import error  # pylint:disable=import-error,unused-import

from UltiSnips.compatibility import col2byte, byte2col, \
    as_unicode, as_vimencoding
from UltiSnips.position import Position

from contextlib import contextmanager


class VimBuffer(object):

    """Wrapper around the current Vim buffer."""

    def __getitem__(self, idx):
        if isinstance(idx, slice):  # Py3
            return self.__getslice__(idx.start, idx.stop)
        rv = vim.current.buffer[idx]
        return as_unicode(rv)

    def __getslice__(self, i, j):  # pylint:disable=no-self-use
        rv = vim.current.buffer[i:j]
        return [as_unicode(l) for l in rv]

    def __setitem__(self, idx, text):
        if isinstance(idx, slice):  # Py3
            return self.__setslice__(idx.start, idx.stop, text)
        vim.current.buffer[idx] = as_vimencoding(text)

    def __setslice__(self, i, j, text):  # pylint:disable=no-self-use
        vim.current.buffer[i:j] = [as_vimencoding(l) for l in text]

    def __len__(self):
        return len(vim.current.buffer)

    @property
    def line_till_cursor(self):  # pylint:disable=no-self-use
        """Returns the text before the cursor."""
        _, col = self.cursor
        return as_unicode(vim.current.line)[:col]

    @property
    def number(self):  # pylint:disable=no-self-use
        """The bufnr() of the current buffer."""
        return vim.current.buffer.number

    @property
    def cursor(self):  # pylint:disable=no-self-use
        """The current windows cursor.

        Note that this is 0 based in col and 0 based in line which is
        different from Vim's cursor.

        """
        line, nbyte = vim.current.window.cursor
        col = byte2col(line, nbyte)
        return Position(line - 1, col)

    @cursor.setter
    def cursor(self, pos):  # pylint:disable=no-self-use
        """See getter."""
        nbyte = col2byte(pos.line + 1, pos.col)
        vim.current.window.cursor = pos.line + 1, nbyte
buf = VimBuffer()  # pylint:disable=invalid-name

@contextmanager
def toggle_opt(name, new_value):
    old_value = eval('&' + name)
    command('set {0}={1}'.format(name, new_value))
    try:
        yield
    finally:
        command('set {0}={1}'.format(name, old_value))

@contextmanager
def save_mark(name):
    old_pos = get_mark_pos(name)
    try:
        yield
    finally:
        if _is_pos_zero(old_pos):
            delete_mark(name)
        else:
            set_mark_from_pos(name, old_pos)

def escape(inp):
    """Creates a vim-friendly string from a group of
    dicts, lists and strings."""
    def conv(obj):
        """Convert obj."""
        if isinstance(obj, list):
            rv = as_unicode('[' + ','.join(conv(o) for o in obj) + ']')
        elif isinstance(obj, dict):
            rv = as_unicode('{' + ','.join([
                '%s:%s' % (conv(key), conv(value))
                for key, value in obj.iteritems()]) + '}')
        else:
            rv = as_unicode('"%s"') % as_unicode(obj).replace('"', '\\"')
        return rv
    return conv(inp)


def command(cmd):
    """Wraps vim.command."""
    return as_unicode(vim.command(as_vimencoding(cmd)))


def eval(text):
    """Wraps vim.eval."""
    rv = vim.eval(as_vimencoding(text))
    if not isinstance(rv, (dict, list)):
        return as_unicode(rv)
    return rv


def feedkeys(keys, mode='n'):
    """Wrapper around vim's feedkeys function.

    Mainly for convenience.

    """
    if eval('mode()') == 'n':
        if keys == 'a':
            cursor_pos = get_cursor_pos()
            cursor_pos[2] = int(cursor_pos[2]) + 1
            set_cursor_from_pos(cursor_pos)
        if keys in 'ai':
            keys = 'startinsert'

    if keys == 'startinsert':
        command('startinsert')
    else:
        command(as_unicode(r'call feedkeys("%s", "%s")') % (keys, mode))


def new_scratch_buffer(text):
    """Create a new scratch buffer with the text given."""
    vim.command('botright new')
    vim.command('set ft=')
    vim.command('set buftype=nofile')

    vim.current.buffer[:] = text.splitlines()

    feedkeys(r"\<Esc>")


def virtual_position(line, col):
    """Runs the position through virtcol() and returns the result."""
    nbytes = col2byte(line, col)
    return line, int(eval('virtcol([%d, %d])' % (line, nbytes)))


def select(start, end):
    """Select the span in Select mode."""
    _unmap_select_mode_mapping()

    selection = eval('&selection')