# -*- coding: utf-8 -*-

###########################################################################
#                              General Stuff                              #
###########################################################################
global !p
import vim
from os import path as ospath
from string import Template
import re
from collections import Counter

#http://docutils.sourceforge.net/docs/ref/rst/roles.html
TEXT_ROLES = ['emphasis','literal','code','math',
              'pep-reference','rfc-reference',
              'strong','subscript','superscript',
              'title-reference','raw'] 
TEXT_ROLES_REGEX = r'\.\.\srole::?\s(w+)'

#http://docutils.sourceforge.net/docs/ref/rst/directives.html#specific-admonitions
SPECIFIC_ADMONITIONS = ["attention", "caution", "danger", 
                        "error", "hint", "important", "note", 
                        "tip", "warning"]
#http://docutils.sourceforge.net/docs/ref/rst/directives.html
DIRECTIVES = ['topic','sidebar','math','epigraph',
              'parsed-literal','code','highlights',
              'pull-quote','compound','container',
              'list-table','class','sectnum',
              'role','default-role','unicode',
              'raw']

NONE_CONTENT_DIRECTIVES = ['rubric', 'contents', 'header',
                           'footer', 'date', 'include', 'title']

INCLUDABLE_DIRECTIVES = ['image', 'figure', 'include']
# CJK chars
# http://stackoverflow.com/questions/2718196/find-all-chinese-text-in-a-string-using-python-and-regex
CJK_RE = re.compile(u'[⺀-⺙⺛-⻳⼀-⿕々〇〡-〩〸-〺〻㐀-䶵一-鿃豈-鶴侮-頻並-龎]', re.UNICODE)


def has_cjk(char):
    """
    Detect char contains CJK character
    
    :param char: characters needs to be detect 
    """
    try:
        CJK_RE.finditer(char).next()
    except StopIteration:
        return False
    else:
        return True

def real_filename(filename):
    """peal extension name off if possible
    # i.e. "foo.bar.png will return "foo.bar"
	"""
    return ospath.splitext(filename)[0]

def check_file_exist(rst_path, relative_path):
    """
    For RST file, it can just include files as relative path.
    
    :param rst_path: absolute path to rst file
    :param relative_path: path related to rst file
    :return: relative file's absolute path if file exist
    """
    abs_path = ospath.join(ospath.dirname(rst_path), relative_path)
    if ospath.isfile(abs_path):
        return abs_path


def rst_char_len(char):
    """ 
    return len of string which fit in rst
    For instance:chinese "我" decode as only one character, 
    However, the rst interpreter needs 2 "=" instead of 1.

    :param: char needs to be count
    """
    return len(re.findall(r'[^\u4e00-\u9fff\s]', char))+len(char)

def	make_items(times, leading='+'):
    """
    make lines with leading char multitimes
    
    :param: times, how many times you need
    :param: leading, leading character
    """
    times = int(times)
    if leading == 1:
        msg = ""
        for x in xrange(1, times+1):
            msg += "%s. Item\n" % x
        return msg
    else:
        return ("%s Item\n" % leading) * times


def look_up_directives(regex, fpath):
    """
    find all directive args in given file
    :param: regex, the regex that needs to match
    :param: path, to path to rst file

    :return: list, empty list if nothing match
    """
    try:
        with open(fpath) as source:
            match = re.findall(regex, source.read())
    except IOError:
        match = []
    return match


def get_popular_code_type():
    """
    find most popular code type in the given rst

    :param path: file to detect

    :return: string, most popular code type in file
    """
    buf = "".join(vim.current.buffer)
    types = re.findall(r'[:|\.\.\s]code::?\s(\w+)', buf)
    try:
        popular_type = Counter(types).most_common()[0][0]
    except IndexError:
        popular_type = "lua" # Don't break default
    return popular_type
 

def complete(t, opts):
    """
    get options that start with t

    :param t: query string
    :param opts: list that needs to be completed

    :return: a string that start with t
    """
    msg = "({0})"
    if t:
        opts = [ m[len(t):] for m in opts if m.startswith(t) ]
    if len(opts) == 1:
        return opts[0]

    if not len(opts):
        msg = "{0}"
    return msg.format("|".join(opts))

