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

"""
Gateway for the Django framework.

This gateway allows you to expose functions in Django to AMF clients and
servers.

@see: U{Django homepage<http://djangoproject.com>}
@since: 0.1.0
"""

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


django = __import__('django.http')
http = django.http
conf = __import__('django.conf')
conf = conf.conf

__all__ = ['DjangoGateway']


class DjangoGateway(gateway.BaseGateway):
    """
    An instance of this class is suitable as a Django view.

    An example usage would be through C{urlconf}::

        from django.conf.urls.defaults import *

        urlpatterns = patterns('',
            (r'^gateway/', 'yourproject.yourapp.gateway.gw_instance'),
        )

    where C{yourproject.yourapp.gateway.gw_instance} refers to an instance of
    this class.

    @ivar expose_request: The standard Django view always has the request
        object as the first parameter. To disable this functionality, set this
        to C{False}.
    """

    csrf_exempt = True

    def __init__(self, *args, **kwargs):
        kwargs['expose_request'] = kwargs.get('expose_request', True)

        try:
            tz = conf.settings.AMF_TIME_OFFSET
        except AttributeError:
            tz = None

        try:
            debug = conf.settings.DEBUG
        except AttributeError:
            debug = False

        kwargs['timezone_offset'] = kwargs.get('timezone_offset', tz)
        kwargs['debug'] = kwargs.get('debug', debug)

        gateway.BaseGateway.__init__(self, *args, **kwargs)

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

        @param http_request: The underlying HTTP Request.
        @type http_request: U{HTTPRequest<http://docs.djangoproject.com
            /en/dev/ref/request-response/#httprequest-objects>}
        @param request: The AMF Request.
        @type request: L{Envelope<pyamf.remoting.Envelope>}
        @rtype: L{Envelope<pyamf.remoting.Envelope>}
        """
        response = remoting.Envelope(request.amfVersion)

        for name, message in request:
            http_request.amf_request = message

            processor = self.getProcessor(message)
            response[name] = processor(message, http_request=http_request)

        return response

    def __call__(self, http_request):
        """
        Processes and dispatches the request.
        """
        if http_request.method != 'POST':
            return http.HttpResponseNotAllowed(['POST'])

        stream = None
        timezone_offset = self._get_timezone_offset()

        try:
            body = http_request.body
        except AttributeError:
            body = http_request.raw_post_data

        # 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()

            # support for Django 0.96
            http_response = http.HttpResponse(
                content_type='text/plain',
                content=response
            )

            http_response.status_code = 400

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

            response = (
                '500 Internal Server Error\n\n'
                'An unexpected error occurred.'
            )

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

            return http.HttpResponseServerError(
                content_type='text/plain',
                content=response
            )

        # Process the request
        try:
            response = self.getResponse(http_request, request)
        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()

            return http.HttpResponseServerError(
                content_type='text/plain',
                content=response
            )

        # Encode the response
        try:
            stream = remoting.encode(
                response,
                strict=self.strict,
                logger=self.logger,
                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()

            return http.HttpResponseServerError(
                content_type='text/plain', content=response)

        buf = stream.getvalue()

        http_response = http.HttpResponse(content_type=remoting.CONTENT_TYPE)
        http_response['Server'] = gateway.SERVER_NAME
        http_response['Content-Length'] = str(len(buf))

        http_response.write(buf)

        return http_response
