# -*- coding: utf-8 -*-
"""
    sphinx.ext.autosummary.generate
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Usable as a library or script to generate automatic RST source files for
    items referred to in autosummary:: directives.

    Each generated RST file contains a single auto*:: directive which
    extracts the docstring of the referred item.

    Example Makefile rule::

       generate:
               sphinx-autogen -o source/generated source/*.rst

    :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
    :license: BSD, see LICENSE for details.
"""

import os
import re
import sys
import pydoc
import optparse
import inspect

from jinja2 import FileSystemLoader, TemplateNotFound
from jinja2.sandbox import SandboxedEnvironment

from sphinx import package_dir
from ..autosummary import import_by_name, get_documenter
from sphinx.jinja2glue import BuiltinTemplateLoader
from sphinx.util.osutil import ensuredir
from sphinx.util.inspect import safe_getattr
from sphinx.pycode import ModuleAnalyzer


def main(argv=sys.argv):
    usage = """%prog [OPTIONS] SOURCEFILE ..."""
    p = optparse.OptionParser(usage.strip())
    p.add_option("-o", "--output-dir", action="store", type="string",
                 dest="output_di
r", default=None,
                 help="Directory to place all output in")
    p.add_option("-s", "-
-suffix", action="store", type="string",
                 dest="suffix", default="rst",
                 help="Default suffix for files (default: %default)")
    p.add_option("-t", "--templates", action="store", type="string",
                 dest="templates", default=None,
                 help="Custom template directory (default: %default)")
    options, args = p.parse_args(argv[1:])

    if len(args) < 1:
        p.error('no input files given')

    generate_autosummary_docs(args, options.output_dir,
                              "." + options.suffix,
                              template_dir=options.templates)


def _simple_info(msg):
    print(msg)


def _simple_warn(msg):
    print('WARNING: ' + msg,
          file=sys.stderr)  # -- Generating output ---------------------------------------------------------

    def generate_autosummary_docs(sources, output_dir=None, suffix='.rst',
                                  warn=_simple_warn, info=_simple_info,
                                  base_path=None, builder=None,
                                  template_dir=None):

        showed_sources = list(sorted(sources))
        if len(showed_sources) > 20:
            showed_sources = showed_sources[:10] + ['...'] + showed_sources[
                -10:]
        info('[autosummary] generating autosummary for: %s' %
             ', '.join(showed_sources))

        if output_dir:
            info('[autosummary] writing to %s' % output_dir)

        if base_path is not None:
            sources = [os.path.join(base_path, filename) for filename in
                       sources]

        # create our own templating environment
        template_dirs = [os.path.join(package_dir, 'ext',
                                      'autosummary', 'templates')]
        if builder is not None:
            # allow the user to override the templates
            template_loader = BuiltinTemplateLoader()
            template_loader.init(builder, dirs=template_dirs)
        else:
            if template_dir:
                template_dirs.insert(0, template_dir)
            template_loader = FileSystemLoader(template_dirs)
        template_env = SandboxedEnvironment(loader=template_loader)

        # read
        items = find_autosummary_in_files(sources)

        # remove possible duplicates
        items = list(dict([(item, True) for item in items]).keys())

        # keep track of new files
        new_files = list()

        # write
        for name, path, template_name in sorted(items):
            if path is None:
                # The corresponding autosummary:: directive did not have
                # a :toctree: option
                continue

            path = output_dir or os.path.abspath(path)
            ensuredir(path)

            try:
                name, obj, parent = import_by_name(name)
            except ImportError as e:
                warn('[autosummary] failed to import %r: %s' % (name, e))
                continue

            # skip base modules
            if name.endswith(".base"):
                continue

            fn = os.path.join(path, name + suffix)

            # skip it if it exists
            if os.path.isfile(fn):
                continue

            new_files.append(fn)

            f = open(fn, 'w')

            try:
                doc = get_documenter(obj, parent)

                if template_name is not None:
                    template = template_env.get_template(template_name)
                else:
                    try:
                        template = template_env.get_template(
                            'autosummary/%s.rst'
                            % doc.objtype)
                    except TemplateNotFound:
                        template = template_env.get_template(
                            'autosummary/base.rst')

                def exclude_member(obj, name):
                    if sys.skip_member(name, obj):
                        return True

                    live = getattr(obj, name)

                    if inspect.isbuiltin(live):
                        return True

                    real_module = inspect.getmodule(live)
                    if real_module is not None:
                        if real_module.__name__ in ["ctypes",
                                                    "unittest"]:
                            return True

                    c = getattr(obj, name)
                    if inspect.isclass(c) or inspect.isfunction(c):
                        if (c.__module__ != obj.__name__ + ".base" and
                                c.__module__ != obj.__name__):
                            return True
                    return False

                def get_members(obj, typ, include_public=list()):
                    items = list()
                    for name in dir(obj):
                        # skip_member
                        if exclude_member(obj, name):
                            continue
                        try:
                            documenter = get_documenter(safe_getattr(obj, name),
                                                        obj)
                        except AttributeError:
                            continue
                        if documenter.objtype == typ:
                            items.append(name)
                        elif typ == 'function' and documenter.objtype == 'boundmethod':
                            items.append(name)
                    public = [x for x in items
                              if x in include_public or not x.startswith('_')]
                    return public, items

                def def_members(obj, typ, include_public=list()):
                    items = list()
                    try:
                        obj_dict = safe_getattr(obj, '__dict__')
                    except AttributeError:
                        return list()
                    defined = list(obj_dict.keys())
                    defined.sort()
                    for name in defined:
                        if exclude_member(obj, name):
                            continue
                        try:
                            documenter = get_documenter(safe_getattr(obj, name),
                                                        obj)
                        except AttributeError:
                            continue
                        if documenter.objtype == typ:
                            items.append(name)
                    public = [x for x in items
                              if x in include_public or not x.startswith('_')]
                    return public

                def get_iattributes(obj):
                    items = list()
                    name = obj.__name__
                    obj_attr = dir(obj)
                    analyzer = ModuleAnalyz