/// <summary>
        /// Export route to GRF file.
        /// </summary>
        /// <param name="writer">XMLWriter.</param>
        /// <param name="route">Route to export.</param>
        /// <param name="stops">Route's stops.</param>
        /// <param name="project">Project, which contains this route.</param>
        /// <param name="geocoder">Geocoder.</param>
        /// <param name="solver">Solver.</param>
        /// <param name="orderPropertiesToExport">Collection of order properties, which must be
        /// exported.</param>
        /// <param name="compress">Flag, shows compress result file or not.</param>
        /// <returns>GrfExportResult which contains messages about exporting.</returns>
        public static GrfExportResult ExportToGRF(XmlWriter writer, Route route, ICollection <Stop> stops, Project project,
                                                  IGeocoder geocoder, IVrpSolver solver, ICollection <string> orderPropertiesToExport, bool compress)
        {
            GrfExportResult result = new GrfExportResult();

            writer.WriteStartElement(GRF_DOC_NODE_NAME);
            writer.WriteAttributeString(VERSION_ATTR_NAME, VERSION_VALUE);

            writer.WriteStartElement(ROUTEINFO_NODE_NAME);
            if (stops.Count > 0)
            {
                _SaveStops(
                    writer,
                    route,
                    stops,
                    orderPropertiesToExport,
                    project,
                    geocoder.AddressFields);
            }

            IDataObjectCollection <Barrier> barriers =
                (IDataObjectCollection <Barrier>)project.Barriers.Search(route.StartTime.Value.Date, true);

            _SaveBarriers(writer, barriers, result);
            _SaveRouteResults(writer, route, stops);
            writer.WriteEndElement();
            _SaveRouteSettings(writer, route, solver, result);
            writer.WriteEndElement();

            return(result);
        }
        /// <summary>
        /// Write point barriers with writer. If there are barriers with other geometry - add
        /// message to 'result'.
        /// </summary>
        /// <param name="writer">XmlWriter.</param>
        /// <param name="barriers">Collection of barriers to write.</param>
        /// <param name="result">GrfExportResult with messages.</param>
        private static void _SaveBarriers(XmlWriter writer, ICollection <Barrier> barriers,
                                          GrfExportResult result)
        {
            if (barriers == null)
            {
                return;
            }

            // Detecting barriers with not point geometry and add warning message.
            var barriersWithNotPointGeometry = barriers.Where(barrier => (barrier.Geometry != null &&
                                                                          !(barrier.Geometry is ESRI.ArcLogistics.Geometry.Point)));

            if (barriersWithNotPointGeometry.Any())
            {
                result.Warnings.Add(Properties.Messages.Warning_BarriersNotSupported);
            }

            // If there is no point barriers then finish function execution.
            var barriersWithPointGeometry = barriers.Where(barrier =>
                                                           barrier.Geometry is ESRI.ArcLogistics.Geometry.Point);

            if (!barriersWithPointGeometry.Any())
            {
                return;
            }

            // Select block point barriers.
            // Detected delay point barriers - add warning message.
            // If there is no block point barriers then finish function execution.
            var blockBarriersWithPointGeometry = barriersWithPointGeometry.Where(barrier =>
                                                                                 barrier.BarrierEffect.BlockTravel);

            if (!blockBarriersWithPointGeometry.Any())
            {
                // Add warning message
                result.Warnings.Add(Properties.Messages.Warning_DelayPointBarriersNotSupported);
                return;
            }

            // Detecting delay point barriers and add warning message.
            if (blockBarriersWithPointGeometry.Count() != barriersWithPointGeometry.Count())
            {
                // Add warning message
                result.Warnings.Add(Properties.Messages.Warning_DelayPointBarriersNotSupported);
            }

            writer.WriteStartElement(BARRIERS_NODE_NAME);
            foreach (Barrier barrier in blockBarriersWithPointGeometry)
            {
                writer.WriteStartElement(BARRIER_NODE_NAME);
                writer.WriteAttributeString(ENABLED_ATTR_NAME, TRUE_VALUE);
                _SaveLocationNode(writer, (Point)barrier.Geometry, barrier.Name, "");
                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }
        private static void _SaveRouteSettings(XmlWriter writer, Route route, IVrpSolver solver,
            GrfExportResult result )
        {
            SolverSettings settings = solver.SolverSettings;
            writer.WriteStartElement(ROUTE_SETTINGS_NODE_NAME);

            writer.WriteStartElement(UTURNPOLICY_NODE_NAME);
            string uTurnPolicy = "";
            switch (settings.GetUTurnPolicy())
            {
                case UTurnPolicy.Nowhere:
                    uTurnPolicy = UTurnNowhere;
                    break;
                case UTurnPolicy.AtDeadEnds:
                    uTurnPolicy = UTurnAtDeadEnds;
                    break;
                case UTurnPolicy.AtDeadEndsAndIntersections:
                    // GRF doesnt support "U-Turns at Dead Ends and Intersections" UTurnPolicy,
                    // so replace it with "U-Turns at Dead Ends".
                    uTurnPolicy = UTurnAtDeadEnds;

                    // Add warning message
                    result.Warnings.Add(Properties.Messages.Warning_UTurnPolicyNotSupported);
                    break;
                default:
                    Debug.Assert(false); // NOTE: not supported
                    break;
            }
            writer.WriteAttributeString(VALUE_ATTR_NAME, uTurnPolicy);
            writer.WriteEndElement();

            writer.WriteStartElement(IMPEDANCE_ATTR_NODE_NAME);
            writer.WriteAttributeString(NAME_ATTR_NAME, solver.NetworkDescription.ImpedanceAttributeName);
            writer.WriteEndElement();

            writer.WriteStartElement(RESTRICTIONS_ATTR_NODE_NAME);

            ICollection<string> restrictions = SolveHelper.GetEnabledRestrictionNames(
                solver.SolverSettings.Restrictions);

            foreach (string name in restrictions)
            {
                writer.WriteStartElement(RESTRICTION_NODE_NAME);
                writer.WriteAttributeString(NAME_ATTR_NAME, name);
                writer.WriteAttributeString(TYPE_ATTR_NAME, STRICT_ATTR_NAME);
                writer.WriteAttributeString(STATUS_ATTR_NAME, ON_ATTR);
                writer.WriteEndElement();
            }
            writer.WriteEndElement();

            _SaveRouteAttributes(writer, route, solver);

            writer.WriteStartElement(TRIPPLANSETTINGS_NODE_NAME);
            writer.WriteStartElement(TRIP_START_NODE_NAME);
            string startTime = route.StartTime.Value.ToString("yyyy-MM-ddTHH:mm:ss");
            writer.WriteAttributeString(VALUE_ATTR_NAME, startTime);
            writer.WriteEndElement();
            writer.WriteEndElement();

            writer.WriteStartElement(DIRECTIONS_LENGTH_UNITS_NODE_NAME);
            string lengthUnits;
            RegionInfo ri = new RegionInfo(System.Threading.Thread.CurrentThread.CurrentCulture.LCID);
            if (ri.IsMetric)
                lengthUnits = KILOMETERS;
            else
                lengthUnits = MILES;
            writer.WriteAttributeString(VALUE_ATTR_NAME, lengthUnits);
            writer.WriteEndElement();

            writer.WriteStartElement(DIRECTIONS_CONTENT_NODE_NAME);
            writer.WriteAttributeString(VALUE_ATTR_NAME, "all");
            writer.WriteEndElement();

            writer.WriteEndElement();
        }
        /// <summary>
        /// Write point barriers with writer. If there are barriers with other geometry - add
        /// message to 'result'.
        /// </summary>
        /// <param name="writer">XmlWriter.</param>
        /// <param name="barriers">Collection of barriers to write.</param>
        /// <param name="result">GrfExportResult with messages.</param>
        private static void _SaveBarriers(XmlWriter writer, ICollection<Barrier> barriers, 
            GrfExportResult result)
        {
            if (barriers == null)
            {
                return;
            }

            // Detecting barriers with not point geometry and add warning message.
            var barriersWithNotPointGeometry = barriers.Where(barrier => (barrier.Geometry != null
                && !(barrier.Geometry is ESRI.ArcLogistics.Geometry.Point)) );
            if (barriersWithNotPointGeometry.Any())
            {
                result.Warnings.Add(Properties.Messages.Warning_BarriersNotSupported);
            }

            // If there is no point barriers then finish function execution.
            var barriersWithPointGeometry = barriers.Where(barrier =>
                barrier.Geometry is ESRI.ArcLogistics.Geometry.Point);
            if (!barriersWithPointGeometry.Any())
            {
                return;
            }

            // Select block point barriers.
            // Detected delay point barriers - add warning message.
            // If there is no block point barriers then finish function execution.
            var blockBarriersWithPointGeometry = barriersWithPointGeometry.Where(barrier =>
                barrier.BarrierEffect.BlockTravel);
            if (!blockBarriersWithPointGeometry.Any())
            {
                // Add warning message
                result.Warnings.Add(Properties.Messages.Warning_DelayPointBarriersNotSupported);
                return;
            }

            // Detecting delay point barriers and add warning message.
            if (blockBarriersWithPointGeometry.Count() != barriersWithPointGeometry.Count())
            {
                // Add warning message
                result.Warnings.Add(Properties.Messages.Warning_DelayPointBarriersNotSupported);
            }

            writer.WriteStartElement(BARRIERS_NODE_NAME);
            foreach (Barrier barrier in blockBarriersWithPointGeometry)
            {
                writer.WriteStartElement(BARRIER_NODE_NAME);
                writer.WriteAttributeString(ENABLED_ATTR_NAME, TRUE_VALUE);
                _SaveLocationNode(writer, (Point)barrier.Geometry, barrier.Name, "");
                writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }
        /// <summary>
        /// Export route to GRF file.
        /// </summary>
        /// <param name="writer">XMLWriter.</param>
        /// <param name="route">Route to export.</param>
        /// <param name="stops">Route's stops.</param>
        /// <param name="project">Project, which contains this route.</param>
        /// <param name="geocoder">Geocoder.</param>
        /// <param name="solver">Solver.</param>
        /// <param name="orderPropertiesToExport">Collection of order properties, which must be
        /// exported.</param>
        /// <param name="compress">Flag, shows compress result file or not.</param>
        /// <returns>GrfExportResult which contains messages about exporting.</returns>
        public static GrfExportResult ExportToGRF(XmlWriter writer, Route route, ICollection<Stop> stops, Project project,
            IGeocoder geocoder, IVrpSolver solver, ICollection<string> orderPropertiesToExport, bool compress)
        {
            GrfExportResult result = new GrfExportResult();

            writer.WriteStartElement(GRF_DOC_NODE_NAME);
            writer.WriteAttributeString(VERSION_ATTR_NAME, VERSION_VALUE);

            writer.WriteStartElement(ROUTEINFO_NODE_NAME);
            if (stops.Count > 0)
            {
                _SaveStops(
                    writer,
                    route,
                    stops,
                    orderPropertiesToExport,
                    project,
                    geocoder.AddressFields);
            }

            IDataObjectCollection<Barrier> barriers =
                (IDataObjectCollection<Barrier>)project.Barriers.Search(route.StartTime.Value.Date, true);
            _SaveBarriers(writer, barriers, result);
            _SaveRouteResults(writer, route, stops);
            writer.WriteEndElement();
            _SaveRouteSettings(writer, route, solver, result);
            writer.WriteEndElement();

            return result;
        }
        /// <summary>
        /// Execute send
        /// </summary>
        /// <param name="routesConfigs">Sent routes config collection</param>
        public void Execute(ICollection<SentRouteConfig> routesConfigs)
        {
            // Set page status to "Sending routes..."
            _app.MainWindow.StatusBar.SetStatus(_page, Properties.Resources.SendingRoutes);

            // Get properties to export
            IList<string> propertyNames = new List<string>(Order.GetPropertyNames(_app.Project.CapacitiesInfo,
                _app.Project.OrderCustomPropertiesInfo, _app.Geocoder.AddressFields)); ;

            int totalSended = 0;
            int totalSendedSuccessfully = 0;
            List<MessageDetail> details = new List<MessageDetail>();

            // List of all selected route's export warning messages.
            List<string> exportWarningMessages = new List<string>();

            foreach (SentRouteConfig sendedRouteConfig in routesConfigs)
            {
                // If route checked to be exported - export it
                if (sendedRouteConfig.IsChecked)
                {
                    GrfExportResult exportWarnings = new GrfExportResult();

                    bool result = _ProcessSendedRouteConfig(sendedRouteConfig.Route, details,
                        propertyNames, ref exportWarnings);

                    // Increase total sent routes counrer
                    totalSended++;

                    // Increase successfully sent routes counter
                    if (result)
                        totalSendedSuccessfully++;

                    // If there was messages during export then add unique
                    // route's export result messages to list of export results warning messages.
                    foreach(string message in exportWarnings.Warnings)
                        if (!exportWarningMessages.Contains(message))
                            exportWarningMessages.Add(message);
                }
            }

            // Show result in message window.
            _ShowResults(totalSended, totalSendedSuccessfully, exportWarningMessages, details);
        }
        /// <summary>
        /// Process sending route
        /// </summary>
        /// <param name="route">Sent route</param>
        /// <param name="details">Results message details</param>
        /// <param name="selectedPropertyNames">Names of selected properties</param>
        /// <returns>True if successfully sent</returns>
        private bool _ProcessSendedRouteConfig(Route route, IList<MessageDetail> details,
            IList<string> selectedPropertyNames, ref GrfExportResult exportResult)
        {
            bool result = false;
            Link link = null;
            string message = string.Empty;
            try
            {
                message = _DoSend(route, selectedPropertyNames, ref exportResult);
                result = true;
            }
            catch (SettingsException ex)
            {
                Logger.Error(ex);
                message = string.Format(Properties.Resources.SendingFailed,
                    route.Name, ex.Message);
            }
            catch (IOException ex)
            {
                Logger.Error(ex);
                message = string.Format(Properties.Resources.SendingFailed,
                    route.Name, ex.Message);
            }
            catch (InvalidOperationException ex)
            {
                Logger.Error(ex);
                message = string.Format(Properties.Resources.SendingFailed,
                    route.Name, ex.Message);
            }
            catch (UnauthorizedAccessException ex)
            {
                Logger.Error(ex);
                message = string.Format(Properties.Resources.SendingFailed,
                    route.Name, ex.Message);
            }
            catch (SmtpException ex)
            {
                Logger.Error(ex);
                string innerMessage = Properties.Resources.MailConnectionError;
                message = string.Format(Properties.Resources.SendingFailed,
                    route.Name, innerMessage);
            }
            catch (MailerSettingsException ex)
            {
                Logger.Error(ex);
                message = string.Format(Properties.Resources.SendingFailed,
                    route.Name, ex.Message);

                link = new Link(Properties.Resources.ExportToNavigatorLink,
                                     ExportToNavigatorPreferencesPagePath, LinkType.Page);
            }
            catch (Exception ex)
            {
                Logger.Error(ex);

                if (ex is AuthenticationException || ex is CommunicationException)
                {
                    string service = (string)_app.FindResource("ServiceNameRouting");
                    message = AddServiceMessageWithDetail(service, ex);

                    if (ex is AuthenticationException)
                    {
                        link = new Link((string)_app.FindResource("LicencePanelText"),
                           ESRI.ArcLogistics.App.Pages.PagePaths.LicensePagePath, LinkType.Page);
                    }
                }
                else
                {
                    string innerMessage = Properties.Resources.UnknownError;
                    message = string.Format(Properties.Resources.SendingFailed,
                        route.Name, innerMessage);
                }
            }

            MessageDetail messageDetail;

            // If route sent successfully - add information details
            // If route was not sent - add error details
            if (result)
            {
                messageDetail = new MessageDetail(MessageType.Information, message);
            }
            else
            {
                if (link == null)
                {
                    messageDetail = new MessageDetail(MessageType.Error, message);
                }
                else
                {
                    messageDetail = new MessageDetail(MessageType.Error, message, link);
                }
            }

            details.Add(messageDetail);

            return result;
        }
        /// <summary>
        /// Make send
        /// </summary>
        /// <param name="route">Route for sending</param>
        /// <param name="selectedPropertyNames">Names of selected properties</param>
        /// <returns>Error message</returns>
        private string _DoSend(Route route, IList<string> selectedPropertyNames, ref GrfExportResult exportResult)
        {
            string message = string.Empty;

            // Generate filename
            DateTime routeDate = route.StartTime.Value.Date;
            string filename = string.Format(FILENAME_FORMAT, routeDate.ToString("yyyyMMdd"),
                route.Name, DateTime.Now.ToString("yyyyMMdd_HHmmss"));

            // Add extention to filename
            filename += (_grfExporterConfig.RouteGrfCompression) ? GZExt : GRFExt;

            // Get mobile device
            MobileDevice mobileDevice = route.Driver.MobileDevice;
            if (mobileDevice == null)
                mobileDevice = route.Vehicle.MobileDevice;

            // React on empty mobile device
            if (mobileDevice == null)
            {
                string errorMessage = Properties.Resources.MobileDeviceEmpty;
                throw new SettingsException(errorMessage);
            }

            // Do send depends on sync type
            switch (mobileDevice.SyncType)
            {
                case SyncType.ActiveSync:
                    exportResult = _SendToActiveSync(route, filename, selectedPropertyNames, mobileDevice);
                    message = string.Format(Properties.Resources.SuccessfullySentToDevice,
                                route.Name, mobileDevice.Name);
                    break;
                case SyncType.EMail:
                    exportResult = _SendToEMail(route, filename, selectedPropertyNames, mobileDevice);
                    message = string.Format(Properties.Resources.SuccessfullyMailed,
                        route.Name, mobileDevice.EmailAddress);
                    break;
                case SyncType.Folder:
                    exportResult =_SendToFolder(route, filename, selectedPropertyNames, mobileDevice);
                    message = string.Format(Properties.Resources.SuccessfullySentToFolder,
                        route.Name, mobileDevice.SyncFolder);
                    break;
                case SyncType.None:
                    {
                        // React on empty sync type
                        string errorMessage = (mobileDevice == route.Driver.MobileDevice) ?
                            Properties.Resources.SyncTypeNotSelectedForDriverDevice :
                            Properties.Resources.SyncTypeNotSelectedForVehicleDevice;
                        throw new SettingsException(errorMessage);
                    }
                default:
                    {
                        throw new SettingsException( Properties.Resources.SyncTypeIsNotSupported);
                    }
            }

            return message;
        }
        private static void _SaveRouteSettings(XmlWriter writer, Route route, IVrpSolver solver,
                                               GrfExportResult result)
        {
            SolverSettings settings = solver.SolverSettings;

            writer.WriteStartElement(ROUTE_SETTINGS_NODE_NAME);

            writer.WriteStartElement(UTURNPOLICY_NODE_NAME);
            string uTurnPolicy = "";

            switch (settings.GetUTurnPolicy())
            {
            case UTurnPolicy.Nowhere:
                uTurnPolicy = UTurnNowhere;
                break;

            case UTurnPolicy.AtDeadEnds:
                uTurnPolicy = UTurnAtDeadEnds;
                break;

            case UTurnPolicy.AtDeadEndsAndIntersections:
                // GRF doesnt support "U-Turns at Dead Ends and Intersections" UTurnPolicy,
                // so replace it with "U-Turns at Dead Ends".
                //fjk: updated so UTurnEverywhere respected
                uTurnPolicy = UTurnEverywhere;

                //fjk: commented out b/c of the above change
                // Add warning message
                //result.Warnings.Add(Properties.Messages.Warning_UTurnPolicyNotSupported);
                break;

            default:
                Debug.Assert(false);     // NOTE: not supported
                break;
            }
            writer.WriteAttributeString(VALUE_ATTR_NAME, uTurnPolicy);
            writer.WriteEndElement();

            writer.WriteStartElement(IMPEDANCE_ATTR_NODE_NAME);
            writer.WriteAttributeString(NAME_ATTR_NAME, "Time");
            writer.WriteEndElement();

            writer.WriteStartElement(RESTRICTIONS_ATTR_NODE_NAME);

            ICollection <string> restrictions = SolveHelper.GetEnabledRestrictionNames(
                solver.SolverSettings.Restrictions);

            foreach (string name in restrictions)
            {
                writer.WriteStartElement(RESTRICTION_NODE_NAME);
                writer.WriteAttributeString(NAME_ATTR_NAME, name);
                writer.WriteAttributeString(TYPE_ATTR_NAME, STRICT_ATTR_NAME);
                writer.WriteAttributeString(STATUS_ATTR_NAME, ON_ATTR);
                writer.WriteEndElement();
            }
            writer.WriteEndElement();

            _SaveRouteAttributes(writer, route, solver);

            writer.WriteStartElement(TRIPPLANSETTINGS_NODE_NAME);
            writer.WriteStartElement(TRIP_START_NODE_NAME);
            string startTime = route.StartTime.Value.ToString("yyyy-MM-ddTHH:mm:ss");

            writer.WriteAttributeString(VALUE_ATTR_NAME, startTime);
            writer.WriteEndElement();
            writer.WriteEndElement();

            writer.WriteStartElement(DIRECTIONS_LENGTH_UNITS_NODE_NAME);
            string     lengthUnits;
            RegionInfo ri = new RegionInfo(System.Threading.Thread.CurrentThread.CurrentCulture.LCID);

            if (ri.IsMetric)
            {
                lengthUnits = KILOMETERS;
            }
            else
            {
                lengthUnits = MILES;
            }
            writer.WriteAttributeString(VALUE_ATTR_NAME, lengthUnits);
            writer.WriteEndElement();

            writer.WriteStartElement(DIRECTIONS_CONTENT_NODE_NAME);
            writer.WriteAttributeString(VALUE_ATTR_NAME, "all");
            writer.WriteEndElement();

            writer.WriteEndElement();
        }