#!E:\Pycharm Projects\Waytous
# -*- coding: utf-8 -*-
# @Time : 2022/5/30 15:35
# @Author : Opfer
# @Site :
# @File : group.py    
# @Software: PyCharm


#!E:\Pycharm Projects\Waytous
# -*- coding: utf-8 -*-
# @Time : 2022/5/30 15:35
# @Author : Opfer
# @Site :
# @File : group.py
# @Software: PyCharm

from data.dispatchInfo import *
from bidict import bidict
from alg.algorithm import AlgorithmBase
import numpy as np
# from settings import get_logger
from para_config import get_value

class CurrentTruck:
    """ class for the information of current dispatching truck.

    Description:
        当前请求调度卡车信息

    Attribute:
        truck_id(uuid)
        group_id(uuid)
        trip(list)
        task(int)

    """

    def __init__(self, truck_id, group_id, trip, task):
        self._truck_id = truck_id
        self._group_id = group_id
        self._trip = trip
        self._task = task

    def get_truck_id(self):
        return self._truck_id

    def get_trip(self):
        return self._trip

    def get_task(self):
        return self._task

    def get_group_id(self):
        return self._group_id


class Group:
    """
    class for group instance.
    """
    def __init__(self, group_id, truck, pre_sch):
        """ Generate a group obj.
        :param group_id: (uuid) group_id
        """
        self.logger = get_logger("ga.Group")
        # basic info.
        self.group_id = group_id
        self.group_mode = 1
        self.truck = truck
        self.pre_sch = pre_sch

        # group devices
        self.excavator = {}    # excavator_id -> unload_area_id
        self.unload_area = {}    # unload_area_id -> load_area_id
        self.dump = {}
        self.truck_set = set()    # truck_id


        # road network info.
        self.to_excavator_distance = None
        self.to_unload_area_distance = None
        self.park_to_excavator_distance = None

        # self.to_excavator_congestion = None
        # self.to_unload_area_congestion = None
        # self.park_to_excavator_congestion = None

        # device map
        self.truck_uuid_index_dict = bidict()
        self.excavator_uuid_index_dict = bidict()
        self.unload_area_uuid_index_dict = bidict()
        self.dump_uuid_index_dict = bidict()

        # truck info.
        self.truck_info_list = {}

    def update_group_mode(self):
        """
        update group mode.
        :param group_mode:
        :return:
        """
        # DispatchInfo.update_group_mode()
        self.group_mode = 1
        self.group_mode = DispatchInfo.get_group_mode(self.group_id)

    def update_group_device(self):
        """
        update group devices.
        :return:
        """
        # update group devices
        # DispatchInfo.update_device_group_structure()
        self.excavator = {}
        self.unload_area = {}
        self.dump = {}
        self.truck_set = set()
        self.excavator = DispatchInfo.get_excavator(self.group_id)
        self.unload_area = DispatchInfo.get_unload_area(self.group_id)
        self.dump = DispatchInfo.get_dump(self.group_id)
        self.truck_set = DispatchInfo.get_truck_set(self.group_id)

    def update_group_road_network(self):
        """
        update group road network.
        :return:
        """
        # DispatchInfo.update_route_distance()
        self.to_excavator_distance = None
        self.to_unload_area_distance = None
        self.park_to_excavator_distance = None
        self.to_excavator_distance = DispatchInfo.get_to_excavator_distance(self.group_id)
        self.to_unload_area_distance = DispatchInfo.get_to_unload_area_distance(self.group_id)
        self.park_to_excavator_distance = DispatchInfo.get_park_to_excavator_distance(self.group_id)

    def update_group_device_map(self):
        """
        update group device map.
        :return:
        """
        # excavator_index = 0

        self.excavator_uuid_index_dict = {}
        self.unload_area_uuid_index_dict = {}
        self.truck_uuid_index_dict = {}
        self.dump_uuid_index_dict = {}

        for i in range(len(self.excavator)):
            self.excavator_uuid_index_dict[list(self.excavator)[i]] = i

        for i in range(len(self.unload_area)):
            self.unload_area_uuid_index_dict[list(self.unload_area)[i]] = i

        for i in range(len(self.dump)):
            self.dump_uuid_index_dict[list(self.dump)[i]] = i

        for i in range(len(self.truck_set)):
            self.truck_uuid_index_dict[list(self.truck_set)[i]] = i

        self.excavator_uuid_index_dict = bidict(self.excavator_uuid_index_dict)
        self.unload_area_uuid_index_dict = bidict(self.unload_area_uuid_index_dict)
        self.truck_uuid_index_dict = bidict(self.truck_uuid_index_dict)
        self.dump_uuid_index_dict = bidict(self.dump_uuid_index_dict)

        # group_excavator_dict = {group_1: {excavator_1: load_area_1}, group_2: {excavator_2: load_area_2}}

    def info_update(self):
        """
        update group info.
        :return:
        """
        self.update_group_mode()
        self.update_group_device()
        self.update_group_device_map()
        self.update_group_road_network()

    def group_dispatch(self, solver: object):
        """
        Receive a alg obj. and output dispatch plan for trucks in this group.
        :param solver:
        :return:
            dispatch plan: Dict({truck_id: match_id})
        """
        truck_dispatch = {}
        self.truck_info_list = {}

        assert issubclass(solver, AlgorithmBase)
        s = solver(self, self.truck, self.pre_sch)  # algorithm init

        for i in list(self.truck_set):
            truck_trip = self.truck.get_truck_current_trip()[self.truck.truck_uuid_to_index_dict[i]]
            truck_task = self.truck.get_truck_current_task()[i]
            truck_info = CurrentTruck(i, self.group_id, truck_trip, truck_task)
            self.truck_info_list[i] = truck_info
            # 全智能模式
            if self.group_mode == 1:
                if truck_task in [-2, 3, 4, 5]:
                    try:

                        next_excavator_value = s.solve(truck_info)
                        # next_excavator_value = s.solve(i, truck_task, truck_trip)
                        # min_index = next_excavator_list.index(min(next_excavator_list))
                        min_index = np.argmin(next_excavator_value)
                        next_excavator_id = self.excavator_uuid_index_dict.inverse[min_index]
                        if truck_task == -2:
                            next_unload_area_id = "Park"
                        else:
                            dump_id = get_value("dump_index_to_uuid_dict")[truck_trip[-1]]
                            next_unload_area_id = get_value("dump_uuid_to_unload_area_uuid_dict")[dump_id]
                        truck_dispatch[i] = [next_excavator_id, next_unload_area_id]

                    except Exception as es:
                        self.logger.error("重载车辆全智能模式-计算异常")
                        self.logger.error(es)

                if truck_task in [0, 1, 2]:
                    try:

                        next_unload_area_value = s.solve(truck_info)
                        min_index = np.argmin(next_unload_area_value)
                        next_unload_area_id = self.unload_area_uuid_index_dict.inverse[min_index]
                        # next_excavator_id = self.excavator_uuid_index_dict.inverse[truck_trip[-1]]
                        next_excavator_id = get_value("excavator_uuid_index_dict")[truck_trip[-1]]
                        truck_dispatch[i] = [next_excavator_id, next_unload_area_id]

                    except Exception as es:
                        self.logger.error("空载车辆全智能模式-计算异常")
                        self.logger.error(es)
                        truck_dispatch[i] = [None, None]
            # 空车智能模式
            elif self.group_mode == 2:
                if truck_task in [-2, 3, 4, 5]:
                    try:

                        next_excavator_value = s.solve(truck_info)
                        # min_index = next_excavator_list.index(min(next_excavator_list))
                        min_index = np.argmin(next_excavator_value)
                        next_excavator_id = self.excavator_uuid_index_dict.inverse[min_index]
                        if truck_task == -2:
                            next_unload_area_id = "Park"
                        else:
                            # next_unload_area_id = self.unload_area_uuid_index_dict.inverse[truck_trip[-1]]
                            dump_id = get_value("dump_index_to_uuid_dict")[truck_trip[-1]]
                            next_unload_area_id = get_value("dump_uuid_to_unload_area_uuid_dict")[dump_id]
                        truck_dispatch[i] = [next_excavator_id, next_unload_area_id]

                    except Exception as es:
                        self.logger.error("重载车辆空车智能模式-计算异常")
                        self.logger.error(es)
                if truck_task in [0, 1, 2]:
                    try:
                        truck_dispatch[i] = DispatchInfo.get_truck_match(i)
                    except Exception as es:
                        self.logger.error("空载车辆空车智能模式-计算异常")
                        self.logger.error(es)
            # 定铲派车
            elif self.group_mode == 3:
                try:
                    truck_dispatch[i] = DispatchInfo.get_truck_match(i)
                except Exception as es:
                    self.logger.error("固定派车-计算异常")
                    self.logger.error(es)


            elif self.group_mode == 4:
                pass

        # return dispatch plan
        return truck_dispatch
