import json, os
from docutils import nodes
from sphinx.builders.dirhtml import DirectoryHTMLBuilder
from sphinx.environment.adapters.toctree import TocTree


class FragmentsBuilder(DirectoryHTMLBuilder):
    name = 'fragments'

    def __init__(self, app):
        super().__init__(app)
        self.config.html_theme = 'bootstrap-fragments'
        self.config.html_theme_path.append(os.path.abspath(os.path.join(__file__, os.pardir, 'theme')))
        self.config.html_sidebars = {
            '**': ['globaltoc.html'],
        }
        self.docsmap_file = os.path.join(self.outdir, 'docsmap.json')
        if os.path.exists(self.docsmap_file):
            with open(self.docsmap_file, 'r') as fh:
                self.docs_map = json.load(fh, encoding='utf-8')
        else:
            self.docs_map = {}

    def prepare_writing(self, docnames):
        super().prepare_writing(docnames)
        for docname in docnames:
            doctree = self.env.get_doctree(docname)
            idx = doctree.first_child_matching_class(nodes.section)
            if idx is None or idx == -1:
                continue

            first_section = doctree[idx]
            idx = first_section.first_child_matching_class(nodes.title)
            if idx is None or idx == -1:
                continue

            doctitle = first_section[idx].astext()
            if doctitle:
                self.docs_map[docname] = doctitle
            self.globalcontext['toctree'] = lambda **kw: self._get_local_toctree(docname, **kw)

    def _get_local_toctree(self, docname, collapse=True, **kwds):
        # type: (str, bool, Any) -> str
        if 'includehidden' not in kwds:
            kwds['includehidden'] = False
        partials = TocTree(self.env).get_toctree_for(docname, self, collapse, **kwds)
        return self.render_partial(partials)['fragment']

    def finish(self):
        super().finish()
        with open(self.docsmap_file, 'w') as fh:
            json.dump(self.docs_map, fh)


def setup(app):
    app.require_sphinx('1.0')
    app.add_builder(FragmentsBuilder)
