# -*- coding: utf-8 -*-
# @Time : 2022/5/30 14:45
# @Author : Opfer
# @Site :
# @File : dispatchInfo.py
# @Software: PyCharm
import numpy as np

from settings import *
import pandas as pd
from tables import DispatchMatch, DispatchEquipment
import bidict

logger = get_logger("zxt.dispatchInfo")


class DispatchInfo:
    """
    class for dispatch group info.
    """
    # dispatch groups
    group_set = set()

    # device group structure
    load_excavator_dict = {}  # 装载区id-->>电铲编号的映射
    excavator_load_dict = {}  # 电铲编号->>装载区id的映射

    # excavator <-> group id
    group_excavator_dict = {}  # team_id -> dict {[excavator_id, excavator_id], ...}
    excavator_group_dict = {}  # excavator_id -> team_id   问题：多个key值对应一个value值

    # unload_area <-> group id
    group_unload_area_dict = {}  # team_id -> dict {unload_area_id, unload_area_id, ...}
    unload_area_group_dict = {}  # unload_area_id -> team_id

    # dump <-> group id
    group_dump_dict = {}
    dump_group_dict = {}

    # truck_id <-> team_id
    group_truck_dict = {}  # team_id -> dict # {group_id:[truck_id,truck_id],...}
    truck_group_dict = {}  # truck_id -> team_id

    # group mode
    group_mode = {}  # 数据格式： {team_id:mode_code}

    # route_distance（路网距离）
    load_distance = {}
    unload_distance = {}

    # group_name <-> group_id
    group_name = {}

    # truck -> match(excavator_id, unload_area_id)
    truck_match_dict = {}

    # unload_area <-> dump id
    dump_unload_area_dict = {}
    unload_area_dump_dict = {}

    # 获取电铲与卡车的双映射 -->> exactor_id <-> truck_id
    exactor_truck_dict = {}
    truck_exactor_dict = {}

    # 获取电铲与卸载区的双映射 -->> exactor_id <-> unload_area_id
    exactor_unload_dict = {}
    unload_exactor_dict = {}

    # 获取卸载区与rate之间的关系 -->> unload_area_id -> rate
    unload_rate_dict = {}

    # 各分组设备映射
    excavator_uuid_to_index_dict = {}
    dump_uuid_to_index_dict = {}
    load_area_uuid_to_index_dict = {}
    unload_area_uuid_to_index_dict = {}

    @classmethod
    def reset(cls):
        """
        @date：2022/6/2 19:50
        @author：maqc
        @desc：实例化对象，可直接访问
        """
        cls.group_set = set()

        cls.load_excavator_dict = {}
        cls.excavator_load_dict = {}

        cls.group_excavator_dict = {}
        cls.excavator_group_dict = {}

        cls.group_unload_area_dict = {}
        cls.unload_area_group_dict = {}

        cls.group_dump_dict = {}
        cls.dump_group_dict = {}

        cls.group_truck_dict = {}
        cls.truck_group_dict = {}

        cls.group_mode = {}

        cls.load_distance = {}
        cls.unload_distance = {}

        cls.truck_match_dict = {}

        cls.dump_unload_area_dict = {}
        cls.unload_area_dump_dict = {}

        cls.exactor_truck_dict = {}
        cls.truck_exactor_dict = {}

        cls.exactor_unload_dict = {}
        cls.unload_exactor_dict = {}

        cls.unload_rate_dict = {}

        cls.excavator_uuid_to_index_dict = {}
        cls.dump_uuid_to_index_dict = {}
        cls.load_area_uuid_to_index_dict = {}
        cls.unload_area_uuid_to_index_dict = {}

        # cls.group_unload_area_dict = {}   # cls.group_unload_area_dict.keys() 相当于所有的team_id
        # cls.unload_area_group_dict = {}  # cls.unload_area_group_dict.keys() 相当于所有的卸载区 unload_area_id
        # cls.group_excavator_dict = {}
        # cls.excavator_group_dict = {}
        # cls.load_excavator_dict = {}  # cls.load_excavator_dict.keys() 相当于所有的装载区 load_area_id
        # cls.excavator_load_dict = {}
        # cls.group_truck_dict = {}
        # cls.truck_group_dict = {}
        # cls.group_mode = {}
        # cls.load_distance = {}  # to_load_diatance-->>空车模式
        # cls.unload_distance = {}  # to_unload_distance-->>重车模式

    @classmethod
    def update_device_group_structure(cls):
        """
        @date：2022/6/2 19:49
        @author：maqc
        @desc：分组与卸载区、挖机、矿卡的映射和反映射
        """
        logger = get_logger("zxt.update_device_group_structure")
        # update excavator_id <-> load_area_id
        try:
            for item in session_mysql.query(DispatchSetting).all():

                cls.load_excavator_dict[item.load_area_id] = item.exactor_id
                cls.excavator_load_dict[item.exactor_id] = item.load_area_id
            # aa = cls.load_excavator_dict
        except Exception as es:
            logger.error("挖机和装载区映射更新异常")
            logger.error(es)
            session_postgre.rollback()
            session_mysql.rollback()

        # update dump_id <-> unload_area_id
        try:
            for item in session_mysql.query(DispatchSetting).all():

                cls.unload_area_dump_dict[item.unload_area_id] = item.dump_id
                cls.dump_unload_area_dict[item.dump_id] = item.unload_area_id
            # aa = cls.load_excavator_dict
        except Exception as es:
            logger.error("卸载设备和卸载区映射更新异常")
            logger.error(es)
            session_postgre.rollback()
            session_mysql.rollback()

        # update  exactor_id <-> truck_id
        try:
            for item in session_mysql.query(DispatchSetting).all():
                if item.exactor_id not in cls.exactor_truck_dict.keys():
                    cls.exactor_truck_dict[item.exactor_id] = [item.truck_id]
                else:
                    if item.truck_id not in cls.exactor_truck_dict[item.exactor_id]:
                        cls.exactor_truck_dict[item.exactor_id].append(item.truck_id)
                cls.truck_exactor_dict[item.truck_id] = item.exactor_id
        except Exception as es:
            logger.error("电铲和卡车的映射关系更新异常")
            logger.error(es)

        # update   exactor_id <-> unload_area_id
        try:
            for item in session_mysql.query(DispatchSetting).all():
                if item.exactor_id not in cls.exactor_unload_dict.keys():
                    cls.exactor_unload_dict[item.exactor_id] = [item.unload_area_id]
                else:
                    if item.unload_area_id not in cls.exactor_unload_dict[item.exactor_id]:
                        cls.exactor_unload_dict[item.exactor_id].append(item.unload_area_id)
        except Exception as es:
            logger.error("电铲和卸载区的映射关系更新异常")
            logger.error(es)

        # update  unload_area_id -> rate
        try:
            for item in session_mysql.query(DispatchSetting).all():
                cls.unload_rate_dict[item.unload_area_id] = item.rate
        except Exception as es:
            logger.error("卸载区和比例值关系更新异常")
            logger.error(es)

        # update excavator_id <-> group_id && unload_area_id <-> group_id && truck_id <-> group_id
        try:
            for item in session_mysql.query(DispatchSetting).all():
                # add excavator
                if item.group_id not in cls.group_excavator_dict.keys():
                    cls.group_excavator_dict[item.group_id] = [item.exactor_id]
                else:
                    if item.exactor_id not in cls.group_excavator_dict[item.group_id]:
                        cls.group_excavator_dict[item.group_id].append(item.exactor_id)
                cls.excavator_group_dict[item.exactor_id] = item.group_id

                # add unload_area
                if item.group_id not in cls.group_unload_area_dict.keys():
                    cls.group_unload_area_dict[item.group_id] = [item.unload_area_id]
                else:
                    if item.unload_area_id not in cls.group_unload_area_dict[item.group_id]:
                        cls.group_unload_area_dict[item.group_id].append(item.unload_area_id)
                cls.unload_area_group_dict[item.unload_area_id] = item.group_id

                # add dump
                if item.group_id not in cls.group_dump_dict.keys():
                    cls.group_dump_dict[item.group_id] = [item.dump_id]
                else:
                    if item.dump_id not in cls.group_dump_dict[item.group_id]:
                        cls.group_dump_dict[item.group_id].append(item.dump_id)
                cls.dump_group_dict[item.dump_id] = item.group_id

                # add truck
                if item.group_id not in cls.group_truck_dict.keys():
                    cls.group_truck_dict[item.group_id] = [item.truck_id]
                else:
                    if item.truck_id not in cls.group_truck_dict[item.group_id]:
                        cls.group_truck_dict[item.group_id].append(item.truck_id)
                cls.truck_group_dict[item.truck_id] = item.group_id

            print("存在的分组")
            print(cls.group_excavator_dict.keys())
        except Exception as es:
            logger.error("挖机/卸载区/矿卡与group_id映射更新异常")
            logger.error(es)
            session_postgre.rollback()
            session_mysql.rollback()

        # update group set
        try:
            cls.group_set = set(cls.group_excavator_dict.keys())
        except Exception as es:
            logger.error(es)

        # update truck_id <-> match_id(excavator_id, unload_area_id)
        try:
            trucks = cls.truck_group_dict.keys()
            for i in trucks:
                match_excavator = session_mysql.query(DispatchSetting).filter_by(truck_id=i).first().exactor_id
                match_unload_area = session_mysql.query(DispatchSetting).filter_by(truck_id=i).first().unload_area_id
                cls.truck_match_dict[i] = [match_excavator, match_unload_area]
        except Exception as es:
            logger.error(es)
            session_postgre.rollback()
            session_mysql.rollback()

    @classmethod
    def update_group_mode(cls):
        """
        @date：2022/6/2 19:49
        @author：maqc
        @desc：处理其它类型的数据
        """
        # update group_id->mode_code
        logger = get_logger("zxt.update_group_mode")
        cls.group_mode = {}
        try:
            for item in session_mysql.query(DispatchGroup).all():
                mode_id = item.mode_id
                mode_code = session_mysql.query(DispatchMode).filter_by(id=mode_id).first().mode_code
                # mode = session_mysql.query(DispatchMode).filter_by(group_code=).first()
                cls.group_mode[item.id] = mode_code
        except Exception as es:
            logger.error("group_id->mode_code更新异常")
            logger.error(es)
            session_postgre.rollback()
            session_mysql.rollback()

    @classmethod
    def update_group_name(cls):
        """
        @date：2022/6/13 20:30
        @author：zxtc
        @desc：处理 group name
        """
        # update group_id->mode_name
        logger = get_logger("zxt.update_group_name")
        cls.group_name = {}
        try:
            for item in session_mysql.query(DispatchGroup).all():
                name = item.group_name
                cls.group_name[item.id] = name
        except Exception as es:
            logger.error("group_id->name更新异常")
            logger.error(es)
            session_postgre.rollback()
            session_mysql.rollback()

        logger.info("group_name")
        logger.info(cls.group_name)

    @classmethod
    # 距离-->>数据格式：矩阵-->>to_load_distance
    def update_route_distance(cls):
        """
        @date：2022/6/2 19:50
        @author：maqc
        @desc：更新路网距离，返回矩阵格式数据
        """
        logger = get_logger("zxt.update_route_distance")

        cls.excavator_uuid_to_index_dict = {}
        cls.dump_uuid_to_index_dict = {}
        cls.load_area_uuid_to_index_dict = {}
        cls.unload_area_uuid_to_index_dict = {}

        # try:
        groups = cls.group_excavator_dict.keys()
        for item in groups:
            try:

                # 每个组的 excavator_id 及对应 load_area_id
                excavator_ids = cls.group_excavator_dict[item]
                load_areas = []
                for excavator_id in excavator_ids:
                    load_areas.append(cls.excavator_load_dict[excavator_id])

                # 每个组的unload_areas
                unload_areas = cls.group_unload_area_dict[item]

            except Exception as es:
                logger.error("装卸载区统计异常")
                logger.error(es)

            group_excavator_uuid_to_index = {}
            group_dump_uuid_to_index = {}
            group_load_area_uuid_to_index = {}
            group_unload_area_uuid_to_index = {}

            try:

                # # unload->load distance
                # unload_load_distance = np.zeros((len(unload_areas), len(load_areas)))
                # for i in range(len(unload_areas)):
                #     for j in range(len(load_areas)):
                #         distance = int(session_postgre.query(WalkTime).filter_by(unload_area_id=unload_areas[i], load_area_id=load_areas[j]).first().to_load_distance)
                #         unload_load_distance[i][j] = distance
                # cls.load_distance[item] = unload_load_distance


                group_excavator_count = 0
                group_dump_count = 0
                group_load_area_count = 0
                group_unload_area_count = 0

                # unload->load distance
                unload_load_distance = np.zeros((len(cls.group_dump_dict[item]), len(cls.group_excavator_dict[item])))
                for excavator_id in cls.group_excavator_dict[item]:
                    group_dump_count = 0
                    group_unload_area_count = 0
                    for dump_id in cls.group_dump_dict[item]:
                        unload_area_id = cls.dump_unload_area_dict[dump_id]
                        load_area_id = cls.excavator_load_dict[excavator_id]
                        distance = int(session_postgre.query(WalkTime).filter_by(
                            unload_area_id=unload_area_id,
                            load_area_id=load_area_id).first().to_load_distance)

                        unload_load_distance[group_dump_count][group_excavator_count] = distance

                        if excavator_id not in group_excavator_uuid_to_index:
                            group_excavator_uuid_to_index[excavator_id] = group_excavator_count

                        if dump_id not in group_dump_uuid_to_index:
                            group_dump_uuid_to_index[dump_id] = group_dump_count

                        if load_area_id not in group_load_area_uuid_to_index:
                            group_load_area_uuid_to_index[load_area_id] = group_load_area_count

                        if unload_area_id not in group_unload_area_uuid_to_index:
                            group_unload_area_uuid_to_index[unload_area_id] = group_unload_area_count

                        cls.excavator_uuid_to_index_dict[item] = group_excavator_uuid_to_index
                        cls.dump_uuid_to_index_dict[item] = group_dump_uuid_to_index
                        cls.load_area_uuid_to_index_dict[item] = group_load_area_uuid_to_index
                        cls.unload_area_uuid_to_index_dict[item] = group_unload_area_uuid_to_index

                        group_dump_count += 1
                        group_unload_area_count += 1

                    group_excavator_count += 1
                    group_load_area_count += 1

                cls.load_distance[item] = unload_load_distance

            except Exception as es:
                logger.warning(f'{item} 分组装载路网异常')
                cls.load_distance[item] = np.full((len(unload_areas), len(load_areas)), 10000)
                logger.warning(es)
                session_postgre.rollback()
                session_mysql.rollback()

            try:

                # load->unload distance
                load_unload_distance = np.zeros((len(load_areas), len(unload_areas)))
                for i in range(len(load_areas)):
                    for j in range(len(unload_areas)):
                        distance = int(session_postgre.query(WalkTime).filter_by(load_area_id=load_areas[i], unload_area_id=unload_areas[j]).first().to_unload_distance)
                        load_unload_distance[i][j] = distance
                cls.unload_distance[item] = load_unload_distance

            except Exception as es:
                logger.warning(f'{item} 分组卸载路网异常')
                cls.unload_distance[item] = np.full((len(load_areas), len(unload_areas)), 10000)
                logger.warning(es)
                session_postgre.rollback()
                session_mysql.rollback()

        # except Exception as es:
        #     logger.error("路网距离更新异常-调度部分和路网部分不一致")
        #     logger.error(es)

    @classmethod
    def get_all_group(cls):
        return set(cls.group_excavator_dict.keys())

    @classmethod
    def get_group_mode(cls, group_id):
        return cls.group_mode[group_id]

    @classmethod
    def get_excavator(cls, group_id):
        return set(cls.group_excavator_dict[group_id])

    @classmethod
    def get_unload_area(cls, group_id):
        return set(cls.group_unload_area_dict[group_id])

    @classmethod
    def get_dump(cls, group_id):
        return set(cls.group_dump_dict[group_id])

    @classmethod
    def get_truck_set(cls, group_id):
        return set(cls.group_truck_dict[group_id])

    @classmethod
    def get_load_area(cls, excavator_id):
        return cls.excavator_load_dict[excavator_id]

    @classmethod
    def get_to_excavator_distance(cls, group_id):
        return cls.load_distance[group_id]

    @classmethod
    def get_to_unload_area_distance(cls, group_id):
        return cls.unload_distance[group_id]

    @classmethod
    def get_truck_match(cls, truck_id):
        return cls.truck_match_dict[truck_id]

    @classmethod
    def get_park_to_excavator_distance(cls, group_id):
        excavators = cls.group_excavator_dict[group_id]
        park_matrix = np.ones((1, len(excavators)))
        try:
            b = excavators
            for i in range(len(excavators)):
                load_area_id = cls.excavator_load_dict[excavators[i]]
                try:
                    distance = session_postgre.query(WalkTimePark).filter_by(load_area_id=load_area_id).first().park_load_distance
                    park_matrix[0][i] = distance
                except Exception as es:
                    logger.warning("查询不到距离，设置为100000000")
                    logger.warning(es)
                    park_matrix[0][i] = 100000000   # 设置为一个很大的数字
        except Exception as es:
            logger.error("park->load距离更新异常, 存在备停区不可达的装载区")
            logger.error(es)
            session_postgre.rollback()
            session_mysql.rollback()
        return park_matrix

    @classmethod
    def get_truck_exactor(cls, truck_id):
        return cls.truck_exactor_dict[truck_id]

    @classmethod
    def get_exactor_truck_nums(cls, exactor_id):
        return len(cls.exactor_truck_dict[exactor_id])

    @classmethod
    def get_exactor_unload_match(cls, exactor_id):
        return cls.exactor_unload_dict[exactor_id]


