"""
A Webware HTTPResponse object.
"""

import time
from wkcommon import NoDefault, Cookie
from MiscUtils.DateInterval import timeDecode
import urlparse
from paste.request import resolve_relative_url
TimeTupleType = type(time.gmtime(0))

class HTTPResponse(object):

    def __init__(self, transaction, environ, start_response):
        self._transaction = transaction
        self._environ = environ
        self._start_response = start_response
        self._writer = None
        self._committed = False
        self._autoFlush = False
        self.reset()
    
    def endTime(self):
        return self._endTime
    
    def recordEndTime(self):
        """
        Stores the current time as the end time of the response. This
        should be invoked at the end of deliver(). It may also be
        invoked by the application for those responses that never
        deliver due to an error."""
        self._endTime = time.time()

    ## Headers ##

    def header(self, name, default=NoDefault):
        """ Returns the value of the specified header. """
        if default is NoDefault:
            return self._headers[name.lower()]
        else:
            return self._headers.get(name.lower(), default)

    def hasHeader(self, name):
        return self._headers.has_key(name.lower())

    def setHeader(self, name, value):
        """
        Sets a specific header by name.
        """
        assert self._committed==0, "Headers have already been sent"
        assert type(name) is str, (
            "Header %r name is not string" % name)
        assert type(value) is str, (
            "Header %s value is not string (%r)" % (name, value))
        self._headers[name.lower()] = value

    def headers(self):
        """
        Returns a dictionary-style object of all Header objects
        contained by this request. """
        return self._headers

    def clearHeaders(self):
        """
        Clears all the headers. You might consider a
        setHeader('Content-type', 'text/html') or something similar
        after this."""
        assert self._committed==0
        self._headers = {}

    ## Cookies ##

    def cookie(self, name):
        """ Returns the value of the specified cookie. """
        return self._cookies[name]

    def hasCookie(self, name):
        """
        Returns true if the specified cookie is present.
        """
        return self._cookies.has_key(name)
    
    def setCookie(self, name, value, path='/', expires='ONCLOSE',
                  secure=False):
        """
        Set a cookie.  You can also set the path (which defaults to /),
        You can also set when it expires.  It can expire:
        
        'NOW':
          this is the same as trying to delete it, but it
          doesn't really seem to work in IE
          
        'ONCLOSE':
          the default behavior for cookies (expires when
          the browser closes)
          
        'NEVER':
          some time in the far, far future.

        integer:
          a timestamp value

        tuple:
          a tuple, as created by the time module
          
        """
        cookie = Cookie(name, value)
        if expires == 'ONCLOSE' or not expires:
            pass # this is already default behavior
        elif expires == 'NOW' or expires == 'NEVER':
            t = time.gmtime(time.time())
            if expires == 'NEVER':
                t = (t[0] + 10,) + t[1:]
            t = time.strftime("%a, %d-%b-%Y %H:%M:%S GMT", t)
            cookie.setExpires(t)
        else:
            t = expires
            if isinstance(t, (str, unicode)) and t.startswith('+'):
                interval = timeDecode(t[1:])
                t = time.time() + interval
            if isinstance(t, (int, long, float)):
                t = time.gmtime(t)
            if isinstance(t, (tuple, TimeTupleType)):
                t = time.strftime("%a, %d-%b-%Y %H:%M:%S GMT", t)
            cookie.setExpires(t)
        if path:
            cookie.setPath(path)
        if secure:
            cookie.setSecure(secure)
        self.addCookie(cookie)

    def addCookie(self, cookie):
        """
        Adds a cookie that will be sent with this response.
        cookie is a Cookie object instance.  See WebKit.Cookie.
        """
        assert self._committed==0
        assert isinstance(cookie, Cookie)
        self._cookies[cookie.name()] = cookie

    def delCookie(self, name):
        """
        Deletes a cookie at the browser. To do so, one has
        to create and send to the browser a cookie with
        parameters that will cause the browser to delete it.
        """
        if self._cookies.has_key(name):
            self._cookies[name].delete()
        else:
            cookie = Cookie(name, None)
            cookie.delete()
            self.addCookie(cookie)
            
    def cookies(self):
        """
        Returns a dictionary-style object of all Cookie objects that will be sent
        with this response.
        """
        return self._cookies

    def clearCookies(self):
        """ Clears all the cookies. """
        assert self._committed==0
        self._cookies = {}

    ## Status ##

    def setStatus(self, code, msg=''):
        """ Set the status code of the response, such as 200, 'OK'. """
        assert self._committed==0, "Headers already sent."
        self.setHeader('Status', str(code) + ' ' + msg)

    ## Special responses ##

    def sendError(self, code, msg=''):
        """
        Sets the status code to the specified code and message.
        """
        assert self._committed==0, "Response already partially sent"
        self.setStatus(code, msg)

    def sendRedirect(self, url):
        """
        This method sets the headers and content for the redirect, but
        does NOT change the cookies. Use clearCookies() as
        appropriate.

        @@ 2002-03-21 ce: I thought cookies were ignored by user
        agents if a redirect occurred. We should verify and update
        code or docs as appropriate.
        """
        # ftp://ftp.isi.edu/in-notes/rfc2616.txt
        # Sections: 10.3.3 and others

        url = resolve_relative_url(
            url, self._transaction.request().environ())
        assert not self._committed, "Headers already sent"

        self.setHeader('Status', '302 Redirect')
        self.setHeader('Location', url)
        self.setHeader('Content-type', 'text/html')

        self.write('<html> <body> This page has been redirected to '
                   '<a href="%s">%s</a>. </body> </html>' % (url, url))

    ## Output ##

    def write(self, charstr=None):
        """
        Write charstr to the response stream.
        """
        if not charstr:
            return
        if self._autoFlush:
            assert self._committed
            self._writer(charstr)
        else:
            self._output.append(charstr)

    def flush(self, autoFlush=True):
        """
        Send all accumulated response data now.  Commits the response
        headers and tells the underlying stream to flush.  if
        autoFlush is true, the responseStream will flush itself
        automatically from now on.
        """
        if not self._committed:
            self.commit()
        if self._output:
            self._writer(''.join(self._output))
        self._autoFlush = autoFlush

    def isCommitted(self):
        """
        Has the reponse already been partially or completely sent?  If
        this returns true, no new headers/cookies can be added to the
        response.
        """
        return self._committed

    def deliver(self):
        """
        The final step in the processing cycle.
        Not used for much with responseStreams added.
        """
        self.recordEndTime()
        if not self._committed: self.commit()

    def commit(self):
        """
        Write out all headers to the reponse stream, and tell the
        underlying response stream it can start sending data.
        """
        status = self._headers['status']
        del self._headers['status']
        headers = self._headers.items()
        for cookie in self._cookies.values():
            headers.append(('Set-Cookie', cookie.headerValue()))
        self._writer = self._start_response(status, headers)
        self._committed = True

    def wsgiIterator(self):
        return self._output

    def recordSession(self):
        raise NotImplementedError

    def reset(self):
        """
        Resets the response (such as headers, cookies and contents).
        """

        assert self._committed == 0
        self._headers = {}
        self.setHeader('Content-type','text/html')
        self.setHeader('Status', '200 OK')
        self._cookies = {}
        self._output = []

    def rawResponse(self):
        raise NotImplementedError

    def size(self):
        raise NotImplementedError

    def mergeTextHeaders(self, headerstr):
        raise NotImplementedError
