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

"""
    sphinxtlisp.autodoc
    ~~~~~~~~~~~~~~~~~~~

    Automatically insert docstrings for TLisp functions. Currently we support
    twp methods:
        TLispDocumenter provides an autodoc Documenter for TLisp
        TLispAutoDirective adds an autodoc directive to the TLispDomain
"""
import re
import traceback
import warnings

from docutils.parsers.rst import Directive
from docutils.statemachine import string2lines
from sphinx.ext.autodoc import Documenter, AutoDirective, logger

from .domain import tlisp_sig_re



class TLispDocumenter(Documenter):
    """
    Base class for documenters of TLisp objects.
    """
    objtype = 'tlisp'
    directivetype = 'function'
    member_order = 30
    priority=10
    domain = 'tl'

    @classmethod
    def can_document_member(cls, member, membername, isattr, parent):
        # type: (Any, unicode, bool, Any) -> bool
        return hasattr(member, 'tlisp_signature')

    def parse_name(self):
        """Determine what module to import and what attribute to document.

        Returns True and sets *self.modname*, *self.objpath*, *self.fullname*,
        *self.args* and *self.retann* if parsing and resolving was successful.
        """
        # first, parse the definition -- auto directives for classes and
        # functions can contain a signature which is then used instead of
        # an autogenerated one
        try:
            name, args, retann = tlisp_sig_re.match(self.name).groups()
        except AttributeError:
            # Called from autosummary we get a full python  module::name
            # note - not working yet!
            name = self.name.split('::')[-1]
            args = None
            retann = None
            self.name = "(%s)" % name
            #self.directive.warn('invalid signature for auto%s (%r)' %
            #                    (self.objtype, self.name))
            #return False
        self.args = args
        self.retann = retann
        self.fullname = (name or '')
        self.modname = '' #'tlisp'
        self.objpath = name,
        return True

    def import_object(self):
        """Import an object from the function list

        Returns True if successful, False if an error occurred.
        """

        try:
            logger.debug('[autodoc] TLisp %s', self.fullname)
            # Get environment
            tlispfuncs = self.env.config._tlispfuncs
            if tlispfuncs is None:
                raise Exception("No TLisp function list")
            obj =  tlispfuncs[self.fullname.lower()]
            self.object = obj
            self.args = obj.args
            self.retann = obj.retann
            return True
        except Exception:
            errmsg = 'autodoc: failed to import %s %r' % \
                     (self.objtype, self.fullname)
            errmsg += '; the following exception was raised:\n%s' % \
                      traceback.format_exc()
            logger.debug(errmsg)
            self.directive.warn(errmsg)
            self.env.note_reread()
            return False

    def format_name(self):
        # type: () -> unicode
        """Format the name of *self.object*.
        This normally should be something that can be parsed by the generated
        directive, but doesn't need to be (Sphinx will display it unparsed
        then).
        """
        # Returning blank as all info is in the signature
        return ""

    def format_signature(self):
        return self.object.tlisp_signature


class TLispAutoDirective(Directive):
    """
    TLispAutoDirective class
    """

    # standard docutils directive settings
    has_content = True
    required_arguments = 1
    optional_arguments = 0
    final_argument_whitespace = True
    # allow any options to be passed; the options are parsed further
    # by the selected Documenter
    option_spec = {}

    def warn(self, msg):
        # type: (unicode) -> None
        self.warnings.append(self.reporter.warning(msg, line=self.lineno))    

    def run(self):
        self.reporter = self.state.document.reporter
        self.env = self.state.document.settings.env
        self.warnings = [] # type: List[unicode]

        try:
            source, lineno = self.reporter.get_source_and_line(self.lineno)
        except AttributeError:
            source = lineno = None
        logger.debug('[tlisp-autodoc] %s:%s: input:\n%s',
                     source, lineno, self.block_text)

        # Get environment
        tlispfuncs = self.env.config._tlispfuncs
        if tlispfuncs is None:
            return []

        # Look for the requested object
        objname = self.arguments[0].lower()
        
        if objname not in tlispfuncs.functions:
            self.state_machine.reporter.warning('Unknown function name: %s'%objname, line=self.lineno)

        obj = tlispfuncs.functions[objname]
        source = self.state_machine.input_lines.source(
            self.lineno - self.state_machine.input_offset - 1)

        include_lines = [obj.tlisp_signature] + string2lines(obj.__doc__, convert_whitespace=1)
        self.state_machine.insert_input(include_lines,source)

        return []
