# -*- coding: utf-8 -*-
"""
jinja_kit.core
~~~~~~~~~~~~~~

Core jinja_kit wrapper class

:copyright: (c) 2009 by the Jinja Team.
:copyright: (c) 2012 - 2013 by Alexandr Lispython (alex@obout.ru).
:license: BSD, see LICENSE for more details.
:github: http://github.com/Lispython/jinja-kit
"""

from itertools import chain

try:
    from cProfile import Profile
except ImportError:
    from profile import Profile

from jinja2 import Environment, FileSystemLoader
from jinja2.utils import import_string

from jinja_kit.exceptions import TemplateNotFound


class Kit(object):
    """Magic wrapper for jinja environment
    """
    def __init__(self, search_path, loader=FileSystemLoader,
                 auto_reload=False, cache_size=50, extensions=(),
                 filters={}, globals={}):
        """Construct wrapper
        """
        self.search_path = search_path
        self.env = Environment(loader=loader(self.search_path),
                               auto_reload=auto_reload,
                               cache_size=cache_size,
                               extensions=extensions)
        self.filters = filters
        self.globals = globals

    def filter(self, name=None):
        def wrapper(func):
            return self.add_filter(func, name)
        return wrapper

    def tglobal(self, name=None):
        def wrapper(func):
            return self.add_global(func, name)
        return wrapper

    def add_filter(self, func, name=None):
        """Add filter to environment
        :param func:
        :param name:
        """
        self.env.filters.update({name or func.func_name: func})
        return func

    def add_global(self, func, name=None):
        """Add global function to context

        :param func:
        :param name:
        """
        self.env.globals.update({name or func.func_name: func})
        return func

    def load_filters(self, paths=None):
        """Load filter to environment
        """
        result = {}
        paths = paths or self.filters
        for m in paths:
            f = __import__(m, fromlist=['filters'])
            result.update(f.filters)
        return result

    def load_globals(self, paths=None, key='globals'):
        """Load global function to environment
        """
        result = {}
        paths = paths or self.globals
        for m in paths:
            f = __import__(m, fromlist=[key])
            result.update(f.globals)
            for name, func in f.globals.iteritems():
                self.add_global(func, name)
        return result

    def load_extensions(self, extensions):
        """Load extensions
        """
        for extension in extensions:
            if isinstance(extension, basestring):
                extension = import_string(extension)
            self.add_extension(extension)

    def add_extension(self, ext):
        """Load extension to environment

        :param ext: extension object
        """
        self.env.add_extension(ext)

    def get_template(self, template_name, global_functions={}):
        """Get template
        """
        return self.env.get_template(template_name, globals=global_functions)

    def select_template(self, templates):
        """Search templates
        """
        if isinstance(templates, (list, tuple)):
            for template in templates:
                try:
                    return self.get_template(template,
                                             global_functions=globals())
                except TemplateNotFound:
                    continue
        elif isinstance(templates, (str, unicode)):
            return self.get_template(templates, globals())

        raise TemplateNotFound

    def render_to_string(self, template_name, context=None,
                         processors=None, processor_arg=None):
        """Render template into string

        :param template_name:
        :param context:
        :param processors:
        :param proccessor_arg:
        """
        context = dict(context or {})
        context['processor_arg'] = processor_arg
        standard_processors = self.get_standard_processors()
        for processor in chain(standard_processors,
                               processors or ()):
            if processor_arg:
                context.update(processor(processor_arg))
        return self.select_template(template_name).render(context)

    def get_standard_processors(self):
        return ()
