# -*- coding: utf-8 -*-

"""
    sphinx-jsonschema
    -----------------

    This package adds the *jsonschema* directive to Sphinx.

    Using this directory you can render JSON Schema directly
    in Sphinx.

    :copyright: Copyright 2017-2020, Leo Noordergraaf
    :licence: GPL v3, see LICENCE for details.
"""

import os.path
import json
from jsonpointer import resolve_pointer
import yaml
from collections import OrderedDict

from docutils.parsers.rst import Directive
from .wide_format import WideFormat

# TODO find out if app is accessible in some other way
_glob_app = None

class JsonSchema(Directive):
    optional_arguments = 1
    has_content = True

    def __init__(self, directive, arguments, options, content, lineno, content_offset, block_text, state, state_machine):
        assert directive == 'jsonschema'

        #breakpoint()

        self.options = options
        self.state = state
        self.lineno = lineno
        self.statemachine = state_machine

        if len(arguments) == 1:
            filename, pointer = self._splitpointer(arguments[0])
            if filename != '':
                self._load_external(filename)
            else:
                self._load_internal(content)
            if pointer:
                self.schema = resolve_pointer(self.schema, pointer)
        else:
            self._load_internal(content)

    def run(self):
        format = WideFormat(self.state, self.lineno, _glob_app)
        return format.transform(self.schema)

    def _load_external(self, file_or_url):
        if file_or_url.startswith('http'):
            try:
                import requests
            except ImportError:
                self.error("JSONSCHEMA loading from http requires requests. Try 'pip install requests'")
            text = requests.get(file_or_url)
            self.schema = self.ordered_load(text.content)
        else:
            if not os.path.isabs(file_or_url):
                # file relative to the path of the current rst file
                dname = os.path.dirname(self.statemachine.input_lines.source(0))
                file_or_url = os.path.join(dname, file_or_url)
            with open(file_or_url) as file:
                text = file.read()
            self.schema = self.ordered_load(text, yaml.SafeLoader)

    def _load_internal(self, text):
        if text is None or len(text) == 0:
            self.error("JSONSCHEMA requires either filename, http url or inline content")
        self.schema = self.ordered_load('\n'.join(text), yaml.SafeLoader)

    def _splitpointer(self, path):
        val = path.split('#', 1)
        if len(val) == 1:
            val.append(None)
        return val


    def ordered_load(self, text, Loader=yaml.Loader, object_pairs_hook=OrderedDict):
        """Allows you to use `pyyaml` to load as OrderedDict.

        Taken from https://stackoverflow.com/a/21912744/1927102
        """
        class OrderedLoader(Loader):
            pass

        def construct_mapping(loader, node):
            loader.flatten_mapping(node)
            return object_pairs_hook(loader.construct_pairs(node))
        OrderedLoader.add_constructor(
            yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
            construct_mapping)
        try:
            text = text.replace('\\(', '\\\\(')
            text = text.replace('\\)', '\\\\)')
            try:
                result = yaml.load(text, OrderedLoader)
            except yaml.scanner.ScannerError:
                # will it load as plain json?
                result = json.loads(text, object_pairs_hook=object_pairs_hook)
        except Exception as e:
            print("exception: ",e)
            self.error(e)
            result = {}
        return result


def setup(app):
    global _glob_app
    _glob_app = app
    app.add_directive('jsonschema', JsonSchema)
    return {'version': '1.15'}