## This file is part of Indico.
## Copyright (C) 2002 - 2014 European Organization for Nuclear Research (CERN).
##
## Indico is free software; you can redistribute it and/or
## modify it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 3 of the
## License, or (at your option) any later version.
##
## Indico is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Indico;if not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import

import os
from contextlib import contextmanager

from flask import Flask, Blueprint
from flask.blueprints import BlueprintSetupState
from flask.templating import DispatchingJinjaLoader
from flask.wrappers import Request
from jinja2 import PrefixLoader, ChoiceLoader, TemplateNotFound, FileSystemLoader
from werkzeug.utils import cached_property

from MaKaC.common import HelperMaKaCInfo
from indico.web.flask.session import IndicoSessionInterface
from indico.web.flask.util import make_view_func
from indico.core.db import DBMgr


class IndicoRequest(Request):
    @cached_property
    def remote_addr(self):
        """The remote address of the client."""
        with DBMgr.getInstance().global_connection():
            if HelperMaKaCInfo.getMaKaCInfoInstance().useProxy():
                if self.access_route:
                    return self.access_route[0]
        return super(IndicoRequest, self).remote_addr

    @cached_property
    def id(self):
        return '{0:012x}'.format(id(self))

    def __repr__(self):
        rv = super(IndicoRequest, self).__repr__()
        if isinstance(rv, unicode):
            rv = rv.encode('utf-8')
        return rv


class PluginPrefixLoader(PrefixLoader):
    """Prefix loader that uses plugin names"""
    def __init__(self):
        super(PluginPrefixLoader, self).__init__(None, ':')

    def get_loader(self, template):
        from indico.core.plugins import plugin_engine
        try:
            plugin_name, name = template.split(self.delimiter, 1)
        except ValueError:
            raise TemplateNotFound(template)
        plugin = plugin_engine.get_plugin(plugin_name)
        if plugin is None:
            raise TemplateNotFound(template)
        loader = FileSystemLoader(os.path.join(plugin.root_path, 'templates'))
        return loader, name

    def list_templates(self):
        raise TypeError('this loader cannot iterate over all templates')


class IndicoFlask(Flask):
    request_class = IndicoRequest
    session_interface = IndicoSessionInterface()

    def create_global_jinja_loader(self):
        return ChoiceLoader([PluginPrefixLoader(), DispatchingJinjaLoader(self)])


class IndicoBlueprintSetupState(BlueprintSetupState):
    @contextmanager
    def _unprefixed(self):
        prefix = self.url_prefix
        self.url_prefix = None
        yield
        self.url_prefix = prefix

    def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
        if rule.startswith('!/'):
            with self._unprefixed():
                super(IndicoBlueprintSetupState, self).add_url_rule(rule[1:], endpoint, view_func, **options)
        else:
            super(IndicoBlueprintSetupState, self).add_url_rule(rule, endpoint, view_func, **options)


class IndicoBlueprint(Blueprint):
    """A Blueprint implementation that allows prefixing URLs with `!` to
    ignore the url_prefix of the blueprint.

    It also supports automatically creating rules in two versions - with and
    without a prefix."""

    def __init__(self, *args, **kwargs):
        self.__prefix = None
        self.__default_prefix = ''
        super(IndicoBlueprint, self).__init__(*args, **kwargs)

    def make_setup_state(self, app, options, first_registration=False):
        return IndicoBlueprintSetupState(self, app, options, first_registration)

    def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
        if view_func is not None:
            # We might have a RH class here - convert it to a callable suitable as a view func.
            view_func = make_view_func(view_func)
        super(IndicoBlueprint, self).add_url_rule(self.__default_prefix + rule, endpoint, view_func, **options)
        if self.__prefix:
            super(IndicoBlueprint, self).add_url_rule(self.__prefix + rule, endpoint, view_func, **options)

    @contextmanager
    def add_prefixed_rules(self, prefix, default_prefix=''):
        """Creates prefixed rules in addition to the normal ones.
        When specifying a default_prefix, too, the normally "unprefixed" rules
        are prefixed with it."""
        assert self.__prefix is None and not self.__default_prefix
        self.__prefix = prefix
        self.__default_prefix = default_prefix
        yield
        self.__prefix = None
        self.__default_prefix = ''

@@ -779,18 +779,8 @@
 rt%0A%0A
-import os%0A
 from
@@ -913,165 +913,79 @@
 ask.
-templating import DispatchingJinjaLoader%0Afrom flask.wrappers import Request%0Afrom jinja2 import PrefixLoader, ChoiceLoader, TemplateNotFound, FileSystemLoader
+wrappers import Request%0Afrom flask_pluginengine import PluginFlaskMixin
 %0Afro
@@ -1857,987 +1857,127 @@
 ass 
-PluginPrefixLoader(PrefixLoader):%0A    %22%22%22Prefix loader that uses plugin names%22%22%22%0A    def __init__(self):%0A        super(PluginPrefixLoader, self).__init__(None, ':')%0A%0A    def get_loader(self, template):%0A        from indico.core.plugins import plugin_engine%0A        try:%0A            plugin_name, name = template.split(self.delimiter, 1)%0A        except ValueError:%0A            raise TemplateNotFound(template)%0A        plugin = plugin_engine.get_plugin(plugin_name)%0A        if plugin is None:%0A            raise TemplateNotFound(template)%0A        loader = FileSystemLoader(os.path.join(plugin.root_path, 'templates'))%0A        return loader, name%0A%0A    def list_templates(self):%0A        raise TypeError('this loader cannot iterate over all templates')%0A%0A%0Aclass IndicoFlask(Flask):%0A    request_class = IndicoRequest%0A    session_interface = IndicoSessionInterface()%0A%0A    def create_global_jinja_loader(self):%0A        return ChoiceLoader(%5BPluginPrefixLoader(), DispatchingJinjaLoader(self)%5D
+IndicoFlask(PluginFlaskMixin, Flask):%0A    request_class = IndicoRequest%0A    session_interface = IndicoSessionInterface(
 )%0A%0A%0A

Move remaining jinja loader stuff to plugin engine