import os
import logging
import enum
from pathlib import Path
from enum import Enum
import inspect
import sys
from typing import List


def print_e(e):
    print(str_e(e))


class AlgType(enum.IntEnum):
    RULE = 0
    MACHINE_LEARNING = 1


class AttackWay(enum.IntEnum):
    """攻击方式"""
    TargetInfo = 0
    Auto = 1  # 自动
    Manual = 2  # 手动
    Bol = 3  # 纯方位


class Throttle(Enum):
    """
    实体油门
    """
    # 不确定
    Unspecified = 0
    # 停住
    FullStop = 1
    # 低速
    Loiter = 2
    # 巡航
    Cruise = 3
    # 全速
    Full = 4
    # 加力
    Flank = 5


class LogLevel(enum.IntEnum):
    FATAL = logging.FATAL  # 致命错误
    ERROR = logging.ERROR  # 错误
    WARN = logging.WARN  # 警告
    INFO = logging.INFO  # 普通信息
    DEBUG = logging.DEBUG  # 调试信息


class SonarType(Enum):
    """# 声纳类型"""
    Passive = "PASSIVE"  # 被动
    Active = "ACTIVE"  # 主动
    Dipping = "DIPPING"  # 吊放


def str_e(e):
    _str = str("exception to str err")
    try:
        _str = str(e.__traceback__.tb_frame.f_globals['__file__']) + ":" + str(
            e.__traceback__.tb_lineno) + ":" + str(
            e.__traceback__.tb_lasti) + ":" + str(e)
    except Exception:
        _str = str(e)
    finally:
        return _str


class Constant:
    """常数设置"""
    # 归一化常数
    SPEED = 1000
    DISTANCE = 200000
    X = 200000
    Y = 200000
    Z = 15000
    ANGLE = 360
    RADAR_GAIN = 100
    RADAR_POWER = 100000
    LON = 180
    LAT = 90
    ALT = 15000
    HEAD = 360  # 待确认
    PITCH = 90  # 待确认
    ROLL = 180  # 待确认
    RADAR_MAX_RANGE = 200
    RADAR_MIN_RANGE = 10
    DISTURB_FREQUENCY = 13000000000
    AIR_RANGE_MAX = 100
    KTS_TO_KM = 1.852  # 海理转公理系数
    EARTH_RADIUS = 6371.393  # 地球半径
    PI = 3.1415926
    degree2radian = PI / 180.0


"""错误日志打印函数"""


