import maps.wikimap.mapspro.services.mrc.eye.experiments.speed_limits.pylibs.road_graph_consts as rg_const
import maps.wikimap.mapspro.services.mrc.eye.experiments.speed_limits.pylibs.road_graph_utils as rg_utils


def make_speed_limits_by_fc(edges):
    def _get_default_speed_limit(edge_fc):
        if (edge_fc < 8):
            return 60
        return 20

    edges_p =[]
    for edge in edges:
        edge_p = {"id": edge["id"]}
        speed_limit = _get_default_speed_limit(edge["fc"])
        if 0 != (edge["oneway"] & rg_const.DIRECTION_FORWARD):
            edge_p["speed_limit_f"] = speed_limit
            edge_p["speed_limit_truck_f"] = speed_limit
        else:
            edge_p["speed_limit_f"] = 0
            edge_p["speed_limit_truck_f"] = 0
        if 0 != (edge["oneway"] & rg_const.DIRECTION_BACKWARD):
            edge_p["speed_limit_t"] = speed_limit
            edge_p["speed_limit_truck_t"] = speed_limit
        else:
            edge_p["speed_limit_t"] = 0
            edge_p["speed_limit_truck_t"] = 0
        edges_p.append(edge_p)
    return edges_p


class SpeedLimitMarkuper(object):
    def __init__(self, edges, objects):
        self.objects = {}
        self.edges = []
        self.edges_idx = {}
        self.node_to_out_edge_ids = {}
        self.cross_road_nodes = []

        self.objects = {x["id"]: x for x in objects}
        self._copy_edges(edges)
        self._fill_node_to_edge()
        self._fill_crossroads()

    def _copy_edges(self, edges):
        self.edges = []
        self.edges_idx = {}
        for idx, edge in enumerate(edges):
            self.edges_idx[edge["id"]] = idx
            edge_p = {key: value for key, value in edge.items() if not key.startswith('speed_limit_')}
            self.edges.append(edge_p)

    def _fill_node_to_edge(self):
        self.node_id_to_out_edge_id = {}
        for edge in self.edges:
            if 0 != (edge["oneway"] & rg_const.DIRECTION_FORWARD):
                node_key = (edge["f_rd_jc_id"], edge["f_zlev"])
                if (node_key not in self.node_to_out_edge_ids):
                    self.node_to_out_edge_ids[node_key] = []
                self.node_to_out_edge_ids[node_key].append({"id": edge["id"], "forward" : True})
            if 0 != (edge["oneway"] & rg_const.DIRECTION_BACKWARD):
                node_key = (edge["t_rd_jc_id"], edge["t_zlev"])
                if (node_key not in self.node_to_out_edge_ids):
                    self.node_to_out_edge_ids[node_key] = []
                self.node_to_out_edge_ids[node_key].append({"id": edge["id"], "forward" : False})

    def _fill_crossroads(self):
        edges_cnt = {}
        for edge in self.edges:
            edges_cnt[(edge["f_rd_jc_id"], edge["f_zlev"])] = edges_cnt.get((edge["f_rd_jc_id"], edge["f_zlev"]), 0) + 1
            edges_cnt[(edge["t_rd_jc_id"], edge["t_zlev"])] = edges_cnt.get((edge["t_rd_jc_id"], edge["t_zlev"]), 0) + 1
        for node_key, cnt in edges_cnt.items():
            if (cnt > 2):
                self.cross_road_nodes.append(node_key)
                self.node_to_out_edge_ids.pop(node_key, 0)

    def _get_last_speed_limit(self, sign_ids, for_truck):
        PREFIX = "prohibitory_max_speed_"
        if (len(sign_ids) == 0):
            return None, None
        speed_limit = None
        speed_limit_obj_id = None
        last_time = rg_utils.convertSQLDateTimeToTimestamp("2000-01-01 00:00:01.00000000+00:00")
        is_truck = None
        for obj_id in sign_ids:
            if (obj_id not in self.objects):
                continue
            obj = self.objects[obj_id]
            is_truck = "information_heavy_vehicle" in obj["slaves"]
            if (for_truck != is_truck):
                continue
            sign_type = obj["sign_type"]
            to_time = rg_utils.convertSQLDateTimeToTimestamp(obj["to_time"])
            if ((PREFIX in sign_type) and (last_time < to_time)):
                speed_limit = int(sign_type[len(PREFIX):])
                speed_limit_obj_id = obj_id
                last_time = to_time
        return speed_limit, speed_limit_obj_id

    def _has_sign(self, sign_ids, for_truck):
        PREFIX_1 = "prohibitory_max_speed_"
        PREFIX_2 = "prohibitory_eof_max_speed"
        EOF_ALL_TYPE = "prohibitory_eof_all"
        if (len(sign_ids) == 0):
            return False
        last_type = None
        last_time = rg_utils.convertSQLDateTimeToTimestamp("2000-01-01 00:00:01.00000000+00:00")
        is_truck = None
        for obj_id in sign_ids:
            if (obj_id not in self.objects):
                continue
            obj = self.objects[obj_id]
            sign_type = obj["sign_type"]
            eof_all = (EOF_ALL_TYPE == sign_type)
            is_truck = "information_heavy_vehicle" in obj["slaves"]
            if (for_truck != is_truck) and (not eof_all):
                continue
            to_time = rg_utils.convertSQLDateTimeToTimestamp(obj["to_time"])
            if last_time >= to_time:
                continue
            if eof_all or (PREFIX_1 in sign_type) or (PREFIX_2 in sign_type):
                last_type = sign_type
                last_time = to_time
        return last_type is not None

    def _get_next_edge(self, edge, forward):
        next_node = (edge["t_rd_jc_id"], edge["t_zlev"]) if forward else (edge["f_rd_jc_id"], edge["f_zlev"])
        if (next_node in self.cross_road_nodes):
            return None, None

        out_edges = self.node_to_out_edge_ids.get(next_node, [])
        for out_edge in out_edges:
            if (out_edge["id"] != edge["id"]):
                return self.edges[self.edges_idx[out_edge["id"]]], out_edge["forward"]
        return None, None

    def _fill(self, edge, forward, speed_limit, obj_id, is_truck):
        key_f = "speed_limit_truck_f" if is_truck else "speed_limit_f"
        key_t = "speed_limit_truck_t" if is_truck else "speed_limit_t"
        obj_key_f = "speed_limit_truck_object_id_f" if is_truck else "speed_limit_object_id_f"
        obj_key_t = "speed_limit_truck_object_id_t" if is_truck else "speed_limit_object_id_t"
        while (True):
            if (forward):
                edge[key_f] = speed_limit
                edge[obj_key_f] = obj_id
            else:  # backward
                edge[key_t] = speed_limit
                edge[obj_key_t] = obj_id

            edge, forward = self._get_next_edge(edge, forward)
            if (edge is None):
                break
            if (forward):
                if key_f in edge:
                    break
                sign_ids = edge["sign_ids_f"]
            else:  # backward
                if key_t in edge:
                    break
                sign_ids = edge["sign_ids_t"]
            if self._has_sign(sign_ids, is_truck):
                break

    def markup(self, speed_limit_def):
        # общие ограничения скорости
        for edge in self.edges:
            if (0 != (edge["oneway"] & rg_const.DIRECTION_FORWARD)):
                speed_limit, obj_id = self._get_last_speed_limit(edge["sign_ids_f"], False)
                if speed_limit is not None:
                    self._fill(edge, True, speed_limit, obj_id, False)

            if (0 != (edge["oneway"] & rg_const.DIRECTION_BACKWARD)):
                speed_limit, obj_id = self._get_last_speed_limit(edge["sign_ids_t"], False)
                if speed_limit is not None:
                    self._fill(edge, False, speed_limit, obj_id, False)

        # ограничения скорости для грузовиков
        for edge in self.edges:
            if (0 != (edge["oneway"] & rg_const.DIRECTION_FORWARD)):
                speed_limit, obj_id = self._get_last_speed_limit(edge["sign_ids_f"], True)
                if speed_limit is not None:
                    self._fill(edge, True, speed_limit, obj_id, True)

            if (0 != (edge["oneway"] & rg_const.DIRECTION_BACKWARD)):
                speed_limit, obj_id = self._get_last_speed_limit(edge["sign_ids_t"], True)
                if speed_limit is not None:
                    self._fill(edge, False, speed_limit, obj_id, True)

        for edge in self.edges:
            if "speed_limit_f" not in edge:
                edge["speed_limit_f"] = speed_limit_def if (0 != (edge["oneway"] & rg_const.DIRECTION_FORWARD)) else 0
            if "speed_limit_truck_f" not in edge:
                edge["speed_limit_truck_f"] = edge["speed_limit_f"]

            if "speed_limit_t" not in edge:
                edge["speed_limit_t"] = speed_limit_def if (0 != (edge["oneway"] & rg_const.DIRECTION_BACKWARD)) else 0
            if "speed_limit_truck_t" not in edge:
                edge["speed_limit_truck_t"] = edge["speed_limit_t"]

        return self.edges


def make_speed_limits_by_objects(edges, objects, speed_limit_def=60):
    markuper = SpeedLimitMarkuper(edges, objects)
    return markuper.markup(speed_limit_def)
