import webob.exc

from repoze.bfg.configuration import Configurator
from repoze.bfg.url import route_url
from repoze.bfg.threadlocal import get_current_request
from repoze.bfg.exceptions import NotFound
from repoze.bfg.exceptions import Forbidden

from repoze.bfg.events import NewRequest

from repoze.bfg.view import AppendSlashNotFoundViewFactory
from repoze.bfg.view import append_slash_notfound_view

from repoze.bfg.renderers import render

from beaker.middleware import SessionMiddleware

from repoze.bfg.jinja2.bindings import renderer_factory as jinja2_renderer

from paste.httpserver import serve
from paste.registry import StackedObjectProxy
from paste.registry import RegistryManager

from weberror.evalexception import EvalException

request = StackedObjectProxy()
application = StackedObjectProxy()
g = StackedObjectProxy()

class Namespace(object):
    pass

class Groundhog(object):
    def __init__(self, package, session_key):
        self.package = package
        self.session_key = session_key
        self.g = Namespace()
        self.config = Configurator()
        self.config.begin()
        self.config.add_renderer('groundhog', self.renderer_factory)
        self.config.add_renderer('.html', jinja2_renderer)
        self.config.set_renderer_globals_factory(self.renderer_globals_factory)
        self.config.add_view(append_slash_notfound_view, context=NotFound)
        self.config.add_view(self.handle_webob_exc,
                             context=webob.exc.WSGIHTTPException)
        self.config.add_static_view('static', '%s:static' % self.package)
        self.config.add_subscriber(self.start_request, NewRequest)

    def start_request(self, event):
        req = event.request
        environ = req.environ
        reg = environ.get('paste.registry')
        if reg is not None:
            reg.register(request, req)
            reg.register(application, self)
            reg.register(g, self.g)

    def handle_webob_exc(self, exc, request):
        return request.get_response(exc)

    def renderer_factory(self, name):
        def render_(value, system):
            request = system.get('request')
            if request is not None:
                request.response_content_type = 'text/html'
            return value
        return render_

    def renderer_globals_factory(self, system):
        return {'g':self.g, 'app':self}

    def mapply(self, func):
        def mapplied(request):
            return func(**request.matchdict)
        return mapplied

    def route(self, pattern, methods=('GET', 'HEAD')):
        def decorator(func):
            endpoint = func.__name__
            self.config.add_route(endpoint, pattern)
            for method in methods:
                view = self.mapply(func)
                self.config.add_view(view, route_name=endpoint,
                                     request_method=method,
                                     renderer='groundhog')
            return func
        return decorator

    def run(self, host=None, port=8080, debug=False):
        self.config.end()
        app = self.config.make_wsgi_app()
        if debug:
            app = EvalException(app)
        session_options = {
            'session.type':'cookie',
            'session.cookie_expires':True,
            'session.auto':True,
            'session.validate_key':self.session_key,
            }
        app = SessionMiddleware(app, session_options)
        app = RegistryManager(app)
        serve(app, host, port)

    def url_for(self, endpoint, *arg, **options):
        if not isinstance(endpoint, basestring):
            endpoint = endpoint.__name__
        request = self.request
        return route_url(endpoint, request, *arg, **options)

    @property
    def request(self):
        return get_current_request()

    @property
    def session(self):
        return self.request.environ['beaker.session']

    def flash(self, message):
        session = self.session
        fq = session.setdefault('flash_queue', [])
        fq.append(message)
        session.save()

    def get_flashed_messages(self):
        session = self.session
        result = session.pop('flash_queue', [])
        session.save()
        return result

    def render_template(self, name, **kw):
        request = self.request
        return render('%s:templates/%s' % (self.package, name),
                      request=request, **kw)

    def abort(self, code, message=''):
        raise webob.exc.status_map[code](message)

    def redirect(self, url):
        raise webob.exc.HTTPFound(location=url)

    def errorhandler(self, code):
        bfg_status_map = {404:NotFound, 401:Forbidden}

        def decorator(func):
            def errfunc(exc, request):
                request.response_status = exc.status
                return func(exc)

            webob_exc = webob.exc.status_map.get(code)
            bfg_exc = bfg_status_map.get(code)

            for exc in (webob_exc, bfg_exc):
                if exc is not None:
                    view = errfunc
                    if exc is NotFound:
                        view = AppendSlashNotFoundViewFactory(errfunc)
                    self.config.add_view(view, context=exc,
                                         renderer='groundhog')

        return decorator

    def listen(self, *event_types):
        def decorator(func):
            if event_types:
                for typ in event_types:
                    self.config.add_subscriber(func, typ)
            else:
                self.config.add_subscriber(func, None)
        return decorator

    def notify(self, event):
        self.config.registry.notify(event)

# application

#from groundhog import request

app = Groundhog(__name__, 'seekrit')

@app.route('/')
def root():
    return 'root'

@app.route('/exc/:code')
def exc(code=404):
    code = int(code)
    app.abort(code, 'this is the message')

@app.route('/redir')
def redir():
    app.redirect('/')

class MyEvent(object):
    def __init__(self, param):
        self.param = param

@app.route('/notify/:param')
def notify(param=None):
    app.notify(MyEvent(param))
    return 'notified'

@app.listen()
def listen(event):
    print event, event.__dict__

@app.route('/showglobals')
def showglobals():
    import cgi
    print request
    print application
    print g
    return cgi.escape('%s %s %s' % (request, application, g))

if __name__ == '__main__':
    app.run(debug=True)

