"""
Logging for code running in the sandbox. The output simply goes to stderr (which gets to the
console of the Node process), but the levels allow some configuration.

We don't use the `logging` module because it assumes more about the `time` module than we have.

Usage:
  import logger
  log = logger.Logger(__name__, logger.DEBUG)    # Or logger.WARN; default is logger.INFO.
  log.info("Hello world")
      -> produces "[I] [foo.bar] Hello world"
"""

import sys

# Level definitions
DEBUG = 10
INFO = 20
WARN = 30
ERROR = 40
CRITICAL = 50

# Level strings
level_strings = {
  DEBUG: 'DEBUG',
  INFO: 'INFO',
  WARN: 'WARN',
  ERROR: 'ERROR',
  CRITICAL: 'CRITICAL',
}

def log_stderr(level, name, msg):
  sys.stderr.write("[%s] [%s] %s\n" % (level_strings.get(level, '?'), name, msg))
  sys.stderr.flush()

_log_handler = log_stderr

def set_handler(log_handler):
  """
  Allows overriding the handler for all log messages. The handler should be a function, called as
  log_handler(level, name, message).
  Returns the handler which was set previously.
  """

  global _log_handler # pylint: disable=global-statement
  prev = _log_handler
  _log_handler = log_handler
  return prev


class Logger(object):
  """
  The object that actually provides the logging interface, specifically the methods debug, info,
  warn, error, and critical. The constructor takes an argument for a name that gets included in
  each message, and a minimum level, below which messages get ignored.
  """
  def __init__(self, name, min_level=INFO):
    self._name = name
    self._min_level = min_level

  def _log(self, level, msg):
    if level >= self._min_level:
      _log_handler(level, self._name, msg)

  def debug(self, msg):
    self._log(DEBUG, msg)
  def info(self, msg):
    self._log(INFO, msg)
  def warn(self, msg):
    self._log(WARN, msg)
  def error(self, msg):
    self._log(ERROR, msg)
  def critical(self, msg):
    self._log(CRITICAL, msg)