"""
Logging facilities

These are thin wrappers on top of Twisted's
logging facilities; logs are all directed
either to stdout (if Evennia is running in
interactive mode) or to game/logs.

The log_file() function uses its own threading
system to log to arbitrary files in game/logs.

Note:
All logging functions have two aliases,
log_type() and log_typemsg(). This is for
historical, back-compatible reasons.

"""

from datetime import datetime
from traceback import format_exc
from twisted.python import log
from twisted.internet.threads import deferToThread


def log_trace(errmsg=None):
    """
    Log a traceback to the log. This should be called
    from within an exception. errmsg is optional and
    adds an extra line with added info.
    """
    tracestring = format_exc()
    try:
        if tracestring:
            for line in tracestring.splitlines():
                log.msg('[::] %s' % line)
        if errmsg:
            try:
                errmsg = str(errmsg)
            except Exception, e:
                errmsg = str(e)
            for line in errmsg.splitlines():
                log.msg('[EE] %s' % line)
    except Exception:
        log.msg('[EE] %s' % errmsg)
log_tracemsg = log_trace


def log_err(errmsg):
    """
    Prints/logs an error message to the server log.

    errormsg: (string) The message to be logged.
    """
    try:
        errmsg = str(errmsg)
    except Exception, e:
        errmsg = str(e)
    for line in errmsg.splitlines():
        log.msg('[EE] %s' % line)
    #log.err('ERROR: %s' % (errormsg,))
log_errmsg = log_err


def log_warn(warnmsg):
    """
    Prints/logs any warnings that aren't critical but should be noted.

    warnmsg: (string) The message to be logged.
    """
    try:
        warnmsg = str(warnmsg)
    except Exception, e:
        warnmsg = str(e)
    for line in warnmsg.splitlines():
        log.msg('[WW] %s' % line)
    #log.msg('WARNING: %s' % (warnmsg,))
log_warnmsg = log_warn


def log_info(infomsg):
    """
    Prints any generic debugging/informative info that should appear in the log.

    infomsg: (string) The message to be logged.
    """
    try:
        infomsg = str(infomsg)
    except Exception, e:
        infomsg = str(e)
    for line in infomsg.splitlines():
        log.msg('[..] %s' % line)
log_infomsg = log_info


def log_dep(depmsg):
    """
    Prints a deprecation message
    """
    try:
        depmsg = str(depmsg)
    except Exception, e:
        depmsg = str(e)
    for line in depmsg.splitlines():
        log.msg('[DP] %s' % line)
log_depmsg = log_dep


# Arbitrary file logger

LOG_FILE_HANDLES = {} # holds open log handles

def log_file(msg, filename="game.log"):
    """
    Arbitrary file logger using threads.  Filename defaults to
    'game.log'. All logs will appear in game/logs directory and log
    entries will start on new lines following datetime info.
    """
    global LOG_FILE_HANDLES

    def callback(filehandle, msg):
        "Writing to file and flushing result"
        msg = "\n%s [-] %s" % (datetime.now(), msg.strip())
        filehandle.write(msg)
        # since we don't close the handle, we need to flush
        # manually or log file won't be written to until the
        # write buffer is full.
        filehandle.flush()
    def errback(failure):
        "Catching errors to normal log"
        log_trace()

    # save to game/logs/ directory
    filename = "logs/" + filename

    if filename in LOG_FILE_HANDLES:
        filehandle = LOG_FILE_HANDLES[filename]
    else:
        try:
            filehandle = open(filename, "a")
            LOG_FILE_HANDLES[filename] = filehandle
        except IOError:
            log_trace()
            return
    deferToThread(callback, filehandle, msg).addErrback(errback)
