import json
import subprocess
import os

from sphinx.util.console import bold
import sphinx.util.logging

from .base import PythonMapperBase, SphinxMapperBase

LOGGER = sphinx.util.logging.getLogger(__name__)


class JavaScriptSphinxMapper(SphinxMapperBase):

    """Auto API domain handler for Javascript

    Parses directly from Javascript files.

    :param app: Sphinx application passed in as part of the extension
    """

    def read_file(self, path, **kwargs):
        """Read file input into memory, returning deserialized objects

        :param path: Path of file to read
        """
        # TODO support JSON here
        # TODO sphinx way of reporting errors in logs?
        subcmd = "jsdoc"
        if os.name == "nt":
            subcmd = ".".join([subcmd, "cmd"])

        try:
            parsed_data = json.loads(subprocess.check_output([subcmd, "-X", path]))
            return parsed_data
        except IOError:
            LOGGER.warning(
                "Error reading file: {0}".format(path),
                type="autoapi",
                subtype="not_readable",
            )
        except TypeError:
            LOGGER.warning(
                "Error reading file: {0}".format(path),
                type="autoapi",
                subtype="not_readable",
            )
        return None

    # Subclassed to iterate over items
    def map(self, options=None):
        """Trigger find of serialized sources and build objects"""
        for _, data in sphinx.util.status_iterator(
            self.paths.items(),
            bold("[AutoAPI] ") + "Mapping Data... ",
            length=len(self.paths),
            stringify_func=(lambda x: x[0]),
        ):
            for item in data:
                for obj in self.create_class(item, options):
                    obj.jinja_env = self.jinja_env
                    self.add_object(obj)

    def create_class(self, data, options=None, **kwargs):
        """Return instance of class based on Javascript data

        Data keys handled here:

            type
                Set the object class

            consts, types, vars, funcs
                Recurse into :py:meth:`create_class` to create child object
                instances

        :param data: dictionary data from godocjson output
        """
        obj_map = dict((cls.type, cls) for cls in ALL_CLASSES)
        try:
            cls = obj_map[data["kind"]]
        except (KeyError, TypeError):
            # this warning intentionally has no (sub-)type
            LOGGER.warning("Unknown type: %s" % data)
        else:
            # Recurse for children
            obj = cls(data, jinja_env=self.jinja_env, app=self.app)
            if "children" in data:
                for child_data in data["children"]:
                    for child_obj in self.create_class(child_data, options=options):
                        obj.children.append(child_obj)
            yield obj


class JavaScriptPythonMapper(PythonMapperBase):

    language = "javascript"

    def __init__(self, obj, **kwargs):
        """
        Map JSON data into Python object.

        This is the standard object that will be rendered into the templates,
        so we try and keep standard naming to keep templates more re-usable.
        """

        super(JavaScriptPythonMapper, self).__init__(obj, **kwargs)
        self.name = obj.get("name")
        self.id = self.name

        # Second level
        self.docstring = obj.get("description", "")
        # self.docstring = obj.get('comment', '')

        self.imports = obj.get("imports", [])
        self.children = []
        self.parameters = map(
            lambda n: {"name": n["name"], "type": n["type"][0]}, obj.get("param", [])
        )


class JavaScriptClass(JavaScriptPythonMapper):
    type = "class"
    ref_directive = "class"
    top_level_object = True


class JavaScriptFunction(JavaScriptPythonMapper):
    type = "function"
    ref_type = "func"


class JavaScriptData(JavaScriptPythonMapper):
    type = "data"
    ref_directive = "data"


class JavaScriptMember(JavaScriptPythonMapper):
    type = "member"
    ref_directive = "member"


class JavaScriptAttribute(JavaScriptPythonMapper):
    type = "attribute"
    ref_directive = "attr"


ALL_CLASSES = [
    JavaScriptFunction,
    JavaScriptClass,
    JavaScriptData,
    JavaScriptAttribute,
    JavaScriptMember,
]