def get_debug_logger(
        log_name: str = None,
        *,
        log_level: int = None,
        formatter: str = None,
        datefmt: int = None,
        filename: str = None,
) -> logging.Logger:
    """
    生成日志对象logger, 帮助开发过程中控制台的调试
    :param log_name: str, 日志名, 默认为 f'{__name__}.py'
    :param log_level: 日志级别
    :param formatter: log格式控制
    :param datefmt: 时间格式
    :param filename: 日志文件名
    :return: logger对象
    """
    try:
        import colorlog
    except ImportError:
        # print(
        #     f'没有<colorlog>模块, 无法使用彩色日志功能\n'
        #     f'你可以在201服务器上的home/cxh/路径下找到colorlog的安装文件\n'
        #     f'你可以选择whl安装文件, 并在终端中执行\n'
        #     f'\tpython -m pip install colorlog-4.6.2-py2.py3-none-any.whl\n'
        #     r'或者直接将colorlog文件夹粘贴到当前环境中Lib\site-packages\文件夹下')
        colorlog = None

    top_log_level_control = {}
    ## 获取当前文件的绝对路径
    WORK_DIR = Path(__file__).parent.parent.parent.parent.parent
    # 输入参数检查及配置============================================================
    if log_name is None:
        # 获取当前栈帧信息
        current_frame = inspect.currentframe()
        # 获取调用者的栈帧信息
        caller_frame = inspect.getouterframes(current_frame, 2)
        # 获取调用者所在的文件名
        caller_filename = caller_frame[1].filename  # 绝对路径
        # 相对工作目录的模块路径
        module_path = os.path.relpath(caller_filename, WORK_DIR)
        # 清理资源
        del current_frame, caller_frame
        # 举例, 'corekit/config/settings.py' -> 'corekit.config.settings.py'
        module_path = module_path.replace(os.path.sep, '.')
        # log_name = os.path.basename(caller_filename)
        log_name = module_path
    else:
        if '/' in log_name or '\\' in log_name:
            # 是一个路径名, 仅需文件名即可
            log_name = os.path.basename(log_name)
    if log_level is None:
        system = sys.platform
        if system.startswith('win'):
            log_level = top_log_level_control.get(log_name, logging.DEBUG)
        else:
            log_level = logging.DEBUG

    default_formatter: str = '%(levelname)s of %(name)s, line %(lineno)d:\t%(message)s'
    if formatter is None:
        formatter: str = default_formatter
    if isinstance(datefmt, int):
        datetime_formats = [
            '%H:%M:%S',  # 比如, 16:15:48
            '%H:%M:%S',  # 比如, 16:15:48,124
            '%Y-%m-%d %H:%M:%S',  # 比如, 2023-10-14 16:15:48
            '%Y-%m-%d %H:%M:%S',  # 比如, 2023-10-14 16:15:48,124
        ]
        time_str = [
            '%(asctime)s ',  # 比如, 16:15:48
            '%(asctime)s,%(msecs)d ',  # 比如, 16:15:48,124
            '%(asctime)s ',  # 比如, 2023-10-14 16:15:48
            '%(asctime)s,%(msecs)d ',  # 比如, 2023-10-14 16:15:48,124
        ]
        formatter = time_str[datefmt] + formatter
        datefmt: str = datetime_formats[datefmt]

    log_colors = {
        'DEBUG': 'white',  # 'cyan',
        'INFO': 'green',
        'WARNING': 'yellow',
        'ERROR': 'red',
        'CRITICAL': 'red',
    }
    # 输入参数检查及配置============================================================

    # 创建logger对象
    logger = logging.getLogger(log_name)
    logger.setLevel(logging.DEBUG)
    logger.propagate = False  # 避免消息被传给 parent logger
    # 创建控制台日志处理器
    console_handler = logging.StreamHandler()
    console_handler.setLevel(log_level)
    # 定义输出格式/颜色控制
    if colorlog is not None:
        sh = colorlog.ColoredFormatter(
            '%(log_color)s' + formatter,
            log_colors=log_colors,
            datefmt=datefmt,
        )
    else:
        sh = logging.Formatter(formatter, datefmt=datefmt)
    console_handler.setFormatter(sh, )

    # 移除默认的handler, 防止重复打印
    for handler in logger.handlers:
        logger.removeHandler(handler)  # 多线程情况下, 会打印多次, 需要多线程打印功能, 请注释这个循环
    for _ in logger.root.handlers:
        logger.root.handlers.pop()

    # 将handler添加到logger对象
    logger.addHandler(console_handler)
    # logger.root.handlers.append(console_handler)

    # logger文件输出设置
    if filename is not None:
        if filename is True:
            filename = f'log/{log_name.split(".")[0]}.log'
        if '.log' not in filename:
            filename = f'{filename}.log'
        if '\\' not in filename and '/' not in filename:  # 非路径名
            filename = f'log/{filename}'
        file_handler = logging.FileHandler(filename, encoding='utf-8')
        file_handler.setLevel(logging.DEBUG)
        file_formatter = logging.Formatter('%(acstime)s %(levelname)s: %(message)s')
        file_handler.setFormatter(file_formatter)
        logger.addHandler(file_handler)
    return logger


def get_file_paths(
        root_path, fileformat: str = '', include='', uninclude='',
        relative: bool = False, depth: int = None, verbose: bool = False
) -> List[str]:
    """获取目标目录下所有特定格式的文件路径列表
    :param root_path: str, 根目录
    :param fileformat: str, 文件格式扩展名, 例如 'json'
    :param include: str, 文件名应包含该字符串
    :param uninclude: str, 文件路径不包含该字符串, 优先级高于 include 参数
    :param relative: bool, 返回结果是否采用以 root_path 为参考的相对路径, False时为绝对路径
    :param depth: int, 查找深度, None代表无限深度
    :param verbose: bool, 是否打印查找结果
    :return: file_paths: list[str], 文件路径的列表
    """
    root_path = os.path.normpath(os.path.abspath(root_path))
    if not os.path.exists(root_path):
        raise OSError(f'目标路径{root_path}不存在')
    if not os.path.isdir(root_path):
        raise OSError(f'请输入一个文件夹路径, 当前输入的是文件路径 {root_path}')
    if isinstance(depth, int) and depth <= 0:
        return []  # 已搜索到目标深度, 直接退出
    file_paths = []

    files = os.listdir(root_path)
    new_depth = depth - 1 if isinstance(depth, int) else None
    for obj in files:
        new_path = os.path.join(root_path, obj)
        if uninclude and uninclude in new_path:
            continue  # 包含排除字符, 跳过

        if os.path.isdir(new_path):
            file_paths.extend(
                get_file_paths(new_path, fileformat=fileformat, include=include, uninclude=uninclude, depth=new_depth)
            )
        elif os.path.isfile(new_path):
            if include and include not in obj:
                continue  # 文件名不包含挑选字符, 跳过
            if fileformat and not obj.endswith(fileformat):
                continue  # 文件格式不符合要求
            file_paths.append(new_path)

    if relative:
        file_paths = [os.path.relpath(path_i, start=root_path) for path_i in file_paths]
    if verbose:
        for i in file_paths:
            try:
                print(i)
            except UnicodeDecodeError:
                pass
    return file_paths
