import logging
import os
import sys

try:
    import curses
except ImportError:
    curses = None


def _stderr_supports_color():
    color = False
    if curses and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty():
        try:
            curses.setupterm()
            if curses.tigetnum("colors") > 0:
                color = True
        except Exception:
            pass
    return color


class LogFormatter(logging.Formatter):
    """Log formatter originally from Tornado and modified."""
    DEFAULT_FORMAT = '%(color)s[%(levelname)1.1s %(asctime)s %(process)d]%(end_color)s %(message)s'
    DEFAULT_DATE_FORMAT = '%y%m%d %H:%M:%S'
    DEFAULT_COLORS = {
        logging.DEBUG: 4,    # Blue
        logging.INFO: 2,     # Green
        logging.WARNING: 3,  # Yellow
        logging.ERROR: 1,    # Red
    }

    def __init__(self, color=True, fmt=DEFAULT_FORMAT, datefmt=DEFAULT_DATE_FORMAT,
                 colors=DEFAULT_COLORS, precision=3):
        r"""
        :arg bool color: Enables color support.
        :arg string fmt: Log message format.
          It will be applied to the attributes dict of log records. The
          text between ``%(color)s`` and ``%(end_color)s`` will be colored
          depending on the level if color support is on.
        :arg dict colors: color mappings from logging level to terminal color
          code
        :arg string datefmt: Datetime format.
          Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``.
        .. versionchanged:: 3.2
           Added ``fmt`` and ``datefmt`` arguments.
        """
        super().__init__()
        self.default_time_format = datefmt
        self.precision = precision
        self.default_msec_format = ''
        self._fmt = fmt

        self._colors = {}
        if color and _stderr_supports_color():
            fg_color = (curses.tigetstr('setaf') or
                        curses.tigetstr('setf') or '')

            for levelno, code in colors.items():
                self._colors[levelno] = curses.tparm(fg_color, code).decode()
            self._normal = curses.tigetstr('sgr0').decode()
        else:
            self._normal = ''

    def format(self, record):
        record.message = record.getMessage()
        record.asctime = self.formatTime(record)

        if record.levelno in self._colors:
            record.color = self._colors[record.levelno]
            record.end_color = self._normal
        else:
            record.color = record.end_color = ''

        formatted = self._fmt % record.__dict__

        if record.exc_info:
            if not record.exc_text:
                record.exc_text = self.formatException(record.exc_info)
        if record.exc_text:
            lines = [formatted.rstrip()]
            lines.extend(ln for ln in record.exc_text.split('\n'))
            formatted = '\n'.join(lines)
        return formatted.replace('\n', '\n    ')

    def formatTime(self, record, datefmt=None):
        if not datefmt:
            datefmt = self.default_time_format
        fmttime = super().formatTime(record, datefmt)
        if self.precision >= 4:
            return '%s.%06d' % (fmttime, record.msecs*1000)
        if self.precision >= 1:
            return '%s.%03d' % (fmttime, record.msecs)
        return fmttime


def setup_logger(name=None, level=None, formatter_opts=None):
    """Sets up pretty logging using LogFormatter."""
    if formatter_opts is None:
        formatter_opts = {}
    logging.captureWarnings(True)
    logger = logging.getLogger(name)
    if 'DEBUG' in os.environ:
        level = logging.DEBUG
    elif level is None:
        level = logging.INFO
    logger.setLevel(level)
    channel = logging.StreamHandler()
    formatter = LogFormatter(**formatter_opts)
    channel.setFormatter(formatter)
    logger.addHandler(channel)
    return logger