'''
Extension for enhancing sphinx documentation generation for cython module
'''

import re
import types
import sys
from os.path import dirname, join
import sphinx
from sphinx.ext.autodoc import MethodDocumenter

class CythonMethodDocumenter(MethodDocumenter):
    # XXX I don't understand the impact of having a priority more than the
    # attribute or instance method but the thing is, if it's a cython module,
    # the attribute will be preferred over method.
    priority = 12

def is_cython_extension(what, obj):
    # try to check if the first line of the doc is a signature
    doc = obj.__doc__
    if not doc:
        return False
    doc = doc.split('\n')
    if not len(doc):
        return False
    doc = doc[0]

    # an identifier starts with a letter or underscore
    # followed by optional numbers or letters or underscores
    identifier_pattern = r"([a-zA-Z_][a-zA-Z0-9_]*)"
    params_pattern = r"\((.*)\)"

    # test for cython cpdef
    if what in ('attribute', 'method') and hasattr(obj, '__objclass__'):
        # match identifier.identifier(anything).
        return re.match(
                r"^" + identifier_pattern
                + r"\." + identifier_pattern + params_pattern,
                doc)
    # test for cython class
    if what == 'class' and hasattr(obj, '__pyx_vtable__'):
        # match identifier(anything)
        return re.match(r"^" + identifier_pattern + params_pattern, doc)

    # test for python method in cython class
    if what in ('method', 'function') and obj.__class__ == types.BuiltinFunctionType:
        # match identifier(anything) where
        return re.match(r"^" + identifier_pattern + params_pattern, doc)

def callback_docstring(app, what, name, obj, options, lines):
    if what == 'module':
        # remove empty lines
        while len(lines):
            line = lines[0].strip()
            if not line.startswith('.. _') and line != '':
                break
            lines.pop(0)

        # if we still have lines, remove the title
        if len(lines):
            lines.pop(0)

        # if the title is followed by a separator, remove it.
        if len(lines) and lines[0].startswith('=='):
            lines.pop(0)

    elif is_cython_extension(what, obj) and lines:
        if what == 'class':
            lines.pop(0)
        line = lines.pop(0)

        # trick to realign the first line to the second one.
        # FIXME: fail if we finishing with::
        line_with_text = [x for x in lines if len(x.strip())]
        if len(line_with_text) and line is not None and len(lines):
            l = len(line_with_text[0]) - len(line_with_text[0].lstrip())
        else:
            l = 0
        lines.insert(0, ' ' * l + line)

        # calculate the minimum space available
        min_space = 999
        for line in lines:
            if not line.strip():
                continue
            min_space = min(min_space, len(line) - len(line.lstrip()))

        # remove that kind of space now.
        if min_space > 0:
            spaces = ' ' * min_space
            for idx, line in enumerate(lines):
                if not line.strip():
                    continue
                if not line.startswith(spaces):
                    continue
                lines[idx] = line[min_space:]

def callback_signature(app, what, name, obj, options, signature,
                       return_annotation):
    # remove the first 'self' argument, because python autodoc don't
    # add it for python method class. So do the same for cython class.
    if is_cython_extension(what, obj):
        try:
            doc = obj.__doc__.split('\n').pop(0)
            doc = '(%s' % doc.split('(')[1]
            doc = doc.replace('(self, ', '(')
            doc = doc.replace('(self)', '( )')
            return doc, None
        except AttributeError:
            pass
        except IndexError:
            pass

def setup(app):
    import kivy
    sys.path += [join(dirname(kivy.__file__), 'extras')]
    from highlight import KivyLexer

    if sphinx.version_info[0] >= 3:
        app.add_lexer('kv', KivyLexer)
    else:
        app.add_lexer('kv', KivyLexer())
    app.add_autodocumenter(CythonMethodDocumenter)
    app.connect('autodoc-process-docstring', callback_docstring)
    app.connect('autodoc-process-signature', callback_signature)
