# -*- coding: utf-8 -*-
from traceback import format_exc

from GPUtil import getAvailable, getGPUs
from loguru import logger
from torch.cuda import is_available

from enums.ExceptionEnum import ExceptionType
from exception.CustomerException import ServiceException


# order- 确定返回可用 GPU 设备 ID 的顺序。order应指定为以下字符串之一：
# 'first'- 按升序排列可用的 GPU 设备 ID（默认）
# 'last'- 按 id 降序排列可用的 GPU 设备 id
# 'random'- 随机订购可用的 GPU 设备 ID
# 'load'- 按负载递增排序可用的 GPU 设备 ID
# 'memory'- 通过升序内存使用来排序可用的 GPU 设备 ID
# limit- 将返回的 GPU 设备 ID 数量限制为指定数量。必须是正整数。（默认 = 1）
# maxLoad- 被认为可用的 GPU 的最大当前相对负载。负载大于 的 GPUmaxLoad不会返回。（默认 = 0.5）
# maxMemory- 被视为可用的 GPU 的最大当前相对内存使用量。maxMemory不返回当前内存使用量大于的 GPU 。（默认 = 0.5）
# includeNan- 真/假标志，指示是否包括负载或内存使用为 NaN 的 GPU（指示无法检索使用情况）。（默认 = 假）
# excludeID- ID 列表，应从可用 GPU 列表中排除。见GPU类描述。（默认 = []）
# excludeUUIDexcludeID-除了它使用 UUID 之外，其他相同。（默认 = []）
# 输出
# deviceIDs - 所有可用 GPU 设备 ID 的列表。如果当前负载和内存使用量分别小于maxLoad和maxMemory，则认为 GPU 可用。该列表是根据 排序的order。返回的设备 ID 的最大数量由 限制limit。
def get_gpu_ids():
    deviceIDs = getAvailable(maxLoad=0.80, maxMemory=0.80)
    return deviceIDs


def get_all_gpu_ids():
    return getGPUs()


def get_first_gpu_name():
    gps = get_all_gpu_ids()
    if len(gps) == 0:
        raise ServiceException(ExceptionType.NO_GPU_RESOURCES.value[0],
                               ExceptionType.NO_GPU_RESOURCES.value[1])
    return gps[0].name


def check_gpu_resource(requestId=None):
    gpu_ids = get_gpu_ids()
    if len(gpu_ids) == 0 or 0 not in gpu_ids:
        print_gpu_status(requestId)
        raise ServiceException(ExceptionType.NO_RESOURCES.value[0],
                               ExceptionType.NO_RESOURCES.value[1])
    return gpu_ids


def print_gpu_ex_status(requestId=None):
    result = False
    try:
        gpu_ids = get_gpu_ids()
        if len(gpu_ids) == 0 or 0 not in gpu_ids:
            result = True
            print_gpu_status(requestId)
    except Exception:
        logger.error("打印gpu状态异常: {}", format_exc())
    return result


def print_gpu_status(requestId=None):
    try:
        GPUs = get_all_gpu_ids()
        if len(GPUs) == 0:
            return
        for gpu in GPUs:
            if requestId:
                logger.info("""############################################################################################
                GPU ID:{}, GPU 名称:{}, 负载率:{}, 内存使用率:{}, 总内存:{}, 占用内存:{}, 空闲内存:{}, requestId:{}
                ############################################################################################""", gpu.id,
                            gpu.name, gpu.load * 100, gpu.memoryUtil * 100, gpu.memoryTotal, gpu.memoryUsed, gpu.memoryFree,
                            requestId)
            else:
                logger.info("""############################################################################################
                GPU ID:{}, GPU 名称:{}, 负载率:{}, 内存使用率:{}, 总内存:{}, 占用内存:{}, 空闲内存:{}
                ############################################################################################""", gpu.id,
                            gpu.name, gpu.load * 100, gpu.memoryUtil * 100, gpu.memoryTotal, gpu.memoryUsed, gpu.memoryFree)
    except Exception:
        logger.error("打印gpu状态异常: {}", format_exc())


def check_cude_is_available():
    if not is_available():
        raise Exception("cuda不在活动状态, 请检测显卡驱动是否正常！！！！")
