#!/usr/bin/env python3
"""
B-ASIC Scheduler-gui Logger Module.

Contains a logger that logs to the console and a file using levels. It is based
on the :mod:`logging` module and has predefined levels of logging.

Usage:
------

    >>> import b_asic.scheduler_gui.logger as logger
    >>> log = logger.getLogger()
    >>> log.info('This is a log post with level INFO')

.. list-table::
   :widths: 50 25 25
   :header-rows: 1

   * - Function call
     - Level
     - Numeric value
   * - debug(str)
     - DEBUG
     - 10
   * - info(str)
     - INFO
     - 20
   * - warning(str)
     - WARNING
     - 30
   * - error(str)
     - ERROR
     - 40
   * - critical(str)
     - CRITICAL
     - 50
   * - exception(str)
     - ERROR
     - 40

The last `exception(str)` is used to capture exceptions output, that normally
will not be captured.
See https://docs.python.org/3/howto/logging.html for more information.

Log Uncaught Exceptions:
------------------------
To log uncaught exceptions, implement the following in your program.
  `sys.excepthook = logger.log_exceptions`"""
import logging
import logging.handlers
import os
import sys
from logging import Logger
from types import TracebackType
from typing import Type, Union


def getLogger(filename: str = "scheduler-gui.log", loglevel: str = "INFO") -> Logger:
    """
    This function creates console- and filehandler and from those, creates a logger
    object.

    Parameters
    ----------
    filename : str optional
        Output filename. Defaults to 'scheduler-gui.log'.
    loglevel : str, optional
        The minimum level that the logger will log. Defaults to 'INFO'.

    Returns
    -------
    Logger : 'logging.Logger' object.
    """

    # logger = logging.getLogger(name)
    # logger = logging.getLogger('root')
    logger = logging.getLogger()

    # if logger 'name' already exists, return it to avoid logging duplicate
    # messages by attaching multiple handlers of the same type
    if logger.handlers:
        return logger
    # if logger 'name' does not already exist, create it and attach handlers
    else:
        # set logLevel to loglevel or to INFO if requested level is incorrect
        loglevel = getattr(logging, loglevel.upper(), logging.INFO)
        logger.setLevel(loglevel)

        # set up the console logger
        c_fmt_date = "%T"
        c_fmt = (
            "[%(process)d] %(asctime)s %(filename)18s:%(lineno)-4s"
            " %(funcName)20s() %(levelname)-8s: %(message)s"
        )
        c_formatter = logging.Formatter(c_fmt, c_fmt_date)
        c_handler = logging.StreamHandler()
        c_handler.setFormatter(c_formatter)
        c_handler.setLevel(logging.WARNING)
        logger.addHandler(c_handler)

        # setup the file logger
        f_fmt_date = "%Y-%m-%dT%T%Z"
        f_fmt = (
            "%(asctime)s %(filename)18s:%(lineno)-4s %(funcName)20s()"
            " %(levelname)-8s: %(message)s"
        )
        f_formatter = logging.Formatter(f_fmt, f_fmt_date)
        f_handler = logging.FileHandler(filename, mode="w")
        f_handler.setFormatter(f_formatter)
        f_handler.setLevel(logging.DEBUG)
        logger.addHandler(f_handler)

    if logger.name == "scheduler-gui.log":
        logger.info(
            "Running: %s %s",
            os.path.basename(sys.argv[0]),
            " ".join(sys.argv[1:]),
        )

    return logger


# log uncaught exceptions
def handle_exceptions(
    exc_type: Type[BaseException],
    exc_value: BaseException,
    exc_traceback: Union[TracebackType, None],
) -> None:
    # def log_exceptions(type, value, tb):
    """This function is a helper function to log uncaught exceptions. Install with:
    `sys.excepthook = <module>.handle_exceptions`"""
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return

    logging.exception(
        "Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback)
    )


# def qt_message_handler(mode, context, message):
#     if mode == QtCore.QtInfoMsg:
#         mode = 'INFO'
#     elif mode == QtCore.QtWarningMsg:
#         mode = 'WARNING'
#     elif mode == QtCore.QtCriticalMsg:
#         mode = 'CRITICAL'
#     # elif mode == QtCore.QtErrorMsg:
#     #     mode = 'ERROR'
#     elif mode == QtCore.QtFatalMsg:
#         mode = 'FATAL'
#     else:
#         mode = 'DEBUG'
#     print('qt_message_handler: line: %d, func: %s(), file: %s' % (
#           context.line, context.function, context.file))
#     print('  %s: %s\n' % (mode, message))