# Copyright (c) The PyAMF Project.
# See LICENSE.txt for details.

"""
WSGI server implementation.

The Python Web Server Gateway Interface (WSGI) is a simple and universal
interface between web servers and web applications or frameworks.

The WSGI interface has two sides: the "server" or "gateway" side, and the
"application" or "framework" side. The server side invokes a callable
object (usually a function or a method) that is provided by the application
side. Additionally WSGI provides middlewares; a WSGI middleware implements
both sides of the API, so that it can be inserted "between" a WSGI server
and a WSGI application -- the middleware will act as an application from
the server's point of view, and as a server from the application's point
of view.

@see: U{WSGI homepage (external)<http://wsgi.org>}
@see: U{PEP-333 (external)<http://www.python.org/peps/pep-0333.html>}

@since: 0.1.0
"""

import pyamf
from pyamf import remoting
from pyamf.remoting import gateway

__all__ = ['WSGIGateway']


class WSGIGateway(gateway.BaseGateway):
    """
    WSGI Remoting Gateway.
    """

    def getResponse(self, request, environ):
        """
        Processes the AMF request, returning an AMF response.

        @param request: The AMF Request.
        @type request: L{Envelope<pyamf.remoting.Envelope>}
        @rtype: L{Envelope<pyamf.remoting.Envelope>}
        @return: The AMF Response.
        """
        response = remoting.Envelope(request.amfVersion)

        for name, message in request:
            processor = self.getProcessor(message)
            environ['pyamf.request'] = message
            response[name] = processor(message, http_request=environ)

        return response

    def badRequestMethod(self, environ, start_response):
        """
        Return HTTP 400 Bad Request.
        """
        response = "400 Bad Request\n\nTo access this PyAMF gateway you " \
            "must use POST requests (%s received)" % environ['REQUEST_METHOD']

        start_response('400 Bad Request', [
            ('Content-Type', 'text/plain'),
            ('Content-Length', str(len(response))),
            ('Server', gateway.SERVER_NAME),
        ])

        return [response]

    def __call__(self, environ, start_response):
        """
        @rtype: C{StringIO}
        @return: File-like object.
        """
        if environ['REQUEST_METHOD'] != 'POST':
            return self.badRequestMethod(environ, start_response)

        body = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH']))
        stream = None
        timezone_offset = self._get_timezone_offset()

        # Decode the request
        try:
            request = remoting.decode(
                body,
                strict=self.strict,
                logger=self.logger,
                timezone_offset=timezone_offset
            )
        except (pyamf.DecodeError, IOError):
            if self.logger:
                self.logger.exception('Error decoding AMF request')

            response = "400 Bad Request\n\nThe request body was unable to " \
                "be successfully decoded."

            if self.debug:
                response += "\n\nTraceback:\n\n%s" % gateway.format_exception()

            start_response('400 Bad Request', [
                ('Content-Type', 'text/plain'),
                ('Content-Length', str(len(response))),
                ('Server', gateway.SERVER_NAME),
            ])

            return [response]
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            if self.logger:
                self.logger.exception('Unexpected error decoding AMF request')

            response = (
                "500 Internal Server Error\n\nAn unexpected error "
                "occurred whilst decoding."
            )

            if self.debug:
                response += "\n\nTraceback:\n\n%s" % gateway.format_exception()

            start_response('500 Internal Server Error', [
                ('Content-Type', 'text/plain'),
                ('Content-Length', str(len(response))),
                ('Server', gateway.SERVER_NAME),
            ])

            return [response]

        # Process the request
        try:
            response = self.getResponse(request, environ)
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            if self.logger:
                self.logger.exception('Error processing AMF request')

            response = (
                "500 Internal Server Error\n\nThe request was "
                "unable to be successfully processed."
            )

            if self.debug:
                response += "\n\nTraceback:\n\n%s" % gateway.format_exception()

            start_response('500 Internal Server Error', [
                ('Content-Type', 'text/plain'),
                ('Content-Length', str(len(response))),
                ('Server', gateway.SERVER_NAME),
            ])

            return [response]

        # Encode the response
        try:
            stream = remoting.encode(
                response,
                strict=self.strict,
                timezone_offset=timezone_offset
            )
        except:
            if self.logger:
                self.logger.exception('Error encoding AMF request')

            response = (
                "500 Internal Server Error\n\nThe request was "
                "unable to be encoded."
            )

            if self.debug:
                response += "\n\nTraceback:\n\n%s" % gateway.format_exception()

            start_response('500 Internal Server Error', [
                ('Content-Type', 'text/plain'),
                ('Content-Length', str(len(response))),
                ('Server', gateway.SERVER_NAME),
            ])

            return [response]

        response = stream.getvalue()

        start_response('200 OK', [
            ('Content-Type', remoting.CONTENT_TYPE),
            ('Content-Length', str(len(response))),
            ('Server', gateway.SERVER_NAME),
        ])

        return [response]
