
import logging
import colorama
import coloredlogs
from .settings import *


CRITICAL = 0 ### logs only fatal errors (not handled)
ERROR = 1 ### logs only errors
WARNING = 2 ### logs also warnings
INFO = 3 ### logs also information
DEBUG = 4 ### logs also debug information
EXCEPTION = 5 ### logs also unhandled exceptions
PACKAGE = 6 ### also sets built-in logger level to logging.INFO
FULL = 7 ### also sets built-in logger level to logging.DEBUG


FIELD_STYLES = dict(
    asctime=dict(color='green', faint=True, bold=BOLD_LOGS),
    levelname=dict(color='cyan', faint=True, bold=BOLD_LOGS),
)

LEVEL_STYLES = dict(
    debug=dict(color='white', faint=True, bold=BOLD_LOGS),
    info=dict(color='white', bold=BOLD_LOGS),
    warning=dict(color='yellow', bold=BOLD_LOGS),
    error=dict(color='red', bold=BOLD_LOGS),
    critical=dict(color='red', bold=BOLD_LOGS)
)

### to control logging verbosity of external libraries
verbosity_map = {
    CRITICAL : logging.CRITICAL,
    ERROR : logging.ERROR,
    WARNING : logging.WARNING,
    INFO : logging.WARNING,
    DEBUG : logging.WARNING,
    EXCEPTION : logging.WARNING,
    PACKAGE : logging.INFO,
    FULL : logging.DEBUG,
}

level_name_map = {
    'CRITICAL': CRITICAL,
    'ERROR': ERROR,
    'WARNING': WARNING,
    'INFO': INFO,
    'DEBUG': DEBUG,
    'EXCEPTION': EXCEPTION,
    'PACKAGE': PACKAGE,
    'FULL': FULL,
}


def get_user_data_dir(appname):
    from pathlib import Path
    import os
    if WIN_PLATFORM:
        import winreg
        key = winreg.OpenKey(
            winreg.HKEY_CURRENT_USER,
            r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
        )
        dir_,_ = winreg.QueryValueEx(key, "Local AppData")
        result = Path(dir_).resolve(strict=False)
    elif MAC_PLATFORM:
        result = Path('~/Library/Application Support/').expanduser()
    elif NIX_PLATFORM:
        result = Path(os.getenv('XDG_DATA_HOME', "~/.local/share")).expanduser()
    else:
        raise NotImplementedError
    os.makedirs(result.joinpath(appname, 'logs'), exist_ok=True)
    return result.joinpath(appname, 'logs')


def get_log_file_name():
    from datetime import datetime
    filename = datetime.now().strftime('%Y-%m-%d.log')
    return os.path.join(get_user_data_dir('dso'), filename) 


class LoggerClass():

    def get_log_format(self):
        log_format = ''
        if TIMESTAMP_LOGS: log_format += '%(asctime)s'
        if LABEL_LOG_LEVELS: log_format += ' [%(levelname)-8s]'
        log_format += ' %(message)s'
        return log_format

    def __init__(self, level):
        self.level = level
        logging.basicConfig(format=self.get_log_format(), datefmt=LOG_TIME_FORMAT, level=self.mapped_level, force=True)
        self.logger = logging.getLogger('dso')
        self.logger.setLevel(logging.DEBUG)
        #### Add file handler to capture all logs, regardless of verbosity level
        fh = logging.FileHandler(get_log_file_name())
        fh.setLevel(logging.DEBUG)
        fh.setFormatter(logging.Formatter(self.get_log_format(), LOG_TIME_FORMAT))
        self.logger.addHandler(fh)
        ### also add it to the root to capture messages from other libraries as well
        self.logger.root.addHandler(fh)
        if COLORED_LOGS:        
            ### set level always to maximum (as the level is controlled by mapped_level), and attach it to root logger
            coloredlogs.install(logger=logging.root, 
                                level='DEBUG', 
                                fmt=self.get_log_format(),
                                level_styles=LEVEL_STYLES,
                                field_styles=FIELD_STYLES,
                                )
        else:
            ch = logging.StreamHandler()
            ch.setLevel(logging.DEBUG)
            ch.setFormatter(logging.Formatter(self.get_log_format(), LOG_TIME_FORMAT))
            self.logger.addHandler(ch)
            ### also add it to the root to capture messages from other libraries as well
            self.logger.root.addHandler(ch)

    ### maps dso log verbosity to internal verbosity
    def _map_level(self, level):
        return verbosity_map[level]

    @property
    def mapped_level(self):
        return self._map_level(self.level)

    def set_verbosity(self,level):
        self.level = level
        logging.root.setLevel(self.mapped_level)

    def critical(self, msg, force=True):
        self.logger.propagate = force or self.level >= CRITICAL
        self.logger.critical(msg)

    def error(self, msg, force=True):
        self.logger.propagate = force or self.level >= ERROR
        self.logger.error(msg)

    def warn(self, msg, force=False):
        self.logger.propagate = force or self.level >= WARNING
        self.logger.warning(msg)

    def info(self, msg, force=False):
        self.logger.propagate = force or self.level >= INFO
        self.logger.info(msg)

    def debug(self, msg, force=False):
        self.logger.propagate = force or self.level >= DEBUG
        self.logger.debug(msg)

Logger = LoggerClass(level_name_map[DEFAULT_LOG_LEVEL])

