from urllib import unquote

import pkg_resources

from webob import Request

import tw
from tw.mods import base
from tw.core import resources
from tw.core.util import asbool

__all__ = ["ToscaWidgetsMiddleware", "make_middleware"]
    
class ToscaWidgetsMiddleware(object):
    """
    This WSGI middleware piece takes care of creating a per-request context for
    ToscaWidgets and injecting resource links into html responses.

    It can also take care of serving those resources if `serve_resources` is
    True (default).
    """
    def __init__(self, application, host_framework, prefix='/toscawidgets',
                 inject_resources=True, serve_resources=True):
        self.host_framework = host_framework
        self.prefix = prefix
        self.serve_resources = serve_resources
        self.inject_resources = inject_resources

        self.application = application

        if self.inject_resources:
            from tw.core.resource_injector import injector_middleware
            self.application = injector_middleware(self.application)

    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)

    def wsgi_app(self, environ, start_response):
        self.host_framework.start_request(environ)
        environ['paste.registry'].register(tw.framework, self.host_framework)
        #XXX Do we really need to stuff these in environ?
        environ['toscawidgets.prefix'] = self.prefix
        environ.setdefault('toscawidgets.framework', self.host_framework)

        req = Request(environ)
        try:
            if self.serve_resources and req.path_info.startswith(self.prefix):
                # Intercept request to possibly serve a static resource
                req.path_info = req.path_info[len(self.prefix):]
                req.script_name += self.prefix
                resources_app = resources.registry
                if req.path_info.startswith(resources_app.prefix):
                    req.path_info = req.path_info[len(resources_app.prefix):]
                    req.script_name += resources_app.prefix
                    resp = req.get_response(resources_app)
                    return resp(environ, start_response)
            else:
                # Pass request downstream  
                resp = req.get_response(self.application)
            return resp(environ, start_response)
        finally:
            self.host_framework.end_request(environ)

#TODO: Wrap to issue deprecation warnings
TGWidgetsMiddleware = ToscaWidgetsMiddleware

def _load_from_entry_point(name):
    for ep in pkg_resources.iter_entry_points('toscawidgets.host_frameworks'):
        if ep.name == name:
            return ep
        
def _extract_args(args, prefix, adapters={}):
    l = len(prefix)
    nop = lambda v: v
    return dict((k[l:], adapters.get(k[l:], nop)(v))
                for k,v in args.iteritems() if k.startswith(prefix))
    
def _load_host_framework(host_framework):
    if ':' not in host_framework:
        ep = _load_from_entry_point(host_framework)
    else:
        ep = pkg_resources.EntryPoint.parse("hf="+host_framework)
    if ep:
        hf = ep.load(False)
    else:
        hf = None
    if not hf:
        raise LookupError("Could not load %s" % host_framework)
    return hf

def make_middleware(app, config=None, **kw):
    """
    Initializes :class:`tw.core.middleware.ToscaWidgetsMiddleware` and a
    :class:`tw.mods.base.HostFramework` and wraps the WSGI application ``app``
    with it.

    Configuration can be passed in a dict as the ``config`` parameter.


    **Available options:**

    toscawidgets.middleware.*
        These parameters will be passed to
        :class:`tw.core.middleware.ToscaWidgetsMiddleware`
        when instatntiating it. See its docstrings for details.

    toscawidgets.framework
        Name of the ``toscawidgets.host_frameworks`` entry point or
        :class:`tw.mods.base.HostFramework` subclass which shall interface with
        the framework. ``wsgi``, ``pylons`` are available. Default is
        ``wsgi``

    toscawidgets.framework.*
        Parameters for the :class:`tw.modes.base.HostFramework`. See their
        respective docstrings for accepted parameters.


    This is the ToscaWidgets#middleware paste.filter_app_factory entrypoint.
    """
    config = (config or {}).copy()
    config.update(kw)
    host_framework = config.pop('toscawidgets.framework', 'wsgi')
    if isinstance(host_framework, basestring):
        host_framework = _load_host_framework(host_framework)
    middleware_args = _extract_args(config, 'toscawidgets.middleware.', {
        'inject_resources': asbool,
        'serve_resources': asbool,
        })
    hf_args = _extract_args(config, 'toscawidgets.framework.', {
        'enable_runtime_checks': asbool,
        })
    app = ToscaWidgetsMiddleware(app, host_framework=host_framework(**hf_args),
                                 **middleware_args)
    if config.get('stack_registry', False):
        from tw.core.registry import RegistryManager
        app = RegistryManager(app)
    return app
