/// <summary>
        /// 
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public List<Location> getAllLocationsStream(string filename)
        {
            try
            {
                List<Location> lLocations = new List<Location>();
                using (StreamReader sr = new StreamReader(filename))
                {
                    string line;
                    while ((line = sr.ReadLine()) != null)
                    {
                        string[] data = line.Split(new[] { "\",\"" }, StringSplitOptions.RemoveEmptyEntries);
                        string Name = getName(data);
                        double Latitude = getLatitude(data);
                        double Longitude = getLongitude(data);
                        Location loc = new Location(Name, Latitude, Longitude);
                        lLocations.Add(loc);

                    }
                    sr.Close();
                }

                return lLocations;
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="Latitude"></param>
        /// <param name="Longitude"></param>
        /// <param name="maxDistance"></param>
        /// <param name="maxResults"></param>
        /// <returns>
        ///     /api/GetLocations?Latitude=52.950175&Longitude=4.766285&maxDistance=10000&maxResults=100
        /// </returns>
        public List<Location> Get(double Latitude, double Longitude, int maxDistance, int maxResults)
        {
            List<Location> lLocations = new List<Location>();

            using (GeoQuerys gq = new GeoQuerys())
            {
                Location location = new Location("P1",Latitude, Longitude);
                lLocations = gq.GetLocations(location, maxDistance, maxResults);
            }
            return lLocations;
        }
        public void GetLocationsTest()
        {
            GeoQuerys target = new GeoQuerys(); // TODO: Initialize to an appropriate value
            Location pLocation = new Location("P1", double.Parse("52.2165425"), double.Parse("5.4778534")); // TODO: Initialize to an appropriate value
            int maxDistance = 10000; // TODO: Initialize to an appropriate value
            int maxResults = 100; // TODO: Initialize to an appropriate value
            SearchResults sr = new SearchResults();
            SearchResults actual;

            sr.StartSearch("52.2165425", "5.4778534", maxDistance.ToString(), maxResults.ToString());
            actual = target.GetLocations(pLocation, maxDistance, maxResults, sr);
            actual.EndSearch("");

            Assert.AreEqual(maxResults.ToString(), actual.maxResults);
        }
        /// <summary>
        /// Method to search locations close to other one 
        /// </summary>
        /// <param name="pLocation">Location</param>
        /// <param name="maxDistance">Max Distance</param>
        /// <param name="maxResults">Max number of Results</param>
        /// <returns></returns>
        public SearchResults GetLocations(Location pLocation, int maxDistance, int maxResults, SearchResults sr)
        {

            List<Location> filteredList = new List<Location>();
            SearchResults srDetailed = sr;
            try
            {
                //Get all Locations
                List<Location> lLocations = new List<Location>();
                using (GeoData gData = new GeoData())
                {
                    DateTime t1 = DateTime.UtcNow;

                    lLocations = gData.getAllLocations();
                    
                    DateTime t2 = DateTime.UtcNow;
                    TimeSpan t = t2 - t1;
                    double d = t.TotalSeconds;
                    srDetailed.ReadDataDuration = d;
                    srDetailed.FileRecords = lLocations.Count;
                }

                //Sort by Distance
                //Added Parallelism
                List<Location> SortedList = lLocations.AsParallel().WithDegreeOfParallelism(4).OrderBy(o => o.CalculateDistance(pLocation)).ToList();

                //Filter the Locations with the same Distance, Longitude and Latitude
                List<Location> filterRepeated = SortedList.AsParallel().WithDegreeOfParallelism(4).GroupBy(x => new { x.Distance, x.Longitude, x.Latitude })
                                                   .Select(g => g.First())
                                                   .ToList();

                //Filter by the max Number of Results
                filteredList = filterRepeated.Where(x => x.Distance <= maxDistance).Take(maxResults).ToList();
                srDetailed.Locations = filteredList;
            }
            catch(Exception ex)
            {
                throw ex;
            }

            return srDetailed;
        }
        public SearchResults GetLocations(string Latitude, string Longitude, string maxDistance, string maxResults)
        {
            ///I've implmented a class to register the Exceptions and traces of the application.
            ///This Trace class is connected with a 3rd Party System called franrodriguez.loggly.com

            using (Trace t = new Trace("SearchService", "GetLocations"))
            {
                using (SearchResults sr = new SearchResults())
                {
                    SearchResults srDetailed = new SearchResults();
                    try
                    {
                        //Start the Search
                        sr.StartSearch(Latitude, Longitude, maxDistance, maxResults);

                        //Lock the object to manage the interlocking of the request
                        lock (this.ThisLock)
                        {
                            //Call to the internal Controller to make the search
                            using (IGeoQuerys gq = new GeoQuerys())
                            {
                                Location location = new Location("P1", double.Parse(Latitude), double.Parse(Longitude));
                                srDetailed = gq.GetLocations(location, Int32.Parse(maxDistance), Int32.Parse(maxResults),sr);

                                //End the Search
                                srDetailed.EndSearch("");
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        t.Error(e.Message.ToString(), e);
                        srDetailed.EndSearch(e.Message.ToString());
                    }

                    //Give back the json object
                    return srDetailed;
                }
            }
        }
        /// <summary>
        /// Calculates the distance between this location and another one, in meters.
        /// </summary>
        public double CalculateDistance(Location location)
        {
            try
            {
                var rlat1 = Math.PI * Latitude / 180;
                var rlat2 = Math.PI * location.Latitude / 180;
                var rlon1 = Math.PI * Longitude / 180;
                var rlon2 = Math.PI * location.Longitude / 180;
                var theta = Longitude - location.Longitude;
                var rtheta = Math.PI * theta / 180;
                var dist = Math.Sin(rlat1) * Math.Sin(rlat2) + Math.Cos(rlat1) * Math.Cos(rlat2) * Math.Cos(rtheta);
                dist = Math.Acos(dist);
                dist = dist * 180 / Math.PI;
                dist = dist * 60 * 1.1515;

                Distance = dist * 1609.344;
            }
            catch
            {
                Distance = 0;
            }

            return Distance;
        }
        /// <summary>
        /// Method to search locations close to other one 
        /// </summary>
        /// <param name="pLocation">Location</param>
        /// <param name="maxDistance">Max Distance</param>
        /// <param name="maxResults">Max number of Results</param>
        /// <returns></returns>
        public async Task<List<Location>> GetLocationsAsync(Location pLocation, int maxDistance, int maxResults)
        {

            List<Location> filteredList = new List<Location>();
            try
            {
                //Get all Locations
                List<Location> lLocationsJoined = new List<Location>();

                using (GeoData gData = new GeoData())
                {
                    int totalL = 1000000;
                    int paralellism = 100000;
                    int nThreads = totalL / paralellism;
                    int[] arrayRanges = new int[nThreads];

                    for (int i = 0; i < nThreads; i++) arrayRanges[i] = i * paralellism;

                    IEnumerable<Task<List<Location>>> getLocationsTasksQuery = from range in arrayRanges
                                                                               select gData.getAllLocationsByRange(range,paralellism);

                    // Use ToArray to execute the query and start the getLocations tasks.
                    Task<List<Location>>[] downloadTasks = getLocationsTasksQuery.ToArray();

                    List<Location>[] lLocations = await Task.WhenAll(downloadTasks);
                    
                }

                //Sort by Distance
                List<Location> SortedList = lLocationsJoined.OrderBy(o => o.CalculateDistance(pLocation)).ToList();

                //Filter the Locations with the same Distance, Longitude and Latitude
                List<Location> filterRepeated = SortedList.GroupBy(x => new { x.Distance, x.Longitude, x.Latitude })
                                                   .Select(g => g.First())
                                                   .ToList();

                //Filter by the max Number of Results
                filteredList = filterRepeated.Where(x => x.Distance <= maxDistance).Take(maxResults).ToList();
            }
            catch
            {
                throw;
            }

            return filteredList;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public async Task<List<Location>> geLocationsByRange(string filename, int index, int offset)
        {
            try
            {
                List<Location> lLocations = new List<Location>();
                using (StreamReader sr = new StreamReader(filename))
                {
                    string line;
                    int lineNbr = 0;
                    int cont = 0;
                    while ((line = await sr.ReadLineAsync()) != null)
                    {
                        lineNbr++;
                        if (lineNbr >= index)
                        {
                            string[] data = line.Split(new[] { "\",\"" }, StringSplitOptions.RemoveEmptyEntries);
                            string Name = getName(data);
                            double Latitude = getLatitude(data);
                            double Longitude = getLongitude(data);
                            Location loc = new Location(Name, Latitude, Longitude);
                            lLocations.Add(loc);
                            cont++;
                        }

                        if (cont == offset) break;
                    }
                    sr.Close();
                }

                return lLocations;
            }
            catch
            {
                throw;
            }
        }
        /// <summary>
        /// Method to get the Location using File.ReadAllLines and ParallelFor
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public List<Location> getAllLocationsStreamParallel(string filename)
        {
            try
            {
                List<Location> lLocations = new List<Location>();
                string[] AllLines = File.ReadAllLines(filename);

                var options = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 4 };

                Parallel.For(0, AllLines.Length, options, i =>
                {
                    try
                    {

                        string[] data = AllLines[i].Split(new[] { "\",\"" }, StringSplitOptions.RemoveEmptyEntries);
                        string Name = getName(data);
                        double Latitude = getLatitude(data);
                        double Longitude = getLongitude(data);
                        Location loc = new Location(Name, Latitude, Longitude);

                        lock (lLocations)
                        {
                            lLocations.Add(loc);
                        }

                    }
                    catch { }
                });

                return lLocations;
            }
            catch(Exception ex)
            {
                throw ex;
            }

        }