/// <summary>
        /// Calculates for each segment the number of cars going to this segment.
        /// We use integer arithmetic for better performance.
        /// </summary>
        public Dictionary <ushort, uint> GetVehicleMetricGoingToSegment(bool includeStopped = true, byte?laneIndex = null, bool debug = false)
        {
            VehicleManager      vehicleManager  = Singleton <VehicleManager> .instance;
            NetManager          netManager      = Singleton <NetManager> .instance;
            VehicleStateManager vehStateManager = VehicleStateManager.Instance;

            Dictionary <ushort, uint> ret = includeStopped ? numVehiclesGoingToSegmentId : numVehiclesFlowingToSegmentId;

            foreach (SegmentEndGeometry endGeo in NodeGeometry.Get(NodeId).SegmentEndGeometries)
            {
                if (endGeo == null)
                {
                    continue;
                }

                if (!endGeo.IncomingOneWay && !ret.ContainsKey(endGeo.SegmentId))
                {
#if DEBUG
                    Log._Debug($"SegmentEnd.GetVehicleMetricGoingToSegment: return dict does not contain entry for segment {endGeo.SegmentId}");
#endif
                }


                ret[endGeo.SegmentId] = 0;
            }

#if DEBUGMETRIC
            if (debug)
            {
                Log._Debug($"GetVehicleMetricGoingToSegment: Segment {SegmentId}, Node {NodeId}. Target segments: {string.Join(", ", ret.Keys.Select(x => x.ToString()).ToArray())}");
            }
#endif

            ushort vehicleId    = FirstRegisteredVehicleId;
            int    numProcessed = 0;
            while (vehicleId != 0)
            {
                VehicleState state = vehStateManager._GetVehicleState(vehicleId);

                bool breakLoop = false;

                state.ProcessCurrentAndNextPathPosition(ref Singleton <VehicleManager> .instance.m_vehicles.m_buffer[vehicleId], delegate(ref Vehicle vehState, ref PathUnit.Position curPos, ref PathUnit.Position nextPos) {
                    if (!state.CheckValidity(ref vehState))
                    {
                        RequestCleanup();
                        return;
                    }

#if DEBUGMETRIC2
                    if (debug)
                    {
                        Log._Debug($" GetVehicleMetricGoingToSegment: Checking vehicle {vehicleId}");
                    }
#endif

                    if (!ret.ContainsKey(nextPos.m_segment))
                    {
#if DEBUGMETRIC2
                        if (debug)
                        {
                            Log._Debug($"  GetVehicleMetricGoingToSegment: ret does not contain key for target segment {nextPos.m_segment}");
                        }
#endif
                        return;
                    }

                    if (!includeStopped && vehState.GetLastFrameVelocity().sqrMagnitude < TrafficPriorityManager.MAX_SQR_STOP_VELOCITY)
                    {
#if DEBUGMETRIC2
                        if (debug)
                        {
                            Log._Debug($"  GetVehicleMetricGoingToSegment: Vehicle {vehicleId}: too slow");
                        }
#endif
                        ++numProcessed;
                        return;
                    }

                    if (laneIndex != null && curPos.m_lane != laneIndex)
                    {
#if DEBUGMETRIC2
                        if (debug)
                        {
                            Log._Debug($"  GetVehicleMetricGoingToSegment: Vehicle {vehicleId}: Lane index mismatch (expected: {laneIndex}, was: {curPos.m_lane})");
                        }
#endif
                        return;
                    }

                    if (Options.simAccuracy <= 2)
                    {
                        uint avgSegmentLength = (uint)netManager.m_segments.m_buffer[SegmentId].m_averageLength;
                        uint normLength       = 100u;
                        if (avgSegmentLength > 0)
                        {
                            normLength = Math.Min(100u, (uint)(state.TotalLength * 100u) / avgSegmentLength);
                        }

#if DEBUGMETRIC
                        if (debug)
                        {
                            Log._Debug($"  GetVehicleMetricGoingToSegment: NormLength of vehicle {vehicleId}: {avgSegmentLength} -> {normLength}");
                        }
#endif

                        ret[nextPos.m_segment] += normLength;
                    }
                    else
                    {
                        ret[nextPos.m_segment] += 10;
                    }

                    ++ret[nextPos.m_segment];
                    ++numProcessed;

                    if ((Options.simAccuracy >= 3 && numProcessed >= 3) || (Options.simAccuracy == 2 && numProcessed >= 5) || (Options.simAccuracy == 1 && numProcessed >= 10))
                    {
                        breakLoop = true;
                        return;
                    }

#if DEBUGMETRIC2
                    if (debug)
                    {
                        Log._Debug($"  GetVehicleMetricGoingToSegment: Vehicle {vehicleId}: *added*! Coming from segment {SegmentId}, lane {laneIndex}. Going to segment {nextPos.m_segment}, lane {nextPos.m_lane}");
                    }
#endif
                });

                if (breakLoop)
                {
                    break;
                }

                vehicleId = state.NextVehicleIdOnSegment;
            }

#if DEBUGMETRIC
            if (debug)
            {
                Log._Debug($"GetVehicleMetricGoingToSegment: Calculation completed. {string.Join(", ", ret.Select(x => x.Key.ToString() + "=" + x.Value.ToString()).ToArray())}");
            }
#endif
            return(ret);
        }
        /// <summary>
        /// Recalculates lane arrows based on present lane connections.
        /// </summary>
        /// <param name="laneId"></param>
        /// <param name="nodeId"></param>
        private void RecalculateLaneArrows(uint laneId, ushort nodeId, bool startNode)
        {
#if DEBUGCONN
            Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}) called");
