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

import sys, socket, posixpath, datetime, traceback
from cPickle import dumps, HIGHEST_PROTOCOL

from rlextra.utils.cgisupport import BorgTimeStamp
from rlextra.ers import docgendb


LOGGER = None

def getConnectionFromDjangoSettings():
    import MySQLdb
    from django.conf import settings

    port = settings.USAGE_DATABASE_PORT
    kwds = dict(host=settings.USAGE_DATABASE_HOST,
                user=settings.USAGE_DATABASE_USER,
                passwd=settings.USAGE_DATABASE_PASSWORD,
                db=settings.USAGE_DATABASE_NAME,
                )
    if port:
        kwds['port'] = int(port)
    return MySQLdb.connect(**kwds)


class DbLogger:
    """Use dbutils to do hit logging.

    Subclass to add extra log entries (rltravelprod does this).
    """

    STACK_LIMIT = 100

    HOSTNAME = socket.gethostname()

    def __init__(self, applicationName, getConnection):
        self._getConn = getConnection
        self._applicationName = applicationName
        self._reset()
        self._conn = None

    def _reset(self):
        self._hitLogEntry = None
        self._docLogEntry = None
        self._errLogEntry = None

    def getConnection(self):
        # it's the caller's job to .close() this connection
        return self._getConn()

    def _getConnection(self):
        self.cleanup()
        self._conn = self._getConn()
        return self._conn

    def cleanup(self):
        if self._conn is not None:
            self._conn.close()
            self._conn = None
        self._reset()

    def logRequestParams(self, path, get, post=None, cookies=None, meta=None, files=None, userId=None):
        """Log request parameters."""
        assert self._hitLogEntry is None
        self._conn = self._getConnection()

        # note POST takes priority, like django.http.HttpRequest.__getitem__
        params = get.copy()
        params.update(post)

        e = docgendb.HitLogEntry(self._conn)
        # XXX we should probably make this id the same as used in PDF filename saved to disk, if any
        bts = BorgTimeStamp(appName=self._applicationName, hostname=DbLogger.HOSTNAME)
        bts.reset()
        e.id = bts.currentId
        e.request = dumps((path, get, post, cookies, meta, files, userId), HIGHEST_PROTOCOL)
        e.hostname = DbLogger.HOSTNAME
        # add field for meta.get('SERVER_ADDR') to docgendb?
        e.serverName = meta.get('SERVER_NAME')
        e.applicationName = self._applicationName
        e.timestamp = datetime.datetime.now()

        e.remoteIpAddress = meta.get('REMOTE_ADDR')
        e.userAgent = meta.get('HTTP_USER_AGENT')
        e.userId = userId

        # RL-specific standard parameters
        e.testUser = params.get('testing')
        e.source = params.get('source')

        e.action = path

        self._hitLogEntry = e

    def logDocumentGeneration(self,
                              chargeable=None,
                              filename=None,
                              documentCode=None,
                              mimetype=None,
                              layout=None,
                              locale=None,
                               branding=None,
                              pageCount=None,
                              fileSize=None,
                              ):
        assert self._hitLogEntry is not None
        assert self._docLogEntry is None
        e = docgendb.DocumentLogEntry(self._conn)
        e.id = e._getNextId()
        e.hitLogEntryId = self._hitLogEntry.id
        e.chargeable = chargeable
        if filename is None:
            filename = posixpath.basename(self._hitLogEntry.action)
        e.filename = filename
        e.documentCode = documentCode
        e.layout= layout
        e.mimetype = mimetype
        e.locale = locale
        e.branding = branding
        e.pageCount = pageCount
        e.fileSize = fileSize
        self._docLogEntry = e

    def logException(self, exc):
        assert self._hitLogEntry is not None
        tb = traceback.format_exc(self.STACK_LIMIT)
        excType, excValue = sys.exc_info()[0:2]
        try:
            msg = str(excValue)
        except:
            msg = '<unprintable %s object>' % type(excValue).__name__

        self._hitLogEntry.succeeded = False
        e = docgendb.ErrorLogEntry(self._conn)
        e.errorType = str(excType)
        e.errorDetail = msg
        e.formattedTraceback = tb
        e.hitLogEntryId = self._hitLogEntry.id
        self._errLogEntry = e

    def logSuccess(self):
        assert self._hitLogEntry is not None
        self._hitLogEntry.succeeded = True

    def _store(self, cur):
        self._hitLogEntry.store(cur)
        if self._docLogEntry is not None:
            self._docLogEntry.store(cur)
        if self._errLogEntry is not None:
            self._errLogEntry.store(cur)

    def commit(self):
        try:
            assert self._hitLogEntry is not None
            # foreign key constraints, so use a single cursor
            cur = self._conn.cursor()
            self._store(cur)
            self._conn.commit()
        finally:
            self._reset()
            cur.close()


def logCommandLineOperation(fn):
    from rldjango.stats.logger import LOGGER as logger
    logger.logRequestParams('[COMMANDLINE]', {},post={}, meta={'SERVER_NAME':'[FAKE]'})
    try:
        try:
            fn()
        except Exception, exc:
            logger.logException(exc)
            raise
        else:
            logger.logSuccess()
    finally:
        logger.commit()
