#Copyright ReportLab Europe Ltd. 2006
#see license.txt for license details
# $Id$

__all__ = ['LoggingMiddleware']

import logging

from django.core import signals
from django.dispatch import dispatcher
import django.views.static


class LoggingMiddleware(object):

    def __init__(self, logger):
        self._logger = logger
        self._handlingView = False

    def process_view(self, request, view_func, view_args, view_kwargs):
        assert not self._handlingView
        _fixModPythonRemoteIp(request)
        if view_func is django.views.static.serve:
            return None

        self._handlingView = True
        self._logger.logRequestParams(*tupleFromDjangoRequest(request))
        return None

    def process_response(self, request, response):
        if not self._handlingView:
            return response

        self._logger.logSuccess()
        self._logger.commit()
        self._handlingView = False
        return response

    def process_exception(self, request, exception):
        if not self._handlingView:
            from django.conf import settings
            assert settings.DEVELOP  # we're in development mode and serving a static file
            return None

        self._logger.logException(exception)
        self._logger.commit()
        self._handlingView = False
        return None


def _fixModPythonRemoteIp(request):
    # Attempt to find remote IP and set it on the request -- this is
    # complicated by reverse proxying.

    # If we're behind a reverse proxy, X-Forwarded-For HTTP header should
    # contain the remote IP.  It may be that if we're NOT behind a proxy and we
    # get this header, the server does NOT remove this header -- in that case,
    # the client can forge their address especially easily.  But we don't care
    # ;-)

    # Apache mod_proxy operating as a reverse proxy seems to just put the
    # original IP address in this header.  Googling, looks like IIS and Squid
    # put a ', '-separated list of (IP address or 'unknown') in this header.
    # At the moment this just sticks the whole header value in the REMOTE_ADDR
    # dict entry, hence the HitLogEntry.remoteIpAddress DB field.

    # Django doesn't give us access to the request headers, so this hack
    # only works for mod_python -- should really be part of the mod_python
    # handler.  The problem is NOT confined to mod_python, though.
    from django.core.handlers.modpython import ModPythonRequest
    if not isinstance(request, ModPythonRequest):
        # not mod_python
        return
    try:
        mpReq = request._req
    except AttributeError:
        # not mod_python, or internals have changed...
        return
    else:
        xff = mpReq.headers_in.get('X-Forwarded-For')
        if xff is not None:
            request.META['REMOTE_ADDR'] = xff

def tupleFromDjangoRequest(request):
    from django.utils.datastructures import MultiValueDict

    user = getattr(request, 'user', None)
    if user is not None:
        userId = user.username
    else:
        userId = None

    # Turn MultiValueDict instances into plain dictionaries.
    # I am paranoid that the types of these dict-like objects may vary
    # depending on who knows what, so check here at runtime instead of assuming
    # particular (e.g. request.GET, request.POST and request.FILES, which I
    # *think* is always exactly the set that are MultiValueDicts).
    dicts = []
    for mapping in [request.GET, request.POST, request.COOKIES, request.META, request.FILES]:
        if isinstance(mapping, MultiValueDict):  # note that MultiValueDict is a dict subclass
            # We only want the first value for each key, since those are the
            # original request values
            dicts.append(dict([(key, vals[0]) for key, vals in mapping.lists()]))
        else:
            assert type(mapping) is dict
            dicts.append(mapping)
    return tuple([request.path]+dicts+[userId])


def standardHitLoggingMiddleware():
    from django.conf import settings
    import rldjango.stats.logger
    logger = rldjango.stats.logger.DbLogger(
        settings.APPNAME, rldjango.stats.logger.getConnectionFromDjangoSettings)
    rldjango.stats.logger.LOGGER = logger
    dispatcher.connect(logger.cleanup, signal=signals.request_finished)
    return LoggingMiddleware(logger)
