# encoding: utf-8
# Created by Jeremy Bowman at Thu Feb  6 17:41:45 2014
# Copyright (c) 2014 Safari Books Online, LLC. All rights reserved.

"""
Sphinx extension that uses jsdoc-toolkit and jsdoc-toolkit-rst-template to
generate JavaScript API documentation.  Depends on having JSDoc formatted
comments in the source code, without that this won't do much.  There are three
relevant Sphinx configuration variables:

* ``jsdoc_source_root`` - The path relative to conf.py of the directory
  containing all of the JavaScript files to be documented (``".."`` by default)

* ``jsdoc_output_root`` - The path relative to conf.py of the directory in
  which to put the generated reST files (``"javascript"`` by default).

* ``jsdoc_exclude`` - A list of regular expressions; files and directories
  matching any of them will be omitted from the documentation (an empty list
  by default).  The regular expressions should be strings using Java's regex
  syntax.

The generated files are left in place between builds so they can be inspected.
The output directory should typically be added to .gitignore so the
intermediate files aren't accidentally committed.

External requirements: java, ant
"""

import os
from shutil import rmtree
from subprocess import Popen

from sphinx.errors import SphinxError

SOURCE_PATH = os.path.abspath(os.path.dirname(__file__))


class JSDocError(SphinxError):
    category = 'jsdoc'


def generate_docs(app):
    """ Generate the reST documentation files for the JavaScript code """
    # Figure out the correct directories to use
    config = app.config
    config_dir = app.env.srcdir
    javascript_root = os.path.join(config_dir, config.jsdoc_source_root)
    if javascript_root[-1] != os.path.sep:
        javascript_root += os.path.sep
    if not javascript_root:
        return
    output_root = os.path.join(config_dir, config.jsdoc_output_root)
    execution_dir = os.path.join(config_dir, '..')
    exclude = config.jsdoc_exclude

    # Remove any files generated by earlier builds
    cleanup(output_root)

    # Generate the actual reST files
    jsdoc_toolkit_dir = os.path.join(SOURCE_PATH, 'jsdoc-toolkit')
    jsdoc_rst_dir = os.path.join(SOURCE_PATH, 'jsdoc-toolkit-rst-template')
    build_xml_path = os.path.join(jsdoc_rst_dir, 'build.xml')
    command = ['ant', '-f', build_xml_path,
               '-Djsdoc-toolkit.dir=%s' % jsdoc_toolkit_dir,
               '-Djs.src.dir=%s' % javascript_root,
               '-Djs.rst.dir=%s' % output_root]
    if exclude:
        exclude_args = ['--exclude=\\"%s\\"' % path for path in exclude]
        command.append('-Djs.exclude="%s"' % ' '.join(exclude_args))
    try:
        process = Popen(command, cwd=execution_dir)
        process.wait()
    except OSError:
        raise JSDocError('Error running ant; is it installed?')

    # Convert the absolute paths in the file listing to relative ones
    path = os.path.join(output_root, 'files.rst')
    with open(path, 'r') as f:
        content = f.read()
    content = content.replace(javascript_root, '')
    with open(path, 'w') as f:
        f.write(content)


def cleanup(output_root):
    """Remove any reST files which were generated by this extension"""
    if os.path.exists(output_root):
        if os.path.isdir(output_root):
            rmtree(output_root)
        else:
            os.remove(output_root)


def setup(app):
    """Sphinx extension entry point"""
    app.add_config_value('jsdoc_source_root', '..', 'env')
    app.add_config_value('jsdoc_output_root', 'javascript', 'env')
    app.add_config_value('jsdoc_exclude', [], 'env')
    app.connect('builder-inited', generate_docs)
