"""Various middlewares we use.

LoggingMiddleware used by Hilton and others to generate
our traditional log files and capture most details of the
request.

Extended with a trick from the Django cookbook to store
the current user, which is something commonly logged
with stats and which models may want to get at.

In early 2009, attempting to be compatible with django 1.1
and 0.97 signals with a feature-detection trick.

LastVisitedMiddleware  sets the last visited url as session field
"""
import datetime, os, sys
from django.conf import settings
from django.core import signals
from django.http import Http404
from django.dispatch import dispatcher
#from rlextra.ers import errhandlers
#from rlextra.ers.apploggers import FileAppLogger
from rlextra.radxml.xmlutils import validateXhtml
from rldjango.common.utils import deepEncode

try:
    from threading import local
except ImportError:
    from django.utils._threading_local import local

_thread_locals = local()

def getCurrentUser():
    "Call this from anywhere to get the user, assuming middleware below is installed"
    return getattr(_thread_locals, 'user', None)

def getCurrentRequest():
    "Call this from anywhere to get the request, assuming middleware below is installed"
    return getattr(_thread_locals, 'request', None)

class UserFindingMiddleware(object):
    """Middleware that gets various objects from the
    request object and saves them in thread local storage."""
    def process_request(self, request):
        _thread_locals.user = getattr(request, 'user', None)
        _thread_locals.request = request

class ValidateXhtmlMiddleware(object):
    """This strictly validates any page with content-type of html
    os xhtml.  Never mind your doctype declaration, this is the fascist
    of Middleware and will give no mercy.

    Please, correct me! Validate me! YES YES YES!
    """
    def process_response(self, request, response):
        if not response.status_code == 200:
            return response
        ct = response['Content-Type'].split(';')[0]   #can have charset afterwards
        if ct in ('text/html', 'text/xhtml'):
            try:
                validateXhtml(response.content)
                return response
            except Exception, e:
                response['Content-Type'] = 'text/plain; charset=utf8'
                output = []
                output.append("The server generated invalid XHTML.  The error message was as follows")
                output.append('')
                for arg in e.args:
                    output.append(arg)
                output.append('')
                output.append('------ generated HTML follows ------')
                for (lineNo, line) in enumerate(response.content.split('\n')):
                    output.append('%05d %s' % (lineNo+1, line))
                newContent = '\n'.join(output)
                response.content = newContent
                return response

        else:
            return response

class LoggingMiddleware(object):

    def get_logger(self, request, force_new=False):
        if not hasattr(self, 'logger') or force_new:
            logFN = os.path.join(settings.LOG_ROOT, '%s-%s-%s-log.txt' % (request.META.get('SERVER_NAME'), settings.APPNAME, datetime.date.today().strftime('%Y%m%d')))
            kwds = dict(logFN=logFN)
            if hasattr(request, 'ts'):
                kwds['ts'] = request.ts
            else:
                kwds['isBorg'] = False
            self.logger = FileAppLogger(**kwds)
        return self.logger

    def finished(self, signal, sender, *args, **kwds):
        try:
            name = self.logger._entry['startcgi']['action']
            error = self.logger._entry.get('error', None)
        except Exception:
            name = error = 'noaction'
        if name in settings.LOG_VIEWS or name in ('handle404', 'noaction') or error:
            if hasattr(dispatcher, 'disconnect'):  #old django
                dispatcher.disconnect(self.finished, signal=signals.request_finished)
            else:  #new django
                signals.request_finished.disconnect(self.finished)

            if not os.path.isdir(settings.LOG_ROOT):
                os.makedirs(settings.LOG_ROOT)
            self.logger.store()

    def process_view(self, request, view_func, view_args, view_kwargs):
        name = '.'.join([view_func.__module__, getattr(view_func,'__name__',getattr(view_func,'__class__').__name__)])
        self.get_logger(request, force_new=True)

        if hasattr(dispatcher, 'connect'):  #old django
            dispatcher.connect(self.finished, signal=signals.request_finished)
        else:  #new django
            signals.request_finished.connect(self.finished)

        params = {}
        params['action'] = name
        params = view_kwargs.copy()
        if request.POST:
            params.update(request.POST.copy())
        else:
            params.update(request.GET.copy())
        for p in params.keys():
            if p in ['firstName', 'lastName']:
                params[p] = '**Privacy Filtered**'
        params['user'] = request.user.username
        self.logger.record('startcgi',
                           params=deepEncode(params, 'utf-8'),
                           action = name,
                           remoteIpAddress = request.META.get('REMOTE_ADDR'),
                           httpHost = request.META.get('HTTP_HOST'),
                           serverName = request.META.get('SERVER_NAME'),
                           userAgent = request.META.get('HTTP_USER_AGENT'),
                           referringPage = request.META.get('HTTP_REFERER'),
                           requestMethod = request.META.get('REQUEST_METHOD', 'GET'),
                           path = deepEncode(request.path, 'utf-8'),)

    def process_response(self, request, response):
        self.get_logger(request)
        if hasattr(response, '_headers'):  #errors don't, so avoid further confusing error messages
            finishParams = getattr(response, 'finishParams', {})
            if response['Content-Type']=='application/pdf' and 'pdfFileSize' not in finishParams:
                finishParams['pdfFileSize'] = len(response.content)
            self.logger.record('finish',
                               params = deepEncode(finishParams),
                               headers = deepEncode(dict(response._headers.itervalues())),
                               )
        elif hasattr(response, 'headers'):  #errors don't, so avoid further confusing error messages
            finishParams = getattr(response, 'finishParams', {})
            if response.headers['Content-Type']=='application/pdf' and 'pdfFileSize' not in finishParams:
                finishParams['pdfFileSize'] = len(response.content)
            self.logger.record('finish',
                               params = finishParams,
                               headers = response.headers,
                               )
        return response

    def process_exception(self, request, exception):
        self.get_logger(request)
        et, ev = map(str,sys.exc_info()[:2])
        ftb = errhandlers.ShowTracebackHandler().handleException()
        if isinstance(exception, Http404): 
            try: 
                scgi = self.logger._entry['startcgi']
                scgi['orig_action'] = scgi['action']
                scgi['action'] = 'handle404'
                self.logger._entry['startcgi'] = scgi
            except:
                new_et, new_ev = map(str,sys.exc_info()[:2])
                new_ftb = errhandlers.ShowTracebackHandler().handleException()
                self.logger.record('error', orig_ftb=ftb, orig_error_type=et, orig_value=ev,
                        ftb=new_ftb, error_type=new_et, error_value=new_ev)
        else:
            self.logger.record('error', ftb=ftb, error_type=et, error_value=ev)

class LastVisitedMiddleware(object):
    """This middleware sets the last visited url as session field"""

    import re
    ignore = re.compile('(\/jsi18n|\.)')


    def process_request(self, request):
        """Intercept the request and add the current path to it"""
        request_path = request.get_full_path()
        try:
            request.session['last_visited'] = request.session['currently_visiting']
        except KeyError:
            # silence the exception - this is the users first request
            pass

        if not self.ignore.search(request_path):
            request.session['currently_visiting'] = request_path
