# vim: set ft=python fileencoding=utf-8 tw=72 fdm=indent nowrap:
"""
reStructuredText translation to HTML and metadata retrieval.

:copyright: © 2012-2020, J. A. Corbal
:license: MIT

.. note::
    There are some presentation and parsing probles using the docutils
    ``Writer`` class from ``html5_polyglot`` if docutils version is
    ``<0.15``.  The result is not the same, some settings are ignored,
    and the ``doctree`` seems to be interpreted in a different way
    concerning the ``docinfo`` element, way too important for the
    document metadata.  The ``fragment`` and the ``body`` parts of the
    publisher data structure seem to have also some erratic behaviour.
    It's required ``docutils>=0.15``.
"""

from docutils.core import publish_string, publish_parts, publish_doctree
from docutils.nodes import docinfo
from docutils.writers.html5_polyglot import Writer


class ParserRst:
    """Generate HTML body from a reStructuredText source file.

    .. versionadded:: 1.3.1a4
        Add reStructuredText support.

    .. seealso:: :class:`ParserMd` and :mod:`parser`.
    """

    def __init__(self, input_data, encoding='utf-8', logger=None):
        """Constructor.

        This class has methods to parse the markup language and get the
        information.  The logging activity is only set to debug, since
        it's not necessary to tell continuously the default activity.

        :param input_data: File from where the data is taken
        :type input_data: str
        :param encoding: Encoding the input file is in
        :type encoding: str
        :param output_format: Output HTML version
        :type output: str
        :param logger: Logger where to store activity in
        :type logger: logging.Logger
        """
        self.input_data = input_data
        self.encoding = encoding
        self.logger = logger

        self.settings = {
            'attribution': None,  # dash, parentheses
            'cloak_email_addresses': True,
            'compact_field_lists': True,
            'compact_lists': True,
            'doctitle_xform': True,
            'embed_stylesheet': False,
            'footnote_references': 'superscript',  # 'brackets'
            'initial_header_level': 2,
            'input_encoding': self.encoding,
            'language_code': 'en',
            'math_output': 'MathML',  # 'LaTeX', 'MathJax', 'HTML'
            'output_encoding': 'utf-8',
            'raw_enabled': True,
            'sectsubtitle_xform': True,
            'smart_quotes': False,
            'smartquotes_locales': True,
            'strip_comments': True,
            # 'strip_elements_with_classes': ['docinfo simple', 'docinfo'],
            # 'stylesheet_path': stylesheet_path,
            'syntax_highlight': True,
            'table_style': 'align-center',  # borderless, booktabs, align-
            'tab_width': 4,
            'toc_backlinks': False,
            'trim_footnote_reference_space': True,
        }

    def html(self):
        """Generate HTML from  a reStructuredText file.

        .. todo::
            This reStructuredText parser should import some locally
            defined roles for this application.
        """
        with open(self.input_data, "r", encoding=self.encoding) as f:
            text = f.read()

        html = publish_parts(text,
                             writer=Writer(),
                             settings_overrides=self.settings).get('fragment')
        self.logger and self.logger.debug(
            'Parsed body data of: "{}"'.format(self.input_data))

        return html

    def metadata(self):
        """Fetch metadata in a reStructuredText file.

        The metadata syntax is case insensitive and as follows::

            :author: Author A. Author
            :title: This is a title
            :subtitle: A longer, much longer title for this entry
            :cdate: 2020-03-10
            :mdate: 2020-03-19
            :tags: this, are, a, lot, of, tags
            :comments: yes

        .. note::
            This is a "cheap" approximation that I dislike.  The
            non-bibliographical fields are gotten in a different way
            than the bibliographical ones.  In docutils, the
            bibliographical fields are::

                address, author, authors, contact, copyright, date,
                field, organization, revision, status, version

            and are processed by docutils in a different way that user
            defined fields.  In order to make a dictionary that does not
            treat any field differently, we check if a field is
            biliographic (length=1), or not, (length=2).  If not, we
            store normally, if it is, we take the short representation
            and strip it from its brackets and ellipsis.

        .. todo::
            Fix how to get the fields in a much more cleaner way.
        """
        meta = dict()
        with open(self.input_data, "r", encoding=self.encoding) as f:
            text = f.read()

        doctree = publish_doctree(text,  # writer=Writer(),
                                  settings_overrides=self.settings)

        # Generate dictionary of meta information {'field': 'value'}
        for info in doctree.traverse(docinfo):
            for field in info.children:
                if len(field.children) == 1:
                    k, v = field.shortrepr().strip('<.>'), field
                    meta[str(k).lower()] = v.astext()
                elif len(field.children) == 2:
                    k, v = field.children
                    meta[k.astext().lower()] = v.astext()

        # Lowercase all the keys in the docinfo, and use the same
        # structure `{k: [v]}` as the Markdown metadata retriever
        meta = {k.lower(): [v] for k, v in meta.items()}

        # In reStructuredText, if there's no title in the meta tags, get
        # it from the document part 'title'
        if 'title' not in meta:
            meta['title'] = parts.get('title')

        self.logger and self.logger.debug(
            'Parsed body data of: "{}"'.format(self.input_data))

        return meta
