/// <summary>
        /// Factory method creates a service helper.
        /// </summary>
        public virtual async Task <IObaServiceHelper> CreateHelperAsync(ObaMethod obaMethod)
        {
            // Find the closest region to the user's location:
            var usersRegion = await this.FindClosestRegionAsync();

            return(new ObaServiceHelper(usersRegion, obaMethod));
        }
        /// <summary>
        /// Returns real time tracking data for the buses at a particular stop.
        /// </summary>
        public async Task <TrackingData[]> GetTrackingDataForStopAsync(string stopId, CancellationToken token)
        {
            try
            {
                ObaMethod method = ObaMethod.arrivals_and_departures_for_stop;
                var       helper = await this.Factory.CreateHelperAsync(method);

                helper.SetId(stopId);
                helper.AddToQueryString("minutesAfter", "60");

                XDocument doc = await helper.SendAndRecieveAsync(Constants.NoCacheAge, token);

                if (doc != null)
                {
                    DateTime serverTime = doc.Root.GetFirstElementValue <long>("currentTime").ToDateTime();

                    string stopName = doc.Descendants("stop").First().GetFirstElementValue <string>("name");

                    // Find all of the stops in the payload that have this route id:
                    return((from arrivalAndDepartureElement in doc.Descendants("arrivalAndDeparture")
                            select new TrackingData(serverTime, stopId, stopName, arrivalAndDepartureElement)).ToArray());
                }
            }
            catch (Exception e)
            {
                if (e is OperationCanceledException)
                {
                    throw;
                }
            }

            return(new TrackingData[] { });
        }
        /// <summary>
        /// Gets the trip data for a specific trip.
        /// </summary>
        public async Task <TripDetails> GetTripDetailsAsync(string tripId)
        {
            try
            {
                ObaMethod method = ObaMethod.trip_details;

                var helper = await this.Factory.CreateHelperAsync(method);

                helper.SetId(tripId);

                XDocument doc = await helper.SendAndRecieveAsync(Constants.NoCacheAge);

                if (doc != null)
                {
                    DateTime serverTime   = doc.Root.GetFirstElementValue <long>("currentTime").ToDateTime();
                    XElement entryElement = doc.Descendants("entry").First();
                    return(new TripDetails(entryElement, serverTime));
                }
            }
            catch
            {
            }

            return(new TripDetails());
        }
        /// <summary>
        /// Get all routes for specified region and agency from Oba
        /// </summary>
        /// <param name="region">specified region</param>
        /// <param name="agency">specified agency</param>
        /// <returns>IEnumerable of routes</returns>
        public async Task <IEnumerable <Route> > GetAllRoutesAsync(Region region, Agency agency)
        {
            IList <Route> routes = new List <Route>();
            ObaMethod     method = ObaMethod.Routes_for_agency;
            var           helper = new ClientHelper(region, method, this.apiKey);

            helper.SetId(agency.Id);

            var routesXml = await helper.SendAndReceiveAsync();

            if (routesXml != null)
            {
                var xmlSerializer = new XmlSerializer(typeof(Route));
                foreach (var item in routesXml.Descendants("route"))
                {
                    using (var reader = item.CreateReader())
                    {
                        var routeItem = (Route)xmlSerializer.Deserialize(reader);

                        // Set the RawContent to route XElement
                        routeItem.RawContent = item.ToString();
                        routes.Add(routeItem);
                    }
                }
            }

            return(routes);
        }
        /// <summary>
        /// Returns route data for a particular route. Since there are two directions for each route,
        /// this method returns an array of routes - one for each direction.
        /// </summary>
        public async Task <RouteData[]> GetRouteDataAsync(string routeId)
        {
            try
            {
                ObaMethod method = ObaMethod.stops_for_route;

                var helper = await this.Factory.CreateHelperAsync(method);

                helper.SetId(routeId);

                var doc = await helper.SendAndRecieveAsync();

                if (doc != null)
                {
                    XElement dataElement = doc.Descendants("data").First();
                    return((from stopGroupElement in dataElement.Descendants("stopGroup")
                            select new RouteData(dataElement, stopGroupElement)).ToArray());
                }
            }
            catch
            {
            }

            return(new RouteData[] { });
        }
        /// <summary>
        /// Returns all of the route Ids for a particular agency.
        /// </summary>
        public async Task <Route[]> GetAllRouteIdsForAgencyAsync(Agency agency)
        {
            try
            {
                ObaMethod method = ObaMethod.routes_for_agency;
                var       helper = await this.Factory.CreateHelperAsync(method);

                helper.SetId(agency.Id);

                var doc = await helper.SendAndRecieveAsync(cacheTimeout : 5 * 24 * 60 * 60 /* 5 days */);

                if (doc != null)
                {
                    return((from routeElement in doc.Descendants("route")
                            select new Route(routeElement)
                    {
                        Agency = agency
                    }).ToArray());
                }
            }
            catch
            {
            }

            return(new Route[] { });
        }
        /// <summary>
        /// Get stop details for specified region and stop Id
        /// </summary>
        /// <param name="region">specified region</param>
        /// <param name="stopId">specified stop Id</param>
        /// <returns>Stop details</returns>
        public async Task <Stop> GetStopDetailsAsync(Region region, string stopId)
        {
            Stop      stopItem = null;
            ObaMethod method   = ObaMethod.Stop;
            var       helper   = new ClientHelper(region, method, this.apiKey);

            helper.SetId(stopId);

            var stopXml = await helper.SendAndReceiveAsync();

            if (stopXml != null)
            {
                var xmlSerializer = new XmlSerializer(typeof(Stop));
                var item          = stopXml.Descendants("entry").First();
                using (var reader = item.CreateReader())
                {
                    stopItem = (Stop)xmlSerializer.Deserialize(reader);

                    // Set the RawContent to Stop XElement
                    stopItem.RawContent = item.ToString();
                }
            }

            return(stopItem);
        }
        /// <summary>
        /// Get all agencies for the specified region from Oba
        /// </summary>
        /// <param name="region">specified region</param>
        /// <returns>IEnumerable of agencies</returns>
        public async Task <IEnumerable <Agency> > GetAllAgenciesAsync(Region region)
        {
            IList <Agency> agencies    = new List <Agency>();
            ObaMethod      method      = ObaMethod.Agencies_with_coverage;
            var            helper      = new ClientHelper(region, method, this.apiKey);
            var            agenciesXml = await helper.SendAndReceiveAsync();

            if (agenciesXml != null)
            {
                var xmlSerializer = new XmlSerializer(typeof(Agency));
                foreach (var item in agenciesXml.Descendants("agency"))
                {
                    using (var reader = item.CreateReader())
                    {
                        var agencyItem = (Agency)xmlSerializer.Deserialize(reader);

                        // Set the RawContent to Agency XElement
                        agencyItem.RawContent = item.ToString();
                        agencies.Add(agencyItem);
                    }
                }
            }

            return(agencies);
        }
            /// <summary>
            /// Creates the service helper.
            /// </summary>
            public ObaServiceHelper(Region region, ObaMethod obaMethod)
            {
                this.obaMethod  = obaMethod;
                this.region     = region;
                this.serviceUrl = this.region.RegionUrl;
                this.id         = null;

                this.uriBuilder = new UriBuilder(serviceUrl);
                this.SetDefaultPath();

                this.queryStringMap            = new Dictionary <string, string>();
                this.queryStringMap["key"]     = Constants.ObaApiKey;
                this.queryStringMap["Version"] = "2";
            }
        /// <summary>
        /// Initializes a new instance of the <see cref="ClientHelper"/> class.
        /// </summary>
        /// <param name="region">region</param>
        /// <param name="obaMethod">oba method</param>
        /// <param name="apiKey">OBA API key</param>
        internal ClientHelper(Region region, ObaMethod obaMethod, string apiKey)
        {
            this.obaMethod  = obaMethod;
            this.region     = region;
            this.serviceUrl = this.region.ObaBaseUrl;
            this.id         = null;
            this.apiKey     = apiKey;

            this.uriBuilder = new UriBuilder(this.serviceUrl);
            this.SetDefaultPath();

            this.queryStringMap            = new Dictionary <string, string>();
            this.queryStringMap["key"]     = apiKey;
            this.queryStringMap["Version"] = "2";
        }
        /// <summary>
        /// Get stops details for specified region and route Id
        /// </summary>
        /// <param name="region">specified region</param>
        /// <param name="routeId">specified route Id</param>
        /// <returns>Stops details</returns>
        public async Task <Stop[]> GetStopsForRouteAsync(Region region, string routeId)
        {
            Stop[]    stops  = null;
            ObaMethod method = ObaMethod.Stops_for_route;
            var       helper = new ClientHelper(region, method, this.apiKey);

            helper.SetId(routeId);

            var stopsXml = await helper.SendAndReceiveAsync();

            if (stopsXml != null)
            {
                var xmlSerializer = new XmlSerializer(typeof(Stop));
                return((from stopElement in stopsXml.Descendants("stop")
                        select new Stop(stopElement)).ToArray());
            }

            return(stops);
        }
        /// <summary>
        /// Get all stop ids for specified region and agency from Oba
        /// </summary>
        /// <param name="region">specified region</param>
        /// <param name="agency">specified agency</param>
        /// <returns>IEnumerable of stop Ids</returns>
        public async Task <IEnumerable <string> > GetAllStopIdsAsync(Region region, Agency agency)
        {
            IList <string> stopIds = new List <string>();
            ObaMethod      method  = ObaMethod.Stop_ids_for_agency;
            var            helper  = new ClientHelper(region, method, this.apiKey);

            helper.SetId(agency.Id);

            var stopsXml = await helper.SendAndReceiveAsync();

            if (stopsXml != null)
            {
                foreach (var item in stopsXml.Descendants("string"))
                {
                    stopIds.Add(item.Value);
                }
            }

            return(stopIds);
        }
        /// <summary>
        /// Returns the schedule for a particular stop / route combination.
        /// </summary>
        public async Task <StopRouteSchedule[]> GetScheduleForStopAndRouteAsync(string stopId, string routeId, DateTime date)
        {
            XDocument doc = null;

            try
            {
                ObaMethod method = ObaMethod.schedule_for_stop;
                var       helper = await this.Factory.CreateHelperAsync(method);

                helper.SetId(stopId);
                helper.AddToQueryString("date", date.ToString("yyyy-MM-dd"));

                doc = await helper.SendAndRecieveAsync(Constants.NoCacheAge);

                if (doc != null)
                {
                    var stopRouteScheduleElement = doc.Descendants("stopRouteSchedule")
                                                   .Where(xe => string.Equals(xe.GetFirstElementValue <string>("routeId"), routeId, StringComparison.OrdinalIgnoreCase))
                                                   .FirstOrDefault();

                    if (stopRouteScheduleElement == null)
                    {
                        throw new ArgumentException(string.Format("Unknown route / stop combination {0}/{1}", routeId, stopId));
                    }

                    DateTime serverTime = doc.Root.GetFirstElementValue <long>("currentTime").ToDateTime();
                    return((from stopRouteDirectionScheduleElement in stopRouteScheduleElement.Descendants("stopRouteDirectionSchedule")
                            select new StopRouteSchedule(serverTime, stopRouteDirectionScheduleElement)).ToArray());
                }
            }
            catch (Exception e)
            {
                // Make sure ArgumentException's bubble up.
                if (e is ArgumentException)
                {
                    throw;
                }
            }

            return(new StopRouteSchedule[] { });
        }
        /// <summary>
        /// Returns all of the agencies that OBA serves.
        /// </summary>
        public async Task <Agency[]> GetAllAgenciesAsync()
        {
            try
            {
                ObaMethod method = ObaMethod.agencies_with_coverage;
                var       helper = await this.Factory.CreateHelperAsync(method);

                var doc = await helper.SendAndRecieveAsync(cacheTimeout : 30 * 24 * 60 * 60 /* 30 days */);

                if (doc != null)
                {
                    return((from agencyWithCoverageElement in doc.Descendants("agency")
                            select new Agency(agencyWithCoverageElement)).ToArray());
                }
            }
            catch
            {
            }

            return(new Agency[] { });
        }
        /// <summary>
        /// Returns the routes for a particular stop.
        /// </summary>
        public async Task <Route[]> GetRoutesForStopAsync(string stopId)
        {
            try
            {
                ObaMethod method = ObaMethod.stop;

                var helper = await this.Factory.CreateHelperAsync(method);

                helper.SetId(stopId);
                var doc = await helper.SendAndRecieveAsync();

                if (doc != null)
                {
                    return((from routeElement in doc.Descendants("route")
                            select new Route(routeElement)).ToArray());
                }
            }
            catch
            {
            }

            return(new Route[] { });
        }
        /// <summary>
        /// Returns the details for a particular stop.
        /// </summary>
        public async Task <Stop> GetStopDetailsAsync(string stopId)
        {
            try
            {
                ObaMethod method = ObaMethod.stop;
                var       helper = await this.Factory.CreateHelperAsync(method);

                helper.SetId(stopId);

                var doc = await helper.SendAndRecieveAsync();

                // Find all of the stops in the payload that have this route id:
                if (doc != null)
                {
                    var entryElement = doc.Descendants("entry").First();
                    return(new Stop(entryElement));
                }
            }
            catch
            {
            }

            return(null);
        }
        /// <summary>
        /// Returns the stops for a particular route.
        /// </summary>
        public async Task <Stop[]> GetStopsForRouteAsync(string routeId)
        {
            try
            {
                ObaMethod method = ObaMethod.stops_for_route;
                var       helper = await this.Factory.CreateHelperAsync(method);

                helper.SetId(routeId);

                var doc = await helper.SendAndRecieveAsync();

                // Find all of the stops in the payload that have this route id:
                if (doc != null)
                {
                    return((from stopElement in doc.Descendants("stop")
                            select new Stop(stopElement)).ToArray());
                }
            }
            catch
            {
            }

            return(new Stop[] { });
        }
            /// <summary>
            /// Creates the service helper.
            /// </summary>
            public ObaServiceHelper(Region region, ObaMethod obaMethod)
            {
                this.obaMethod = obaMethod;
                this.region = region;
                this.serviceUrl = this.region.RegionUrl;
                this.id = null;

                this.uriBuilder = new UriBuilder(serviceUrl);
                this.SetDefaultPath();

                this.queryStringMap = new Dictionary<string, string>();
                this.queryStringMap["key"] = UtilitiesConstants.API_KEY;
                this.queryStringMap["Version"] = "2";
            }
 /// <summary>
 /// Factory method creates a service helper.
 /// </summary>
 public virtual async Task<IObaServiceHelper> CreateHelperAsync(ObaMethod obaMethod)
 {
     // Find the closest region to the user's location:
     var usersRegion = await this.FindClosestRegionAsync();
     return new ObaServiceHelper(usersRegion, obaMethod);
 }