#endif
            if (!Flags.mayHaveLaneArrows(laneId, startNode))
            {
#if DEBUGCONN
                Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): lane {laneId}, startNode? {startNode} must not have lane arrows");
#endif
                return;
            }

            if (!HasConnections(laneId, startNode))
            {
#if DEBUGCONN
                Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): lane {laneId} does not have outgoing connections");
#endif
                return;
            }

            if (nodeId == 0)
            {
#if DEBUGCONN
                Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): invalid node");
#endif
                return;
            }

            Flags.LaneArrows arrows = Flags.LaneArrows.None;

            NetManager netManager = Singleton <NetManager> .instance;
            ushort     segmentId  = netManager.m_lanes.m_buffer[laneId].m_segment;

            if (segmentId == 0)
            {
#if DEBUGCONN
                Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): invalid segment");
#endif
                return;
            }

#if DEBUGCONN
            Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): startNode? {startNode}");
#endif

            NodeGeometry nodeGeo = NodeGeometry.Get(nodeId);
            if (!nodeGeo.IsValid())
            {
#if DEBUGCONN
                Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): invalid node geometry");
#endif
                return;
            }

            SegmentGeometry segmentGeo = SegmentGeometry.Get(segmentId);
            if (!segmentGeo.IsValid())
            {
#if DEBUGCONN
                Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): invalid segment geometry");
#endif
                return;
            }

            ushort[] connectedSegmentIds = segmentGeo.GetConnectedSegments(startNode);
            if (connectedSegmentIds == null)
            {
#if DEBUGCONN
                Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): connectedSegmentIds is null");
#endif
                return;
            }

            foreach (ushort connectedSegmentId in connectedSegmentIds)
            {
                if (connectedSegmentId == 0)
                {
                    continue;
                }
                Direction dir = segmentGeo.GetDirection(connectedSegmentId, startNode);

#if DEBUGCONN
                Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {connectedSegmentId}. dir={dir}");
#endif

                // check if arrow has already been set for this direction
                switch (dir)
                {
                case Direction.Turn:
                default:
                    continue;

                case Direction.Forward:
                    if ((arrows & Flags.LaneArrows.Forward) != Flags.LaneArrows.None)
                    {
                        continue;
                    }
                    break;

                case Direction.Left:
                    if ((arrows & Flags.LaneArrows.Left) != Flags.LaneArrows.None)
                    {
                        continue;
                    }
                    break;

                case Direction.Right:
                    if ((arrows & Flags.LaneArrows.Right) != Flags.LaneArrows.None)
                    {
                        continue;
                    }
                    break;
                }

#if DEBUGCONN
                Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {connectedSegmentId}: need to determine arrows");
#endif

                bool addArrow = false;

                uint curLaneId = netManager.m_segments.m_buffer[connectedSegmentId].m_lanes;
                while (curLaneId != 0)
                {
#if DEBUGCONN
                    Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {connectedSegmentId}: checking lane {curLaneId}");
#endif
                    if (AreLanesConnected(laneId, curLaneId, startNode))
                    {
#if DEBUGCONN
                        Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {connectedSegmentId}: checking lane {curLaneId}: lanes are connected");
#endif
                        addArrow = true;
                        break;
                    }

                    curLaneId = netManager.m_lanes.m_buffer[curLaneId].m_nextLane;
                }

#if DEBUGCONN
                Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {connectedSegmentId}: finished processing lanes. addArrow={addArrow} arrows (before)={arrows}");
#endif
                if (addArrow)
                {
                    switch (dir)
                    {
                    case Direction.Turn:
                    default:
                        continue;

                    case Direction.Forward:
                        arrows |= Flags.LaneArrows.Forward;
                        break;

                    case Direction.Left:
                        arrows |= Flags.LaneArrows.Left;
                        break;

                    case Direction.Right:
                        arrows |= Flags.LaneArrows.Right;
                        break;
                    }

#if DEBUGCONN
                    Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): processing connected segment {connectedSegmentId}: arrows={arrows}");
#endif
                }
            }

#if DEBUGCONN
            Log._Debug($"LaneConnectionManager.RecalculateLaneArrows({laneId}, {nodeId}): setting lane arrows to {arrows}");
#endif

            Flags.setLaneArrowFlags(laneId, arrows, true);
        }