from urllib import quote_plus

from genshi import XML
from pylons import (app_globals, config, session, tmpl_context, request,
                    response, templating)
from repoze.what import predicates

import tg
from tg.configuration import Bunch


class MissingRendererError(Exception):
    def __init__(self, template_engine):
        super(MissingRendererError, self).__init__(
            ("The renderer for '%(template_engine)s' templates is missing. "
            "Try adding the following line in you app_cfg.py:\n"
            "\"base_config.renderers.append('%(template_engine)s')\"") % dict(
            template_engine=template_engine))
        self.template_engine = template_engine

class DeprecatedFlashVariable(object):
    def __init__(self, callable, msg):
        self.callable = callable
        self.msg = msg

    def __unicode__(self):
        import warnings
        warnings.warn(self.msg, DeprecationWarning, 2)
        return unicode(self.callable())

    def __nonzero__(self):
        import warnings
        warnings.warn(self.msg, DeprecationWarning, 2)
        return bool(self.callable())

def _get_tg_vars():
    """Create a Bunch of variables that should be available in all templates.

    These variables are:

    WARNING: This function should not be called from outside of the render()
    code.  Please consider this function as private.

    quote_plus
        the urllib quote_plus function
    url
        the turbogears.url function for creating flexible URLs
    identity
        the current visitor's identity information
    session
        the current beaker.session if the session_filter.on it set
        in the app.cfg configuration file. If it is not set then session
        will be None.
    locale
        the default locale
    inputs
        input values from a form
    errors
        validation errors
    request
        the WebOb Request Object
    config
        the app's config object
    auth_stack_enabled
        A boolean that determines if the auth stack is present in the environment
    predicates
        The :mod:`repoze.what.predicates` module.

    """
    # TODO: Implement user_agent and other missing features.
    tg_vars = Bunch(
        config = tg.config,
        flash_obj = tg.flash,
        flash = DeprecatedFlashVariable(
            lambda: tg.flash.message,
            "flash is deprecated, please use flash_obj.message instead "
            "or use the new flash_obj.render() method"
            ),
        flash_status = DeprecatedFlashVariable(
            lambda: 'status_' + tg.flash.status,
            "flash_status is deprecated, please use flash_obj.status instead "
            "or use the new flash_obj.render() method"
            ),
        quote_plus = quote_plus,
        url = tg.url,
        # this will be None if no identity
        identity = request.environ.get('repoze.who.identity'),
        session = session,
        locale = tg.request.accept_language.best_matches(),
        errors = getattr(tmpl_context, "form_errors", {}),
        inputs = getattr(tmpl_context, "form_values", {}),
        request = tg.request,
        auth_stack_enabled = 'repoze.who.plugins' in tg.request.environ,
        predicates = predicates,
        )

    # TODO: we should actually just get helpers from the package's helpers
    # module and dump the use of the SOP.
    helpers = config.get('pylons.h') or config.get('pylons.helpers')

    root_vars = Bunch(
        c = tmpl_context,
        tmpl_context = tmpl_context,
        response = response,
        request = request,
        url = tg.url,
        helpers = helpers,
        tg = tg_vars
        )
    #Allow users to provide a callable that defines extra vars to be
    #added to the template namespace
    variable_provider = config.get('variable_provider', None)
    if variable_provider:
        root_vars.update(variable_provider())
    return root_vars

def render(template_vars, template_engine=None, template_name=None, **kwargs):

    if template_engine is not None:
        # the engine was defined in the @expose()
        render_function = config['render_functions'].get(template_engine)

        if render_function is None:
            # engine was forced in @expose() but is not present in the
            # engine list, warn developper
            raise MissingRendererError(template_engine)

    if not template_vars:
        template_vars={}

    #Get the extra vars, and merge in the vars from the controller
    tg_vars = _get_tg_vars()
    tg_vars.update(template_vars)
    template_vars = tg_vars

    if not render_function:
        # getting the default renderer. (this is only if no engine was defined
        # in the @expose()
        render_function = config['render_functions'][config['default_renderer']]

    return render_function(template_name, template_vars, **kwargs)

def render_chameleon_genshi(template_name, template_vars, **kwargs):
    """Render the template_vars with the chameleon.genshi template"""
    template_vars['XML'] = XML

    if config.get('use_dotted_templatenames', False):
        template_name = tg.config['pylons.app_globals'
                ].dotted_filename_finder.get_dotted_filename(
                        template_name,
                        template_extension='.html')

    # here we use the render genshi function because it should be api compliant
    return templating.render_genshi(template_name, extra_vars=template_vars,
                                    **kwargs)

def render_genshi(template_name, template_vars, **kwargs):
    """Render the template_vars with the Genshi template"""
    template_vars['XML'] = XML

    if config.get('use_dotted_templatenames', False):
        template_name = tg.config['pylons.app_globals'
                ].dotted_filename_finder.get_dotted_filename(
                        template_name,
                        template_extension='.html')

    return templating.render_genshi(template_name, extra_vars=template_vars,
                                    **kwargs)


def render_mako(template_name, template_vars, **kwargs):
    if config.get('use_dotted_templatenames', False):
        template_name = tg.config['pylons.app_globals'
                ].dotted_filename_finder.get_dotted_filename(
                        template_name,
                        template_extension='.mak')

    return templating.render_mako(template_name, extra_vars=template_vars,
                                  **kwargs)

def render_jinja(template_name, template_vars, **kwargs):
    return templating.render_jinja2(template_name, extra_vars=template_vars,
                                   **kwargs)

