"""
This module allows the tonnikala templating language --
http://pypi.python.org/pypi/tonnikala/

-- to be used in the Pyramid web framework --
http://docs.pylonshq.com/
"""

import os

try:
    import pkg_resources

    def resource_filepath(module, path):
        return pkg_resources.resource_filename(module, path)
except ImportError:
    import importlib.resources
    import pathlib

    _PATHLIB_CLASS = pathlib.Path().__class__

    def resource_filepath(module, path):
        path = importlib.resources.files(module).joinpath(path)
        if not isinstance(path, _PATHLIB_CLASS):
            raise FileNotFoundError(
                f"Tonnikala is unable to resolve {module}:{path} as a file, might be zipped?"
            )

        return str(path)


from ..helpers import is_nonstr_iter
from pyramid.settings import asbool, aslist

import tonnikala.loader


class PyramidTonnikalaLoader(tonnikala.loader.FileLoader):
    def __init__(self):
        super(PyramidTonnikalaLoader, self).__init__()
        self.search_paths = []

    def add_search_path(self, module, directory):
        """
        Add a search path for the loader. Module if not None,
        is a package, relative to which we are locating the
        module.
        """

        self.search_paths.append((module, directory))

    def resolve(self, name):
        """
        Resolve the name using the given search paths.
        """

        if ":" in name:
            try:
                module, path = name.split(":", 1)
                name = resource_filepath(module, path)
                if name and os.path.exists(name):
                    return name

            except Exception:
                pass

        for module, directory in self.search_paths:
            path = os.path.join(directory, name)
            if module:
                path = resource_filepath(module, path)

            if os.path.exists(path):
                return path

        return super(PyramidTonnikalaLoader, self).resolve(name)


class TonnikalaTemplateRenderer(object):
    def __init__(self, info, loader, debug=True):
        self.info = info
        self.loader = loader
        self.debug = debug

    def implementation(self):
        return self

    def __call__(self, value, system, fragment=False):
        """``value`` is the result of the view.
        Returns a result (a string or unicode object useful as a
        response body). Values computed by the system are passed in the
        ``system`` parameter, which is a dictionary containing:

        * ``view`` (the view callable that returned the value),
        * ``renderer_name`` (the template name or simple name of the renderer),
        * ``context`` (the context object passed to the view), and
        * ``request`` (the request object passed to the view).
        """

        name = system["renderer_name"]
        compiled = self.loader.load(name)

        try:
            system.update(value)
        except (TypeError, ValueError):
            raise ValueError(
                "TonnikalaTemplateRenderer was passed a " "non-dictionary as value."
            )

        rendered = compiled.render(system)
        if not fragment:
            rendered = str(rendered)

        return rendered

    def fragment(self, tmpl, value, system):
        system["renderer_name"] = tmpl
        return self(value, system, fragment=True)


class TonnikalaRendererFactory(object):
    def __init__(self):
        self.debug = False
        self.loader = PyramidTonnikalaLoader()

    def set_l10n(self, flag):
        self.loader.translatable = flag

    def set_reload(self, flag):
        self.loader.set_reload(flag)

    def add_search_path(self, module, path):
        self.loader.add_search_path(module, path)

    def __call__(self, info):
        return TonnikalaTemplateRenderer(info, self.loader, debug=self.debug)


def add_tonnikala_extensions(config, *extensions):
    for extension in extensions:
        if not extension.startswith("."):
            extension = "." + extension

        config.add_renderer(extension, config.registry.tonnikala_renderer_factory)


def add_tonnikala_search_paths(config, *paths):
    for path in paths:
        module_name, dummy, base_dir = path.partition(":")
        if not base_dir:
            base_dir = module_name
            module_name = None

        config.registry.tonnikala_renderer_factory.add_search_path(
            module_name, base_dir
        )


def set_tonnikala_reload(config, flag):
    """
    Sets the reload flag for tonnikala template renderer.
    If True, the templates are reloaded if changed
    """

    config.registry.tonnikala_renderer_factory.set_reload(flag)


def set_tonnikala_l10n(config, flag):
    """
    Set the l10n/translation enabled flag for tonnikala template renderer.
    If True, the templates are translated using the gettext facility
    """

    config.registry.tonnikala_renderer_factory.set_l10n(flag)


def includeme(config):
    if hasattr(config.registry, "tonnikala_renderer_factory"):
        return

    config.registry.tonnikala_renderer_factory = TonnikalaRendererFactory()

    config.add_directive("add_tonnikala_extensions", add_tonnikala_extensions)
    config.add_directive("add_tonnikala_search_paths", add_tonnikala_search_paths)
    config.add_directive("set_tonnikala_reload", set_tonnikala_reload)
    config.add_directive("set_tonnikala_l10n", set_tonnikala_l10n)

    settings = config.registry.settings

    if "tonnikala.extensions" in settings:
        extensions = settings["tonnikala.extensions"]
        if not is_nonstr_iter(extensions):
            extensions = aslist(extensions, flatten=True)

        config.add_tonnikala_extensions(*extensions)

    if "tonnikala.search_paths" in settings:
        paths = settings["tonnikala.search_paths"]
        if not is_nonstr_iter(paths):
            paths = aslist(paths, flatten=True)

        config.add_tonnikala_search_paths(*paths)

    tk_reload = settings.get("tonnikala.reload")
    if tk_reload is None:
        tk_reload = settings.get("pyramid.reload_templates")

    config.set_tonnikala_reload(asbool(tk_reload))

    l10n = asbool(settings.get("tonnikala.l10n"))
    config.set_tonnikala_l10n(l10n)
