internal VehiclePosition GetVehicle(ushort vehicleId)
        {
            VehiclePosition ret = null;

            Vehicles.TryGetValue(vehicleId, out ret);
            return(ret);
        }
		public void AddVehicle(ushort vehicleId, VehiclePosition carPos) {
			if (carPos.ToNode != NodeId || carPos.FromSegment != SegmentId) {
				Log.Warning($"Refusing to add vehicle {vehicleId} to PrioritySegment {SegmentId} @ {NodeId} (given: {carPos.FromSegment} @ {carPos.ToNode}).");
                return;
			}
			Vehicles[vehicleId] = carPos;
			TrafficPriority.MarkVehicleInSegment(vehicleId, SegmentId);
		}
 public void AddVehicle(ushort vehicleId, VehiclePosition carPos)
 {
     if (carPos.ToNode != NodeId || carPos.FromSegment != SegmentId)
     {
         Log.Warning($"Refusing to add vehicle {vehicleId} to PrioritySegment {SegmentId} @ {NodeId} (given: {carPos.FromSegment} @ {carPos.ToNode}).");
         return;
     }
     Vehicles[vehicleId] = carPos;
     TrafficPriority.MarkVehicleInSegment(vehicleId, SegmentId);
 }
		static TrafficPriority() {
			PrioritySegments = new TrafficSegment[Singleton<NetManager>.instance.m_segments.m_size];
			Vehicles = new VehiclePosition[Singleton<VehicleManager>.instance.m_vehicles.m_size];
			for (int i = 0; i < Singleton<VehicleManager>.instance.m_vehicles.m_size; ++i) {
				Vehicles[i] = new VehiclePosition();
			}
			markedVehicles = new HashSet<ushort>[Singleton<VehicleManager>.instance.m_vehicles.m_size];
			for (int i = 0; i < markedVehicles.Length; ++i) {
				markedVehicles[i] = new HashSet<ushort>();
			}
		}
		/// <summary>
		/// Handles vehicle path information in order to manage special nodes (nodes with priority signs or traffic lights).
		/// Data like "vehicle X is on segment S0 and is going to segment S1" is collected.
		/// </summary>
		/// <param name="vehicleId"></param>
		/// <param name="vehicleData"></param>
		internal static void HandleVehicle(ushort vehicleId, ref Vehicle vehicleData, bool addTraffic, bool realTraffic, byte maxUpcomingPathPositions, bool debug = false) {
			if (maxUpcomingPathPositions <= 0)
				maxUpcomingPathPositions = 1; // we need at least one upcoming path position

			var netManager = Singleton<NetManager>.instance;
			var lastFrameData = vehicleData.GetLastFrameData();
			var lastFrameVehiclePos = lastFrameData.m_position;
#if DEBUGV
			var camPos = Camera.main.transform.position;
			//debug = (lastFrameVehiclePos - camPos).sqrMagnitude < CloseLod;
			debug = false;
			List<String> logBuffer = new List<String>();
			bool logme = false;
#endif
			if ((vehicleData.m_flags & Vehicle.Flags.Created) == 0) {
				TrafficPriority.RemoveVehicleFromSegments(vehicleId);
				return;
			}

			if (vehicleData.Info.m_vehicleType != VehicleInfo.VehicleType.Car &&
				vehicleData.Info.m_vehicleType != VehicleInfo.VehicleType.Train &&
				vehicleData.Info.m_vehicleType != VehicleInfo.VehicleType.Tram) {
				//Log._Debug($"HandleVehicle does not handle vehicles of type {vehicleData.Info.m_vehicleType}");
				return;
			}
#if DEBUGV
			logBuffer.Add("Calculating prio info for vehicleId " + vehicleId);
#endif

			ExtVehicleType? vehicleType = CustomVehicleAI.DetermineVehicleTypeFromVehicle(vehicleId, ref vehicleData);
			if (vehicleType == null) {
				Log.Warning($"Could not determine vehicle type of vehicle {vehicleId}!");
			}

			if (vehicleType == null || vehicleType == ExtVehicleType.None) {
				return;
			}

			// add vehicle to our vehicle list
			VehiclePosition vehiclePos = TrafficPriority.GetVehiclePosition(vehicleId);

			// we extract the segment information directly from the vehicle
			var currentPathUnitId = vehicleData.m_path;
			List<ushort> realTimeDestinationNodes = new List<ushort>(); // current and upcoming node ids
			List<PathUnit.Position> realTimePositions = new List<PathUnit.Position>(); // current and upcoming vehicle positions

#if DEBUGV
			logBuffer.Add("* vehicleId " + vehicleId + ". currentPathId: " + currentPathUnitId + " pathPositionIndex: " + vehicleData.m_pathPositionIndex);
#endif

			if (currentPathUnitId > 0) {
				// vehicle has a path...
				if ((Singleton<PathManager>.instance.m_pathUnits.m_buffer[currentPathUnitId].m_pathFindFlags & PathUnit.FLAG_READY) != 0) {
					// The path(unit) is established and is ready for use: get the vehicle's current position in terms of segment and lane
					realTimePositions.Add(Singleton<PathManager>.instance.m_pathUnits.m_buffer[currentPathUnitId].GetPosition(vehicleData.m_pathPositionIndex >> 1));
					if (realTimePositions[0].m_offset == 0) {
						realTimeDestinationNodes.Add(netManager.m_segments.m_buffer[realTimePositions[0].m_segment].m_startNode);
					} else {
						realTimeDestinationNodes.Add(netManager.m_segments.m_buffer[realTimePositions[0].m_segment].m_endNode);
					}

					if (maxUpcomingPathPositions > 0) {
						// evaluate upcoming path units
						byte i = 0;
						uint pathUnitId = currentPathUnitId;
						int pathPos = (byte)((vehicleData.m_pathPositionIndex >> 1) + 1);
						while (true) {
							if (pathPos > 11) {
								// go to next path unit
								pathPos = 0;
								pathUnitId = Singleton<PathManager>.instance.m_pathUnits.m_buffer[pathUnitId].m_nextPathUnit;
#if DEBUGV
								logBuffer.Add("* vehicleId " + vehicleId + ". Going to next path unit (1). pathUnitId=" + pathUnitId);
#endif
								if (pathUnitId <= 0)
									break;
							}

							PathUnit.Position nextRealTimePosition = default(PathUnit.Position);
							if (!Singleton<PathManager>.instance.m_pathUnits.m_buffer[pathUnitId].GetPosition(pathPos, out nextRealTimePosition)) { // if this returns false, there is no next path unit
#if DEBUGV
								logBuffer.Add("* vehicleId " + vehicleId + ". No next path unit! pathPos=" + pathPos + ", pathUnitId=" + pathUnitId);
#endif
								break;
							}

							ushort destNodeId = 0;
							if (nextRealTimePosition.m_segment > 0) {
								if (nextRealTimePosition.m_offset == 0) {
									destNodeId = netManager.m_segments.m_buffer[nextRealTimePosition.m_segment].m_startNode;
								} else {
									destNodeId = netManager.m_segments.m_buffer[nextRealTimePosition.m_segment].m_endNode;
								}
							}

#if DEBUGV
							logBuffer.Add("* vehicleId " + vehicleId + ". Next path unit! node " + destNodeId + ", seg. " + nextRealTimePosition.m_segment + ", pathUnitId=" + pathUnitId + ", pathPos: " + pathPos);
#endif

							realTimePositions.Add(nextRealTimePosition);
							realTimeDestinationNodes.Add(destNodeId);

							if (i >= maxUpcomingPathPositions - 1)
								break; // we calculate up to 2 upcoming path units at the moment

							++pathPos;
							++i;
						}
					}

					// please don't ask why we use "m_pathPositionIndex >> 1" (which equals to "m_pathPositionIndex / 2") here (Though it would
					// be interesting to know why they used such an ugly indexing scheme!!). I assume the oddness of m_pathPositionIndex relates
					// to the car's position on the segment. If it is even the car might be in the segment's first half and if it is odd, it might
					// be in the segment's second half.
#if DEBUGV
					logBuffer.Add("* vehicleId " + vehicleId + ". *INFO* rtPos.seg=" + realTimePositions[0].m_segment + " nrtPos.seg=" + (realTimePositions.Count > 1 ? ""+realTimePositions[1].m_segment : "n/a"));
#endif
				}
			}

			// we have seen the car!
			vehiclePos.LastFrame = Singleton<SimulationManager>.instance.m_currentFrameIndex;

#if DEBUGV
			logBuffer.Add("* vehicleId " + vehicleId + ". ToNode: " + vehiclePos.ToNode + ". FromSegment: " + vehiclePos.FromSegment/* + ". FromLaneId: " + TrafficPriority.Vehicles[vehicleId].FromLaneId*/);
#endif
			if (addTraffic && vehicleData.m_leadingVehicle == 0 && realTimePositions.Count > 0) {
				// add traffic to lane
				uint laneId = PathManager.GetLaneID(realTimePositions[0]);
				CustomRoadAI.AddTraffic(laneId, (ushort)Mathf.RoundToInt(vehicleData.CalculateTotalLength(vehicleId)), (ushort)Mathf.RoundToInt(lastFrameData.m_velocity.magnitude), realTraffic);
			}

#if DEBUGV
			logBuffer.Add("* vehicleId " + vehicleId + ". Real time positions: " + realTimePositions.Count + ", Destination nodes: " + realTimeDestinationNodes.Count);
#endif
			if (realTimePositions.Count >= 1) {
				// we found a valid path unit
				var sourceLaneIndex = realTimePositions[0].m_lane;

				if (
					!vehiclePos.Valid ||
					vehiclePos.ToNode != realTimeDestinationNodes[0] ||
					vehiclePos.FromSegment != realTimePositions[0].m_segment ||
					vehiclePos.FromLaneIndex != sourceLaneIndex) {
					// vehicle information is not up-to-date. remove the car from old priority segments (if existing)...
					TrafficPriority.RemoveVehicleFromSegments(vehicleId);

					if (realTimePositions.Count >= 2) {
						// save vehicle information for priority rule handling
						vehiclePos.Valid = true;
						vehiclePos.CarState = VehicleJunctionTransitState.None;
						vehiclePos.WaitTime = 0;
						vehiclePos.Stopped = false;
						vehiclePos.ToNode = realTimeDestinationNodes[0];
						vehiclePos.FromSegment = realTimePositions[0].m_segment;
						vehiclePos.FromLaneIndex = realTimePositions[0].m_lane;
						vehiclePos.ToSegment = realTimePositions[1].m_segment;
						vehiclePos.ToLaneIndex = realTimePositions[1].m_lane;
						vehiclePos.ReduceSpeedByValueToYield = UnityEngine.Random.Range(16f, 28f);
						vehiclePos.OnEmergency = (vehicleData.m_flags & Vehicle.Flags.Emergency2) != 0;
						vehiclePos.VehicleType = (ExtVehicleType)vehicleType;

#if DEBUGV
					logBuffer.Add($"* vehicleId {vehicleId}. Setting current position to: from {vehiclePos.FromSegment} (lane {vehiclePos.FromLaneIndex}), going over {vehiclePos.ToNode}, to {vehiclePos.ToSegment} (lane {vehiclePos.ToLaneIndex})");
#endif

						//if (!Options.disableSomething) {
						// add the vehicle to upcoming priority segments that have timed traffic lights
						for (int i = 0; i < realTimePositions.Count - 1; ++i) {
							var prioritySegment = TrafficPriority.GetPrioritySegment(realTimeDestinationNodes[i], realTimePositions[i].m_segment);
							if (prioritySegment == null)
								continue;

							// add upcoming segments only if there is a timed traffic light
							TrafficLightSimulation nodeSim = TrafficLightSimulation.GetNodeSimulation(realTimeDestinationNodes[i]);
							if (i > 0 && (nodeSim == null || !nodeSim.IsTimedLight() || !nodeSim.IsTimedLightActive()))
								continue;

							VehiclePosition upcomingVehiclePos = new VehiclePosition();
							upcomingVehiclePos.Valid = true;
							upcomingVehiclePos.CarState = VehicleJunctionTransitState.None;
							upcomingVehiclePos.LastFrame = vehiclePos.LastFrame;
							upcomingVehiclePos.ToNode = realTimeDestinationNodes[i];
							upcomingVehiclePos.FromSegment = realTimePositions[i].m_segment;
							upcomingVehiclePos.FromLaneIndex = realTimePositions[i].m_lane;
							upcomingVehiclePos.ToSegment = realTimePositions[i + 1].m_segment;
							upcomingVehiclePos.ToLaneIndex = realTimePositions[i + 1].m_lane;
							upcomingVehiclePos.ReduceSpeedByValueToYield = UnityEngine.Random.Range(16f, 28f);
							upcomingVehiclePos.OnEmergency = (vehicleData.m_flags & Vehicle.Flags.Emergency2) != 0;
							upcomingVehiclePos.VehicleType = (ExtVehicleType)vehicleType;
#if DEBUGV
							logBuffer.Add($"* vehicleId {vehicleId}. Adding future position: from {upcomingVehiclePos.FromSegment}  (lane {upcomingVehiclePos.FromLaneIndex}), going over {upcomingVehiclePos.ToNode}, to {upcomingVehiclePos.ToSegment} (lane {upcomingVehiclePos.ToLaneIndex})");
#endif

							prioritySegment.AddVehicle(vehicleId, upcomingVehiclePos);
						}
					}
					//}
				} else {
#if DEBUGV
					logBuffer.Add($"* vehicleId {vehicleId}. Nothing has changed. from {vehiclePos.FromSegment} (lane {vehiclePos.FromLaneIndex}), going over {vehiclePos.ToNode}, to {vehiclePos.ToSegment} (lane {vehiclePos.ToLaneIndex})");
					logme = false;
#endif
				}
			} else {
#if DEBUGV
				logBuffer.Add($"* vehicleId {vehicleId}. Insufficient path unit positions.");
#endif
				TrafficPriority.RemoveVehicleFromSegments(vehicleId);
			}

#if DEBUGV
			if (logme) {
				Log._Debug("vehicleId: " + vehicleId + " ============================================");
				foreach (String logBuf in logBuffer) {
					Log._Debug(logBuf);
				}
				Log._Debug("vehicleId: " + vehicleId + " ============================================");
			}
#endif
		}
		internal static void AddVehicle(ushort vehicleId, VehiclePosition vehiclePos) {
			Vehicles[vehicleId] = vehiclePos;
		}
		public void TmCalculateSegmentPosition(ushort vehicleId, ref Vehicle vehicleData, PathUnit.Position nextPosition,
			PathUnit.Position position, uint laneID, byte offset, PathUnit.Position prevPos, uint prevLaneID,
			byte prevOffset, out Vector3 pos, out Vector3 dir, out float maxSpeed) {
			var netManager = Singleton<NetManager>.instance;
			//var vehicleManager = Singleton<VehicleManager>.instance;
			netManager.m_lanes.m_buffer[(int)((UIntPtr)laneID)].CalculatePositionAndDirection(offset * 0.003921569f, out pos, out dir);
			bool isRecklessDriver = (uint)vehicleId % (Options.getRecklessDriverModulo()) == 0;

			var lastFrameData = vehicleData.GetLastFrameData();
			var lastFrameVehiclePos = lastFrameData.m_position;

			var camPos = Camera.main.transform.position;
			if (vehicleData.Info.m_vehicleType == VehicleInfo.VehicleType.Car) {
				// add vehicle to our vehicle list
				VehiclePosition vehiclePos = TrafficPriority.Vehicles[vehicleId];
				if (vehiclePos == null) {
					vehiclePos = new VehiclePosition();
					TrafficPriority.Vehicles[vehicleId] = vehiclePos;
				}
				if (
					(
						(lastFrameVehiclePos - camPos).sqrMagnitude < CloseLod &&
						(TrafficPriority.Vehicles[vehicleId].LastFrame >> closeLodUpdateMod[Options.simAccuracy]) < (Singleton<SimulationManager>.instance.m_currentFrameIndex >> closeLodUpdateMod[Options.simAccuracy]) // very often
					) ||
					(
						(lastFrameVehiclePos - camPos).sqrMagnitude < FarLod &&
						(TrafficPriority.Vehicles[vehicleId].LastFrame >> farLodUpdateMod[Options.simAccuracy]) < (Singleton<SimulationManager>.instance.m_currentFrameIndex >> farLodUpdateMod[Options.simAccuracy]) // often
					) ||
						(TrafficPriority.Vehicles[vehicleId].LastFrame >> veryFarLodUpdateMod[Options.simAccuracy]) < (Singleton<SimulationManager>.instance.m_currentFrameIndex >> veryFarLodUpdateMod[Options.simAccuracy]) // less often
					) {
					//Log.Message("handle vehicle after threshold");
					try {
						HandleVehicle(vehicleId, ref vehicleData, false);
					} catch (Exception e) {
						Log.Error("CarAI TmCalculateSegmentPosition Error: " + e.ToString());
					}
				}
			} else {
				Log.Message($"TmCalculateSegmentPosition does not handle vehicles of type {vehicleData.Info.m_vehicleType}");
			}

			// I think this is supposed to be the lane position?
			// [VN, 12/23/2015] It's the 3D car position on the Bezier curve of the lane.
			// This crazy 0.003921569f equals to 1f/255 and prevOffset is the byte value (0..255) of the car position.
			var vehiclePosOnBezier = netManager.m_lanes.m_buffer[(int)((UIntPtr)prevLaneID)].CalculatePosition(prevOffset * 0.003921569f);
			//ushort currentSegmentId = netManager.m_lanes.m_buffer[(int)((UIntPtr)prevLaneID)].m_segment;

			ushort destinationNodeId;
			ushort sourceNodeId;
			if (offset < position.m_offset) {
				destinationNodeId = netManager.m_segments.m_buffer[position.m_segment].m_startNode;
				sourceNodeId = netManager.m_segments.m_buffer[position.m_segment].m_endNode;
			} else {
				destinationNodeId = netManager.m_segments.m_buffer[position.m_segment].m_endNode;
				sourceNodeId = netManager.m_segments.m_buffer[position.m_segment].m_startNode;
			}
			var previousDestinationNode = prevOffset == 0 ? netManager.m_segments.m_buffer[prevPos.m_segment].m_startNode : netManager.m_segments.m_buffer[prevPos.m_segment].m_endNode;
			
			// this seems to be like the required braking force in order to stop the vehicle within its half length.
			var crazyValue = 0.5f * lastFrameData.m_velocity.sqrMagnitude / m_info.m_braking + m_info.m_generatedInfo.m_size.z * 0.5f;

			// Essentially, this is true if the car has enough time and space to brake (e.g. for a red traffic light)
			if (destinationNodeId == previousDestinationNode) {
				if (Vector3.Distance(lastFrameVehiclePos, vehiclePosOnBezier) >= crazyValue - 1f) {
					var currentFrameIndex = Singleton<SimulationManager>.instance.m_currentFrameIndex;
					var num5 = (uint)((previousDestinationNode << 8) / 32768);
					var num6 = currentFrameIndex - num5 & 255u;

					var nodeFlags = netManager.m_nodes.m_buffer[destinationNodeId].m_flags;
					var prevLaneFlags = (NetLane.Flags)netManager.m_lanes.m_buffer[(int)((UIntPtr)prevLaneID)].m_flags;
					var hasTrafficLight = (nodeFlags & NetNode.Flags.TrafficLights) != NetNode.Flags.None;
					var hasCrossing = (nodeFlags & NetNode.Flags.LevelCrossing) != NetNode.Flags.None;
					var isJoinedJunction = (prevLaneFlags & NetLane.Flags.JoinedJunction) != NetLane.Flags.None;
					bool checkSpace = !Options.mayEnterBlockedJunctions;
					if (checkSpace && (uint)vehicleId % (Options.getRecklessDriverModulo() / 2) == 0) {
						checkSpace = false;
					}
					if (checkSpace) {
						// check if there is enough space
						if ((nodeFlags & (NetNode.Flags.Junction | NetNode.Flags.OneWayOut | NetNode.Flags.OneWayIn)) ==
							NetNode.Flags.Junction && netManager.m_nodes.m_buffer[destinationNodeId].CountSegments() != 2) {
							var len = vehicleData.CalculateTotalLength(vehicleId) + 2f;
							if (!netManager.m_lanes.m_buffer[(int)((UIntPtr)laneID)].CheckSpace(len)) {
								var sufficientSpace = false;
								if (nextPosition.m_segment != 0 &&
									netManager.m_lanes.m_buffer[(int)((UIntPtr)laneID)].m_length < 30f) {
									var flags3 = netManager.m_nodes.m_buffer[sourceNodeId].m_flags;
									if ((flags3 &
										 (NetNode.Flags.Junction | NetNode.Flags.OneWayOut | NetNode.Flags.OneWayIn)) !=
										NetNode.Flags.Junction || netManager.m_nodes.m_buffer[sourceNodeId].CountSegments() == 2) {
										var laneId2 = PathManager.GetLaneID(nextPosition);
										if (laneId2 != 0u) {
											sufficientSpace = netManager.m_lanes.m_buffer[(int)((UIntPtr)laneId2)].CheckSpace(len);
										}
									}
								}
								if (!sufficientSpace) {
									maxSpeed = 0f;
									return;
								}
							}
						}
					}


					try {
						if (vehicleData.Info.m_vehicleType == VehicleInfo.VehicleType.Car) {
							if (hasTrafficLight && (!isJoinedJunction || hasCrossing)) {
								var nodeSimulation = TrafficLightSimulation.GetNodeSimulation(previousDestinationNode);

								var destinationInfo = netManager.m_nodes.m_buffer[destinationNodeId].Info;
								RoadBaseAI.TrafficLightState vehicleLightState;
								ManualSegmentLight light = TrafficLightsManual.GetSegmentLight(previousDestinationNode, prevPos.m_segment); // TODO rework

								if (light == null || nodeSimulation == null ||
									(nodeSimulation.TimedTrafficLights && !nodeSimulation.TimedTrafficLightsActive)) {
									RoadBaseAI.TrafficLightState pedestrianLightState;
									bool flag5;
									bool pedestrians;
									RoadBaseAI.GetTrafficLightState(previousDestinationNode,
										ref netManager.m_segments.m_buffer[prevPos.m_segment],
										currentFrameIndex - num5, out vehicleLightState, out pedestrianLightState, out flag5,
										out pedestrians);
									if (!flag5 && num6 >= 196u) {
										flag5 = true;
										RoadBaseAI.SetTrafficLightState(previousDestinationNode,
											ref netManager.m_segments.m_buffer[prevPos.m_segment], currentFrameIndex - num5,
											vehicleLightState, pedestrianLightState, flag5, pedestrians);
									}

									if ((vehicleData.m_flags & Vehicle.Flags.Emergency2) == Vehicle.Flags.None ||
										destinationInfo.m_class.m_service != ItemClass.Service.Road) {
										switch (vehicleLightState) {
											case RoadBaseAI.TrafficLightState.RedToGreen:
												if (num6 < 60u) {
													maxSpeed = 0f;
													return;
												}
												break;
											case RoadBaseAI.TrafficLightState.Red:
												maxSpeed = 0f;
												return;
											case RoadBaseAI.TrafficLightState.GreenToRed:
												if (num6 >= 30u) {
													maxSpeed = 0f;
													return;
												}
												break;
										}
									}
								} else {
									// traffic light simulation is active
									var stopCar = false;

									if (isRecklessDriver)
										vehicleLightState = RoadBaseAI.TrafficLightState.Green;
									else {
										// determine responsible traffic light (left, right or main)
										if (CustomRoadAI.segmentGeometries[prevPos.m_segment].IsLeftSegment(position.m_segment, destinationNodeId)) {
											vehicleLightState = light.GetLightLeft();
										} else if (CustomRoadAI.segmentGeometries[prevPos.m_segment].IsRightSegment(position.m_segment, destinationNodeId)) {
											vehicleLightState = light.GetLightRight();
										} else {
											vehicleLightState = light.GetLightMain();
										}
									}

									if ((vehicleData.m_flags & Vehicle.Flags.Emergency2) == Vehicle.Flags.None ||
										destinationInfo.m_class.m_service != ItemClass.Service.Road) {
										switch (vehicleLightState) {
											case RoadBaseAI.TrafficLightState.RedToGreen:
												if (num6 < 60u) {
													stopCar = true;
												}
												break;
											case RoadBaseAI.TrafficLightState.Red:
												stopCar = true;
												break;
											case RoadBaseAI.TrafficLightState.GreenToRed:
												if (num6 >= 30u) {
													stopCar = true;
												}
												break;
										}
									}

									if (stopCar) {
										maxSpeed = 0f;
										return;
									}
								}
							} else if ((lastFrameVehiclePos - camPos).sqrMagnitude < FarLod && !isRecklessDriver) {
#if DEBUG
								//bool debug = destinationNodeId == 10864;
								bool debug = false;
#endif
								//bool debug = false;
#if DEBUG
								if (debug)
									Log.Message($"Vehicle {vehicleId} is arriving @ seg. {prevPos.m_segment} ({position.m_segment}, {nextPosition.m_segment}), node {destinationNodeId} which is not a traffic light.");
#endif

								var prioritySegment = TrafficPriority.GetPrioritySegment(destinationNodeId, prevPos.m_segment);
								if (prioritySegment != null) {
#if DEBUG
									if (debug)
										Log.Message($"Vehicle {vehicleId} is arriving @ seg. {prevPos.m_segment} ({position.m_segment}, {nextPosition.m_segment}), node {destinationNodeId} which is not a traffic light and is a priority segment.");
#endif
                                    VehiclePosition segTargetPos = prioritySegment.GetVehicle(vehicleId);
									if (segTargetPos != null) {
#if DEBUG
										if (debug)
											Log.Message($"Vehicle {vehicleId}: segment target position found");
#endif
										VehiclePosition globalTargetPos = TrafficPriority.Vehicles[vehicleId];
										if (globalTargetPos != null) {
#if DEBUG
											if (debug)
												Log.Message($"Vehicle {vehicleId}: global target position found. carState = {globalTargetPos.CarState.ToString()}");
#endif
											var currentFrameIndex2 = Singleton<SimulationManager>.instance.m_currentFrameIndex;
											var frame = currentFrameIndex2 >> 4;

											if (globalTargetPos.CarState == CarState.None) {
												globalTargetPos.CarState = CarState.Enter;
											}

											if ((vehicleData.m_flags & Vehicle.Flags.Emergency2) == Vehicle.Flags.None && globalTargetPos.CarState != CarState.Leave) {
												bool hasIncomingCars;
												switch (prioritySegment.Type) {
													case PrioritySegment.PriorityType.Stop:
#if DEBUG
														if (debug)
															Log.Message($"Vehicle {vehicleId}: STOP sign. waittime={globalTargetPos.WaitTime}, vel={lastFrameData.m_velocity.magnitude}");
#endif
														if (globalTargetPos.WaitTime < 30) {
															globalTargetPos.CarState = CarState.Stop;

															if (lastFrameData.m_velocity.magnitude < 0.5f ||
																globalTargetPos.Stopped) {
																globalTargetPos.Stopped = true;
																globalTargetPos.WaitTime++;

																if (globalTargetPos.WaitTime > 1) {
																	hasIncomingCars = TrafficPriority.HasIncomingVehicles(vehicleId, destinationNodeId);
#if DEBUG
																	if (debug)
																		Log.Message($"hasIncomingCars: {hasIncomingCars}");
#endif

																	if (hasIncomingCars) {
																		maxSpeed = 0f;
																		return;
																	}
																	globalTargetPos.CarState = CarState.Leave;
																} else {
																	maxSpeed = 0f;
																	return;
																}
															} else {
																maxSpeed = 0f;
																return;
															}
														} else {
															globalTargetPos.CarState = CarState.Leave;
														}
														break;
													case PrioritySegment.PriorityType.Yield:
#if DEBUG
														if (debug)
															Log.Message($"Vehicle {vehicleId}: YIELD sign. waittime={globalTargetPos.WaitTime}");
#endif
														if (globalTargetPos.WaitTime < 30) {
															globalTargetPos.WaitTime++;
															globalTargetPos.CarState = CarState.Stop;
															hasIncomingCars = TrafficPriority.HasIncomingVehicles(vehicleId, destinationNodeId);
#if DEBUG
															if (debug)
																Log.Message($"hasIncomingCars: {hasIncomingCars}");
#endif
															if (hasIncomingCars) {
																if (lastFrameData.m_velocity.magnitude > 0) {
																	maxSpeed = Math.Max(0f, lastFrameData.m_velocity.magnitude - globalTargetPos.ReduceSpeedByValueToYield);
																} else {
																	maxSpeed = 0f;
																}
#if DEBUG
																/*if (TrafficPriority.Vehicles[vehicleId].ToNode == 8621)
																	Log.Message($"Vehicle {vehicleId} is yielding at node {destinationNodeId}. Speed: {maxSpeed}, Waiting time: {TrafficPriority.Vehicles[vehicleId].WaitTime}");*/
#endif
																return;
															} else {
#if DEBUG
																/*if (TrafficPriority.Vehicles[vehicleId].ToNode == 8621)
																	Log.Message($"Vehicle {vehicleId} is NOT yielding at node {destinationNodeId}.");*/
#endif
															}
															globalTargetPos.CarState = CarState.Leave;
														} else {
															globalTargetPos.CarState = CarState.Leave;
														}
														break;
													case PrioritySegment.PriorityType.Main:
#if DEBUG
														if (debug)
															Log.Message($"Vehicle {vehicleId}: MAIN sign. waittime={globalTargetPos.WaitTime}");
#endif
														if (globalTargetPos.WaitTime < 30) {
															globalTargetPos.WaitTime++;
															globalTargetPos.CarState = CarState.Stop;
															maxSpeed = 0f;

															hasIncomingCars = TrafficPriority.HasIncomingVehicles(vehicleId, destinationNodeId);
#if DEBUG
															if (debug)
																Log.Message($"hasIncomingCars: {hasIncomingCars}");
#endif

															if (hasIncomingCars) {
																globalTargetPos.Stopped = true;
																return;
															}
															globalTargetPos.CarState = CarState.Leave;
															globalTargetPos.Stopped = false;
														}

														var info3 = netManager.m_segments.m_buffer[position.m_segment].Info;
														if (info3.m_lanes != null && info3.m_lanes.Length > position.m_lane) {
															maxSpeed =
																CalculateTargetSpeed(vehicleId, ref vehicleData, info3.m_lanes[position.m_lane].m_speedLimit, netManager.m_lanes.m_buffer[(int)((UIntPtr)laneID)].m_curve) * 0.8f;
														} else {
															maxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, 1f, 0f) * 0.8f;
														}
														return;
												}
											} else {
												globalTargetPos.CarState = CarState.Leave;
											}
										} else {
#if DEBUG
											if (debug)
												Log.Warning($"globalTargetPos is null! {vehicleId} @ seg. {prevPos.m_segment} @ node {destinationNodeId}");
#endif
										}
									} else {
#if DEBUG
										if (debug)
											Log.Warning($"targetPos is null! {vehicleId} @ seg. {prevPos.m_segment} @ node {destinationNodeId}");
#endif
									}
								}
							}
						}
					} catch (Exception e) {
						Log.Error($"Error occured in TmCalculateSegmentPosition: {e.ToString()}");
					}
				}
			}

			var info2 = netManager.m_segments.m_buffer[position.m_segment].Info;
			if (info2.m_lanes != null && info2.m_lanes.Length > position.m_lane) {
				var laneSpeedLimit = info2.m_lanes[position.m_lane].m_speedLimit;

				if (TrafficRoadRestrictions.IsSegment(position.m_segment)) {
					var restrictionSegment = TrafficRoadRestrictions.GetSegment(position.m_segment);

					if (restrictionSegment.SpeedLimits[position.m_lane] > 0.1f) {
						laneSpeedLimit = restrictionSegment.SpeedLimits[position.m_lane];
					}
				}

				maxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, laneSpeedLimit,
					netManager.m_lanes.m_buffer[(int)((UIntPtr)laneID)].m_curve);
			} else {
				maxSpeed = CalculateTargetSpeed(vehicleId, ref vehicleData, 1f, 0f);
			}

			if (isRecklessDriver)
				maxSpeed *= Random.Range(1.2f, 2.5f);
		}
        /// <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(float?minSpeed, ExtVehicleType?vehicleTypes = null, ExtVehicleType separateVehicleTypes = ExtVehicleType.None, bool debug = false)
        {
            Dictionary <ushort, uint> numCarsGoingToSegmentId = new Dictionary <ushort, uint>();
            VehicleManager            vehicleManager          = Singleton <VehicleManager> .instance;
            NetManager netManager = Singleton <NetManager> .instance;

            for (var s = 0; s < 8; s++)
            {
                var segmentId = netManager.m_nodes.m_buffer[NodeId].GetSegment(s);

                if (segmentId == 0 || segmentId == SegmentId)
                {
                    continue;
                }

                if (CustomRoadAI.GetSegmentGeometry(segmentId).IsIncomingOneWay(NodeId))
                {
                    continue;
                }

                numCarsGoingToSegmentId[segmentId] = 0;
            }

            List <ushort> vehicleIdsToReHandle = new List <ushort>();

            foreach (KeyValuePair <ushort, VehiclePosition> e in Vehicles)
            {
                var vehicleId = e.Key;
                var carPos    = e.Value;

                if (vehicleId <= 0 || carPos.ToSegment <= 0)
                {
                    continue;
                }
                if ((vehicleManager.m_vehicles.m_buffer[vehicleId].m_flags & Vehicle.Flags.Created) == Vehicle.Flags.None)
                {
                    vehicleIdsToReHandle.Add(vehicleId);
                    continue;
                }
                if (minSpeed != null && vehicleManager.m_vehicles.m_buffer[vehicleId].GetLastFrameVelocity().magnitude < minSpeed)
                {
                    continue;
                }
                VehiclePosition globalPos = TrafficPriority.GetVehiclePosition(vehicleId);
                if (globalPos == null || !globalPos.Valid || globalPos.LastFrame >> 7 < Singleton <SimulationManager> .instance.m_currentFrameIndex >> 7)                 // ~64 sec.
                {
                    vehicleIdsToReHandle.Add(vehicleId);
                    continue;
                }
                if (vehicleTypes != null)
                {
                    if (vehicleTypes == ExtVehicleType.None)
                    {
                        if ((globalPos.VehicleType & separateVehicleTypes) != ExtVehicleType.None)
                        {
                            // we want all vehicles that do not have separate traffic lights
                            continue;
                        }
                    }
                    else
                    {
                        if ((globalPos.VehicleType & vehicleTypes) == ExtVehicleType.None)
                        {
                            continue;
                        }
                    }
                }

                //debug = vehicleManager.m_vehicles.m_buffer[vehicleId].Info.m_vehicleType == VehicleInfo.VehicleType.Tram;
#if DEBUG
                /*if (debug) {
                 *      Log._Debug($"getNumCarsGoingToSegment: Handling vehicle {vehicleId} going from {carPos.FromSegment}/{SegmentId} to {carPos.ToSegment}. carState={globalPos.CarState}. lastUpdate={globalPos.LastCarStateUpdate}");
                 * }*/
#endif

                uint avgSegmentLength = (uint)Singleton <NetManager> .instance.m_segments.m_buffer[SegmentId].m_averageLength;
                uint normLength       = (uint)(vehicleManager.m_vehicles.m_buffer[vehicleId].CalculateTotalLength(vehicleId) * 100u) / avgSegmentLength;

#if DEBUG
                /*if (debug) {
                 *      Log._Debug($"getNumCarsGoingToSegment: NormLength of vehicle {vehicleId} going to {carPos.ToSegment}: {avgSegmentLength} -> {normLength}");
                 * }*/
#endif

                if (numCarsGoingToSegmentId.ContainsKey(carPos.ToSegment))
                {
                    /*if (carPos.OnEmergency)
                     *      numCarsGoingToSegmentId[carPos.ToSegment] += 10000f;
                     * else*/
                    numCarsGoingToSegmentId[carPos.ToSegment] += normLength;
                }
                // "else" must not happen (incoming one-way)
            }

            foreach (ushort vehicleId in vehicleIdsToReHandle)
            {
                CustomVehicleAI.HandleVehicle(vehicleId, ref Singleton <VehicleManager> .instance.m_vehicles.m_buffer[vehicleId], false, false);
            }
            return(numCarsGoingToSegmentId);
        }