# #!E:\Pycharm Projects\Waytous
# # -*- coding: utf-8 -*-
# # @Time : 2022/5/30 14:45
# # @Author : Opfer
# # @Site :
# # @File : dispatchInfo.py
# # @Software: PyCharm
# from settings import *
#
# logger = get_logger("mqc.dispatchInfo")
#
#
# class DispatchInfo:
#     """
#     class for dispatch group info.
#     """
#
#     # dispatch groups
#     group_set = set()
#
#     # device group structure
#     group_unload_area_dict = {}  # team_id -> dict {unload_area_id, unload_area_id, ...}
#     # {group_id:unload_area_id,...}
#     group_excavator_dict = {}  # team_id -> dict {[excavator_id, load_area_id], ...}
#     # {group_id: [excavator_id, unload_area_id], ...}
#     group_truck_dict = {}  # team_id -> dict {truck_id, ...}
#     # {group_id:[truck_id,truck_id],...}
#
#     unload_area_group_dict = {}  # unload_area_id -> team_id
#     excavator_group_dict = {}  # excavator_id -> team_id
#     truck_group_dict = {}  # truck_id -> team_id
#
#     # group feature
#     group_mode = {}  # 四种模式：全智能、空车智能、定铲定铲和分流配比模式
#     # {mode_name:mode_code}
#
#     # route_distance（路网距离）
#     load_excavator_distance = {}  # 装载区-挖机（电铲）的距离  数据格式 -->> {load_area_id:[excavator_uuid, to_load_distance],}
#     unload_excavator_distance = {}  # 卸载区-挖机（电铲）的距离 数据格式 -->> {unload_area_id:[excavator_uuid, to_unload_distance],}
#
#     @classmethod
#     def renew_set(cls):
#         """
#         @date：2022/6/2 19:50
#         @author：maqc
#         @desc：实例化对象，可直接访问
#         """
#         cls.group_unload_area_dict = {}
#         cls.unload_area_group_dict = {}
#
#         cls.group_excavator_dict = {}
#         cls.excavator_group_dict = {}
#
#         cls.group_truck_dict = {}
#         cls.truck_group_dict = {}
#
#         cls.group_mode = {}
#         cls.load_excavator_distance = {}
#         cls.unload_excavator_distance = {}
#
#     @classmethod
#     def update_device_group_structure(cls):
#         """
#         @date：2022/6/2 19:49
#         @author：maqc
#         @desc：分组与卸载区、挖机、矿卡的映射和反映射
#         """
#         logger = get_logger("mqc.update_device_group_structure")
#         try:
#             # group_unload_area_dict = {} -->> {group_id:unload_area_id,...}
#             # unload_area_group_dict = {}  # unload_area_id -> team_id
#             group_code_dict = {}  # group_code-->>team_id的映射
#             for valve in session_mysql.query(DispatchMatch).filter_by(group_type=1).all():
#                 group_code_dict[valve.group_code] = valve.team_id
#                 if valve.team_id not in cls.group_unload_area_dict:
#                     cls.group_unload_area_dict[valve.team_id] = [valve.unload_area_id]  # 注意：一个team_id可能对应多个unload_area_id
#                 else:
#                     cls.group_unload_area_dict[valve.team_id].append(valve.unload_area_id)
#                     cls.unload_area_group_dict[valve.unload_area_id] = valve.team_id  # 一个unload_area_id只对应一个team_id
#             # group_excavator_dict = {} -->>  {group_id: [excavator_id, unload_area_id], ...}  # 一个team_id对应一组excavator_id, unload_area_id
#             # excavator_group_dict = {} -->>  excavator_id -> team_id  一个excavator_id只对应一个team_id
#             for item in session_mysql.query(DispatchMatch).filter_by(group_type=1).all():
#                 # cls.group_excavator_dict[item.team_id] = [item.excavator_id, item.unload_area_id]
#                 cls.group_excavator_dict[item.team_id] = item.excavator_id
#                 cls.excavator_group_dict[item.excavator_id] = item.team_id
#             # group_truck_dict = {} -->> {group_id:[truck_id,truck_id],...}
#             # truck_group_dict = {} -->> truck_id -> team_id
#             for key in session_mysql.query(DispatchEquipment).filter_by(group_type=1).all():
#                 if key.group_code in group_code_dict:
#                     if key.group_code not in cls.group_truck_dict:
#                         cls.group_truck_dict[key.group_code] = [key.equipment_id]
#                     else:
#                         cls.group_truck_dict[key.group_code].append(key.equipment_id)
#                     cls.truck_group_dict[key.equipment_id] = group_code_dict[key.group_code]
#         except Exception as es:
#             logger.error("分组到卸载区的数据更新异常")
#             logger.error(es)
#
#     @classmethod
#     def update_group_mode(cls):
#         """
#         @date：2022/6/2 19:49
#         @author：maqc
#         @desc：处理其它类型的数据
#         """
#         logger = get_logger("mqc.update_group_mode")
#         try:
#             for value in session_mysql.query(DispatchMode).all():
#                 cls.group_mode[value.mode_name] = value.mode_code
#         except Exception as es:
#             logger.error("派车模式数据更新异常")
#             logger.error(es)
#
#     @classmethod
#     def dispatch_group_init(cls):
#         """
#         update basic paras (group_set, dict, num ...)
#         :return:
#         """
#         pass
#
#
#     @classmethod
#     def get_group_road_info(cls,group_id, truck_info):
#         if truck_info == 1:
#             for i in group_excavator():
#                 pass
#                 for j in group_unload_area():
#                     pass
#         if truck_info == 2:
#             pass
#     @classmethod
#     # 距离-->>数据格式：矩阵-->>to_load_distance
#     def update_route_distance(cls):
#         """
#         @date：2022/6/2 19:50
#         @author：maqc
#         @desc：更新路网距离，返回矩阵格式数据
#         """
#         load_excavator_dict = {}  # 装载区id-->>电铲编号的映射
#         logger = get_logger("mqc.update_route_distance")
#         try:
#             for value in session_postgre.query(DiggingWorkArea).all():
#                 load_excavator_dict[value.Id] = value.Exactorld
#             for value in session_postgre.query(WalkTime).all():
#                 if value.load_area_id in load_excavator_dict:
#                     cls.load_excavator_distance[value.load_area_id] = [load_excavator_dict[value.load_area_id],
#                                                                        value.to_load_distance]
#                     cls.unload_excavator_distance[value.unload_area_id] = [load_excavator_dict[value.load_area_id],
#                                                                            value.to_unload_distance]
#         except Exception as es:
#             logger.error("路网数据更新异常")
#             logger.error(es)
#
#