endglobal

snippet part "Part" b
`!p snip.rv = rst_char_len(t[1])*'#'`
${1:Part name}
`!p snip.rv = rst_char_len(t[1])*'#'`

$0
endsnippet

snippet sec "Section" b
${1:Section name}
`!p snip.rv = rst_char_len(t[1])*'='`

$0
endsnippet

snippet ssec "Subsection" b
${1:Section name}
`!p snip.rv = rst_char_len(t[1])*'-'`

$0
endsnippet

snippet sssec "Subsubsection" b
${1:Section name}
`!p snip.rv = rst_char_len(t[1])*'^'`

$0
endsnippet

snippet chap "Chapter" b
`!p snip.rv = rst_char_len(t[1])*'*'`
${1:Chapter name}
`!p snip.rv = rst_char_len(t[1])*'*'`

$0
endsnippet

snippet para "Paragraph" b
${1:Paragraph name}
`!p snip.rv = rst_char_len(t[1])*'"'`

$0
endsnippet

snippet em "Emphasize string" i
`!p
# dirty but works with CJK charactor detection
if has_cjk(vim.current.line):
    snip.rv ="\ "`*${1:${VISUAL:Em}}*`!p
if has_cjk(vim.current.line): 
    snip.rv ="\ "
else:
    snip.rv = " "
`$0
endsnippet

snippet st "Strong string" i
`!p 
if has_cjk(vim.current.line):
    snip.rv ="\ "`**${1:${VISUAL:Strong}}**`!p
if has_cjk(vim.current.line): 
    snip.rv ="\ "
else:
    snip.rv = " "
`$0
endsnippet

snippet "li(st)? (?P<num>\d+)" "List" br
$0
`!p
# usage: li 4<tab>
# which will extand into a unordered list contains 4 items
snip.rv = make_items(match.groupdict()['num'])
`
endsnippet

snippet "ol(st)? (?P<num>\d+)" "Order List" br
$0
`!p
# usage: ol 4<tab>
# which will extand into a ordered list contains 4 items
snip.rv = make_items(match.groupdict()['num'], 1)
`
endsnippet
###########################################################################
#                         More Specialized Stuff.                         #
###########################################################################
snippet cb "Code Block" b
.. code-block:: ${1:`!p snip.rv = get_popular_code_type()`}

   ${2:code}

$0
endsnippet

# match snippets :
# img, inc, fig
snippet id "Includable Directives" b
`!p 
real_name=real_filename(ospath.basename(t[2]))
di=t[1][:2]

link=""
content=""

if di == 'im':
    link = "|{0}|".format(real_name)

if di == 'fi':
    content="""
    :alt: {0}
    {0}""".format(real_name)
`
..`!p snip.rv = " %s" % link if link else ""` $1`!p snip.rv=complete(t[1], INCLUDABLE_DIRECTIVES)`:: ${2:file}`!p if content:
    snip.rv +="    "+content`
`!p
# Tip of whether file is exist in comment type
if not check_file_exist(path, t[2]):
    snip.rv='.. FILE {0} does not exist'.format(t[2])
else:
    snip.rv=""
`$0
endsnippet

snippet di "Directives" b
.. $1`!p snip.rv=complete(t[1], DIRECTIVES)`:: $2

    ${3:Content}
$0
endsnippet

snippet nd "None Content Directives" b
.. $1`!p snip.rv=complete(t[1], NONE_CONTENT_DIRECTIVES)`:: $2
$0
endsnippet

snippet sa "Specific Admonitions" b
.. $1`!p snip.rv =complete(t[1], SPECIFIC_ADMONITIONS)`::
    
    ${2:Content}

$0
endsnippet

#it will be trigger at start of line or after a word
snippet ro "Text Roles" w
\ :$1`!p snip.rv=complete(t[1],
                          TEXT_ROLES+look_up_directives(TEXT_ROLES_REGEX,
                                                        path))`:\`$2\`\ 
endsnippet

############
#  Sphinx  #
############

snippet sid "SideBar" b
.. sidebar:: ${1:SideBar Title}

    ${2:SideBar Content}
endsnippet
# vim:ft=snippets: