import re

from dez.http.server import HTTPDaemon, HTTPResponse, HTTPVariableResponse, WSGIResponse
from dez.http.static import StaticHandler
from dez.http.wsgithreadpool import WSGIThreadPool
from dez.http.proxy.proxy import proxy

import event
import cgi

class HTTPApplication(object):
    """
        HTTPApplication depends on HTTPDaemon for its prefix ordering behavior.
        It issues cbs as per add_cb_rule in the same manner that HTTPDaemon
        does as well -- it simply passes a dez.http.server.request.HTTPRequest
        instance as the single argument to the callback. Its up to the user to
        construct the appropriate RawHTTPResponse or HTTPResponse as required.
    """

    def __init__(self, bind_address, port, get_logger=None, server_name="Dez"):
        """start listening on the given port (this doesn't include a call to
           event.dispatch)"""
        self.daemon = HTTPDaemon(bind_address, port, get_logger)
        self.host = bind_address
        self.port = port
        self.static_request = StaticHandler(server_name)
        self.wsgi_pool = None
        
    def start(self):
        """calls event.dispatch"""
        event.signal(2, self.stop)
        if self.wsgi_pool:
            self.wsgi_pool.start()
        event.dispatch()

    def stop(self):
        if self.wsgi_pool:
            self.wsgi_pool.stop()
        event.abort()
        
    def add_proxy_rule(self, prefix, dest_host, dest_port):
        """ Adds a prefix that will be proxied to the destination
            http_app.add_proxy_rule("/chat/", "127.0.0.1", 4700)
            url == "/chat/index.html"  # will be proxied
            url == "/hello/chat/index.html" # will not be proxied
        """
        self.daemon.register_prefix(prefix, self.__proxy, [dest_host, dest_port])
        
        
    def __proxy(self, req, host, port):
        return proxy(req, host, port)

    def add_static_rule(self, prefix, local_base_resource):
        """ Adds a prefix that will be served as a static directory or file
            http_app.add_static_rule("/static/", "/home/app/static")
            url == "/static/css/common.css" refers to /home/app/static/css/common.css

            http_app.add_static_rule("/", "/home/app/static/html/index.html")
            url == "/" refers to /home/app/static/html/index.html

            http_app.add_static_rule("/main.html", "/home/app/static/html/main.html")
            url == "/main.html" refers to /home/app/static/html/main.html
            url == "/main.html/foo" returns a 404
        """
        self.daemon.register_prefix(prefix, self.static_request, [prefix, local_base_resource])
#        self.static.register(prefix, local_base_resource)
#        raise Exception("NotImplemented"), "Static file serving not implemented"

    def add_cb_rule(self, prefix, cb, parsed=True):
        """ Adds a prefix that will issue a call to cb when a request with that
            url prefix is matched. This works the same as the HTTPDaemon's
            register_prefix function.

            def dispatch(req):
                req.write("HTTP/1.0 200 OK\r\n\r\nHello World")
                req.close()

            http_app.add_cb_rule("/helloworld", dispatch)
        """
        app_cb = cb
        if parsed:
            def format_wrapper(req):
                req = ParsedHTTPRequest(req, cb)
            app_cb = format_wrapper
        self.daemon.register_prefix(prefix, app_cb)

    def add_wsgi_rule(self, prefix, app):
        """ Adds a prefix that will execute (call) a wsgi compliant app function.
            Will provide the app with the appropriate environ and start_response
            function. 
        """
        if not self.wsgi_pool:
            self.wsgi_pool = WSGIThreadPool()
            
        script_name = prefix
        script_name_match = re.match(r"/(\S+?)\/?$", script_name)
        if script_name_match:
            script_name = "/" + script_name_match.group(1)
        
        if script_name != "/":
            try:
                from paste.deploy.config import PrefixMiddleware
                app = PrefixMiddleware(app, prefix=script_name)
            except ImportError:
                message = "could not import PrefixMiddleware from\n"
                message += "paste.deploy.config.\n"
                message += "You must install PasteDeploy before you can"
                message += " load a Pylons application.\n"
                raise ImportError(message)
        
        def wsgi_cb(req):
            r = WSGIResponse(req, app, self.host, self.port)
            self.wsgi_pool.dispatch(r)
#            r.dispatch()
        self.daemon.register_prefix(prefix, wsgi_cb)


class ParsedHTTPRequest(object):
    
    def __init__(self, req, cb):
        self.req = req
        self.cb = cb
        self.form = {}
        self.cookies = {}
        req.read_body(self.recv_body)
        
    def __getattr__(self, key):
        try:
            return object.__getattr__(self, key)
        except AttributeError:
          return getattr(self.req, key)
        
    def recv_body(self, body):
        self.body = body
        self.dispatch()  
      
    def dispatch(self):
        self.setup_url()
        self.setup_form()
        self.setup_cookies()
        self.cb(self)
        
    def setup_url(self):
        if self.req.method.lower() == "get" and "?" in self.req.url:
            self.url, self.qs = self.req.url.split('?', 1)
        elif self.req.method.lower() == "post":
            self.url, self.qs = self.req.url, self.body
        else:
            self.url, self.qs = self.req.url, ""
            
    def setup_form(self):
        try:
            for key, val in cgi.parse_qsl(self.qs):
                self.form[key] = val
        except ValueError:
            raise HTTPProtocolError, "Invalid querystring format"
        
    def setup_cookies(self):
        pass

def serve_wsgi_application(application, global_conf, **kwargs):
    """ Serve a WSGI application using a paste.deploy.config
    
        This is an adapter to allow dez to be used with the paster
        serve mechanism as interpreted through a *.ini file.
    """
    
    host = kwargs.pop("host", "127.0.0.1")
    port = int(kwargs.pop("port", "8888"))
    
    print "listening on %s:%s" % (host, port)
    httpd = HTTPApplication(host, port, **kwargs)
    httpd.add_wsgi_rule("/", application)
    httpd.start() 
