///////////////////////////////////////////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Gets stop's point.
        /// </summary>
        /// <param name="stop">Stop as source for point.</param>
        /// <returns>Stop's related point.</returns>
        private static Point _GetStopPoint(StopData stop)
        {
            Debug.Assert(null != stop);
            Debug.Assert(StopType.Lunch != stop.StopType);
            Debug.Assert(null != stop.AssociatedObject);

            var geocodable = stop.AssociatedObject as IGeocodable;
            Debug.Assert(null != geocodable);

            Point? pt = geocodable.GeoLocation;
            Debug.Assert(pt != null);

            return pt.Value;
        }
        /// <summary>
        /// Gets arrival delay for selected stop.
        /// </summary>
        /// <param name="arrivalDelay">Arrival delay value.</param>
        /// <param name="stop">Stop as source.</param>
        /// <param name="prevStopPoint">Previously stop point.</param>
        /// <returns>Arrival delay value or 0 if not need considers arrival delay.</returns>
        private static int _GetArrivalDelay(int arrivalDelay,
                                            StopData stop,
                                            ref Point? prevStopPoint)
        {
            Debug.Assert(null != stop);

            Point point = _GetStopPoint(stop);

            // if location changed
            bool needDelay = (!prevStopPoint.HasValue ||
                              (prevStopPoint.HasValue && (point != prevStopPoint)));
            prevStopPoint = point;

            return needDelay ? arrivalDelay : 0;
        }
        /// <summary>
        /// Method creates stop from order GP feature.
        /// </summary>
        /// <param name="feature">Order GP feature</param>
        /// <param name="route">Route.</param>
        /// <returns>Stop data.</returns>
        private StopData _CreateStopFromOrder(GPFeature feature, Route route)
        {
            var stop = new StopData();

            // distance
            stop.Distance = feature.Attributes.Get<double>(NAAttribute.FROM_PREV_DISTANCE);

            // sequence number
            stop.SequenceNumber = feature.Attributes.Get<int>(NAAttribute.SEQUENCE);

            // wait time
            stop.WaitTime = feature.Attributes.Get<double>(NAAttribute.WAIT_TIME, 0.0);

            // service time
            stop.TimeAtStop = feature.Attributes.Get<double>(NAAttribute.SERVICE_TIME, 0.0);

            // travel time
            stop.TravelTime =
                feature.Attributes.Get<double>(NAAttribute.FROM_PREV_TRAVEL_TIME, 0.0);

            // arrive time
            var arriveTime = _Get(feature.Attributes, NAAttribute.ARRIVE_TIME);

            // set stop arrive time = real arrive time + wait time
            stop.ArriveTime = arriveTime.AddMinutes(stop.WaitTime);

            // TW1
            stop.TimeWindowStart1 = _TryGet(feature.Attributes, NAAttribute.TW_START1);
            stop.TimeWindowEnd1 = _TryGet(feature.Attributes, NAAttribute.TW_END1);

            // TW2
            stop.TimeWindowStart2 = _TryGet(feature.Attributes, NAAttribute.TW_START2);
            stop.TimeWindowEnd2 = _TryGet(feature.Attributes, NAAttribute.TW_END2);

            // curbapproach
            stop.NACurbApproach =
                (NACurbApproachType)feature.Attributes.Get<int>(NAAttribute.ArriveCurbApproach);

            // associated object
            Guid assocObjectId = _AttrToObjectId(NAAttribute.NAME, feature.Attributes);

            Order order = _project.Orders.SearchById(assocObjectId);
            if (order == null)
            {
                string message = Properties.Messages.Error_InvalidGPFeatureMapping;
                throw new RouteException(message); // exception
            }

            stop.AssociatedObject = order;

            stop.TimeAtStop = order.ServiceTime;

            var plannedDate = route.Schedule.PlannedDate.Value;
            var timeWindow = order.TimeWindow.ToDateTime(plannedDate);
            stop.TimeWindowStart1 = timeWindow.Item1;
            stop.TimeWindowEnd1 = timeWindow.Item2;

            timeWindow = order.TimeWindow2.ToDateTime(plannedDate);
            stop.TimeWindowStart2 = timeWindow.Item1;
            stop.TimeWindowEnd2 = timeWindow.Item2;

            // route id
            stop.RouteId = _AttrToObjectId(NAAttribute.ROUTE_NAME, feature.Attributes);

            // geometry
            if (order.GeoLocation != null)
            {
                stop.Geometry = GPObjectHelper.PointToGPPoint(order.GeoLocation.Value);
            }

            // stop type
            stop.StopType = StopType.Order;

            return stop;
        }
        /// <summary>
        /// Method creates stop from depot.
        /// </summary>
        /// <param name="depotVisitFeature">Depot GP feature.</param>
        /// <param name="route">Route.</param>
        /// <param name="renewalsByID">Renewals collection associated with its Id.</param>
        /// <returns>Stop data.</returns>
        private StopData _CreateStopFromDepot(
            GPFeature depotVisitFeature,
            Route route,
            IDictionary<Guid, GPFeature> renewalsByID)
        {
            Debug.Assert(depotVisitFeature != null);
            Debug.Assert(route != null);
            Debug.Assert(renewalsByID != null);

            StopData stop = new StopData();

            // Distance.
            stop.Distance = depotVisitFeature.Attributes.Get<double>(NAAttribute.FROM_PREV_DISTANCE);

            // Sequence number.
            stop.SequenceNumber = depotVisitFeature.Attributes.Get<int>(NAAttribute.SEQUENCE);

            // Wait time.
            stop.WaitTime = depotVisitFeature.Attributes.Get<double>(NAAttribute.WAIT_TIME, 0.0);

            // Travel time.
            stop.TravelTime =
                depotVisitFeature.Attributes.Get<double>(NAAttribute.FROM_PREV_TRAVEL_TIME, 0.0);

            // Arrive time.
            stop.ArriveTime = _Get(depotVisitFeature.Attributes, NAAttribute.ARRIVE_TIME);

            // Associated object.
            Guid assocObjectId =
                _AttrToObjectId(NAAttribute.NAME, depotVisitFeature.Attributes);

            Location loc = _GetRouteLocation(route, assocObjectId);
            if (loc == null)
            {
                string message = Properties.Messages.Error_InvalidGPFeatureMapping;
                throw new RouteException(message); // exception
            }

            stop.AssociatedObject = loc;

            var renewalFeature = default(GPFeature);
            if (renewalsByID.TryGetValue(assocObjectId, out renewalFeature))
            {
                stop.TimeAtStop = renewalFeature.Attributes.Get<double>(
                    NAAttribute.SERVICE_TIME,
                    0.0);
            }

            var plannedDate = route.Schedule.PlannedDate.Value;
            var timeWindow = loc.TimeWindow.ToDateTime(plannedDate);
            stop.TimeWindowStart1 = timeWindow.Item1;
            stop.TimeWindowEnd1 = timeWindow.Item2;

            // Curb approach.
            stop.NACurbApproach = CurbApproachConverter.ToNACurbApproach(
                _settings.GetDepotCurbApproach());

            // Route id.
            stop.RouteId = route.Id;

            // Geometry.
            if (loc.GeoLocation != null)
            {
                stop.Geometry = GPObjectHelper.PointToGPPoint(loc.GeoLocation.Value);
            }

            // Stop type.
            stop.StopType = StopType.Location;

            return stop;
        }
        /// <summary>
        /// Method creates stop from break GP feature.
        /// </summary>
        /// <param name="feature">Break GP feature</param>
        /// <param name="route">Route.</param>
        /// <param name="routeBreak">Break from route.</param>
        /// <param name="stop">Stop data to fill.</param>
        /// <returns>True - if successfully created, otherwise - False.</returns>
        private bool _CreateStopFromBreak(GPFeature feature, Route route,
            Break routeBreak, out StopData stop)
        {
            Debug.Assert(feature != null);
            Debug.Assert(route != null);
            Debug.Assert(routeBreak != null);

            // Check sequence number.
            int sequenceNumber = 0;

            if (!feature.Attributes.TryGet<int>(NAAttribute.SEQUENCE, out sequenceNumber)
                // Sequence always starts from 1. Otherwise: this is empty break.
                || sequenceNumber == 0)
            {
                stop = null;
                return false;
            }

            stop = new StopData();
            stop.SequenceNumber = sequenceNumber;

            // Distance.
            stop.Distance = feature.Attributes.Get<double>(NAAttribute.FROM_PREV_DISTANCE);

            // Wait time.
            stop.WaitTime = feature.Attributes.Get<double>(NAAttribute.WAIT_TIME, 0.0);

            // Service time.
            stop.TimeAtStop = routeBreak.Duration;

            // Travel time.
            stop.TravelTime =
                feature.Attributes.Get<double>(NAAttribute.FROM_PREV_TRAVEL_TIME, 0.0);

            // Time Windows for break stop.
            var breakTimeWindow = _GetBreakTimeWindow(routeBreak);
            var plannedDate = route.Schedule.PlannedDate.Value;

            var timeWindow = breakTimeWindow.ToDateTime(plannedDate);
            stop.TimeWindowStart1 = timeWindow.Item1;
            stop.TimeWindowEnd1 = timeWindow.Item2;

            // Arrive time.
            var arriveTime = _TryGet(feature.Attributes, NAAttribute.ARRIVE_TIME);

            if (!arriveTime.HasValue)
            {
                arriveTime = stop.TimeWindowStart1;
            }

            arriveTime = arriveTime.AddMinutes(stop.WaitTime);

            stop.ArriveTime = arriveTime.Value;

            // Route id.
            stop.RouteId = _AttrToObjectId(NAAttribute.ROUTE_NAME, feature.Attributes);

            // Geometry:
            // There is no geometry for breaks.

            // Curb approach.
            stop.NACurbApproach = NACurbApproachType.esriNAEitherSideOfVehicle;

            // Stop type.
            stop.StopType = StopType.Lunch;

            // Break stop does not have an associated object.
            stop.AssociatedObject = null;

            return true;
        }
        /// <summary>
        /// Gets effective TimeWindow.
        /// </summary>
        /// <param name="stop">Stop data.</param>
        /// <param name="twStart">Start timewindow (can be null).</param>
        /// <param name="twEnd">End timewindow (can be null).</param>
        /// <returns>TRUE if twStart and twEnd not null.</returns>
        private static bool _GetEffectiveTW(StopData stop,
                                            out DateTime? twStart,
                                            out DateTime? twEnd)
        {
            Debug.Assert(stop != null);

            twStart = null;
            twEnd = null;

            bool hasTW1 = (stop.TimeWindowStart1 != null && stop.TimeWindowEnd1 != null);
            bool hasTW2 = (stop.TimeWindowStart2 != null && stop.TimeWindowEnd2 != null);

            DateTime? start = null;
            DateTime? end = null;

            bool result = false;
            if (stop.StopType == StopType.Order || stop.StopType == StopType.Location)
            {
                // Orders and locations have 2 time windows, need to choose appropriate one.
                if (hasTW1 && hasTW2)
                {
                    // Find and set time window, where time of arrival falls within the range.
                    if (stop.ArriveTime <= (DateTime)stop.TimeWindowEnd1)
                    {
                        start = stop.TimeWindowStart1;
                        end = stop.TimeWindowEnd1;
                    }
                    else
                    {
                        start = stop.TimeWindowStart2;
                        end = stop.TimeWindowEnd2;
                    }
                }
                else
                {
                    // Set one of existed time windows.
                    if (hasTW1)
                    {
                        start = stop.TimeWindowStart1;
                        end = stop.TimeWindowEnd1;
                    }
                    else if (hasTW2)
                    {
                        start = stop.TimeWindowStart2;
                        end = stop.TimeWindowEnd2;
                    }
                }
            }
            else if (stop.StopType == StopType.Lunch)
            {
                if (hasTW1)
                {
                    // Breaks have only one time window.
                    start = stop.TimeWindowStart1;
                    end = stop.TimeWindowEnd1;
                }
            }

            if (start != null && end != null)
            {
                twStart = start;
                twEnd = end;

                result = true;
            }

            return result;
        }