context: using System;
  using System.Collections.Generic;
  using System.Device.Location;
  using System.Globalization;
  using System.Linq;
  using System.Threading.Tasks;
  using GoogleMapsApi;
  using GoogleMapsApi.Entities.Common;
  using GoogleMapsApi.Entities.Directions.Request;
  using GoogleMapsApi.Entities.Directions.Response;
  using POGOProtos.Data.Player;
  using POGOProtos.Enums;
  using POGOProtos.Inventory.Item;
  using POGOProtos.Map.Fort;
  using POGOProtos.Map.Pokemon;
  using POGOProtos.Networking.Responses;
  using PokemonGo.RocketAPI;
  using PokemonGo.RocketAPI.Exceptions;
  using PokemonGo.RocketAPI.Helpers;
  using PokeMaster.Logic;
  using PokeMaster.Logic.Functions;
  using PokeMaster.Logic.Shared;
  using PokeMaster.Logic.Utils;
  using Google.Protobuf.Collections;
  using PokemonGo.RocketAPI.Shared;
  using System.Net.Http;
  using System.Text;
  
  namespace PokeMaster.Logic
  {
      public class Logic
      {
  
          #region Members and Constructor
  
          public static Client objClient;
          public readonly ISettings BotSettings;
          public TelegramUtil Telegram;
          public BotStats BotStats;
          public readonly Navigation navigation;
          public const double SpeedDownTo = 10 / 3.6;
          public readonly LogicInfoObservable infoObservable;
          private readonly PokeVisionUtil pokevision;
          public bool pokeballoutofstock;
          public static Logic Instance;
          private readonly List<string> lureEncounters = new List<string>();
          public static int FailedSoftban = 0;
          public double lastsearchtimestamp = 0;
  
  
          public string Lure = "lureId";
          private bool addedlure = false;
          public Sniper sniperLogic;
          public static bool restartLogic = true;
  
          public TutorialFunctions Tutorial = new TutorialFunctions();
  
          #endregion
  
          #region Constructor
          public Logic(ISettings botSettings, LogicInfoObservable infoObservable)
          {
              this.BotSettings = botSettings;
              var clientSettings = new PokemonGo.RocketAPI.Shared.ClientSettings(botSettings.pFHashKey, botSettings.DefaultLatitude, botSettings.DefaultLongitude, botSettings.DefaultAltitude,
                        botSettings.proxySettings.hostName, botSettings.proxySettings.port, botSettings.proxySettings.username, botSettings.proxySettings.password,
                        botSettings.AuthType, botSettings.Username, botSettings.Password, botSettings.LastTimePlayedTs, GlobalVars.BotApiSupportedVersion);
              objClient = new Client(clientSettings);
              objClient.setFailure(new ApiFailureStrat(objClient));
              objClient.EvMakeTutorial += MakeTutorial;
              LocaleInfo.SetValues(GlobalVars.LocaleCountry, GlobalVars.LocaleLanguage, GlobalVars.LocaleTimeZone, GlobalVars.LocaleLanguage + "-" + GlobalVars.LocaleCountry);
              BotStats = new BotStats();
              navigation = new Navigation(objClient, botSettings);
              pokevision = new PokeVisionUtil();
              this.infoObservable = infoObservable;
              Instance = this;
              sniperLogic = new Sniper(objClient, botSettings);
              PokemonGo.RocketAPI.Shared.KeyCollection.Load();
  
              #region Set Session values
              Setout.sessionStart = DateTime.UtcNow;
              Setout.pokemonCatchCount = 0;
              Setout.pokeStopFarmedCount = 0;
              if (GlobalVars.ContinueLatestSession)
              {
                  Setout.LoadSession();
                  Logger.Info("Last Session Loaded");
                  Logger.Info($"Session Start: {Setout.sessionStart}");
                  Logger.Info($"Pokemon Catch Count: {Setout.pokemonCatchCount}");
                  Logger.Info($"Pokestop Farmed Count: {Setout.pokeStopFarmedCount}");
              }
              else
              {
                  Logger.Info($"Session Started");
              }
              Logger.Info($"Session Start: {Setout.sessionStart}");
              Logger.Info($"Pokemon Catch Count: {Setout.pokemonCatchCount}");
              Logger.Info($"Pokestop Farmed Count: {Setout.pokeStopFarmedCount}");
              #endregion
          }
          #endregion
  
          #region Workflow
  
          #region Start and run the Bot
          public void Execute()
          {
              Logger.SelectedLevel = LogLevel.Error;
              Logger.Warning("Source code and binary files of this bot are absolutely free and open-source!");
              Logger.Warning("If you've paid for it. Request a chargeback immediately!");
              Logger.Warning("You only need pay for a key to access to Hash Service");
  
              if (GlobalVars.Debug.VerboseMode)
              {
                  Logger.SelectedLevel = LogLevel.Debug;
                  Logger.ColoredConsoleWrite(ConsoleColor.Red, $"LogLevel set to {Logger.SelectedLevel}. Many logs will be generated.");
              }
  
  
              // Quarthy PID
             // StringUtils.PidToFile(BotSettings);
  
              // Here we set which server to be used for the hashing service
              Logger.Info("Using " + Resources.Api.HashServerInfo.ID + " for Hash Key service.");
  
              #region Log Logger
  
              Logger.Info($"Starting Execute on login server: {BotSettings.AuthType}");
  
              if (BotSettings.LogPokemons)
              {
                  Logger.Info("Pokemon Log will be saved to " + GlobalVars.FileForPokemonsCaught);
              }
  
              if (BotSettings.LogTransfer)
              {
                  Logger.Info("Transfer Log will be saved to " + GlobalVars.FileForTransfers);
              }
  
              if (BotSettings.LogEvolve)
              {
                  Logger.Info("Evolution Log will be saved to " + GlobalVars.FileForEvolve);
              }
              #endregion
  
              objClient.CurrentAltitude = BotSettings.DefaultAltitude;
              objClient.CurrentLongitude = BotSettings.DefaultLongitude;
              objClient.CurrentLatitude = BotSettings.DefaultLatitude;
  
  
              #region Fix Altitude
  
              if (Math.Abs(objClient.CurrentAltitude) < 0.001)
              {
                  objClient.CurrentAltitude = LocationUtils.GetAltitude(objClient.CurrentLatitude, objClient.CurrentLongitude);
                  BotSettings.DefaultAltitude = objClient.CurrentAltitude;
  
                  Logger.Warning($"Altitude was 0, resolved that. New Altitude is now: {objClient.CurrentAltitude}");
              }
  
              #endregion
  
              #region Use Proxy
  
              if (BotSettings.proxySettings.enabled)
              {
                  Logger.Error("===============================================");
                  Logger.Error("Proxy enabled.");
                  Logger.Error($"ProxyIP: { BotSettings.proxySettings.hostName }:{BotSettings.proxySettings.port}");
                  Logger.Error("===============================================");
              }
  
              #endregion
  
              #region Login & Start
              //Restart unless killswitch thrown
              while (restartLogic)
              {
                  try
                  {
                      objClient.Login.DoLogin().Wait();
                      Logger.Debug("login done");
  
                      TelegramLogic.Instantiante();
                      DiscordLogic.Init();
  
                      /*********************************************************/
                      // BreakSettings Timer
                      BreakSettings AdvancedBreaks = new BreakSettings();
                      AdvancedBreaks.CheckEnabled(BotSettings);
                      /********************************************************/
                      PostLoginExecute();
  
                      Logger.Info("All Pokestops in range was already visited.");
                  }
                  catch (PtcOfflineException)
                  {
                      Logger.Error("PTC Servers are probably down.");
                  }
                  catch (AggregateException ae)
                  {
                      foreach (var e in ae.Flatten().InnerExceptions)
                      {
                          if (e is LoginFailedException)
                          {
                              Logger.Error(e.Message);
                              Logger.Error("Exiting in 10 Seconds.");
                              RandomHelper.RandomSleep(10000, 10001);
                              Environment.Exit(0);
                          }
                          else if (e is GoogleException)
                          {
                              Logger.Error("Login Failed. Your credentials are wrong or Google Account is banned.");
                              Logger.Error("Exiting in 10 Seconds.");
                              RandomHelper.RandomSleep(10000, 10001);
                              Environment.Exit(0);
                          }
                          else if (e is AccountNotVerifiedException)
                          {
                              Logger.Error("Your PTC Account is not activated.");
                              Logger.Error("Exiting in 10 Seconds.");
                              RandomHelper.RandomSleep(10000, 10001);
                              Environment.Exit(0);
                          }
                          else
                          {
                              throw;
                          }
                      }
                  }
                  catch (Exception ex)
                  {
                      #region Log Error 
                      Exception realerror = ex;
                      while (realerror.InnerException != null)
                          realerror = realerror.InnerException;
                      Logger.ExceptionInfo(ex.Message + "/" + realerror + "/" + ex.GetType());
                      #endregion
                  }
  
                  TelegramLogic.Stop();
                  DiscordLogic.Finish();
                  const int msToWait = 50000;
                  Logger.ColoredConsoleWrite(ConsoleColor.Red, $"Restarting in over {(msToWait + 5000) / 1000} Seconds.");
                  RandomHelper.RandomSleep(msToWait, msToWait + 10000);
              }
              #endregion
          }
          #endregion
  
          private void FarmPokestopOnBreak(FortData[] pokeStops, Client client)
          {
              //check for overlapping pokestops where we are taking a break
              Logger.ColoredConsoleWrite(ConsoleColor.Green, "Reached break location. Using Lures Enabled");
  
              var pokestopsWithinRangeStanding = pokeStops
                  .Where(i => LocationUtils
                     .CalculateDistanceInMeters(
                         objClient.CurrentLatitude,
                         objClient.CurrentLongitude,
                         i.Latitude,
                         i.Longitude) < 40);
  
              var pokestopCount = pokestopsWithinRangeStanding.Count();
  
              Logger.ColoredConsoleWrite(ConsoleColor.Green, $"{pokestopCount} Pokestops within range of where you are standing.");
  
              //Begin farming loop while on break
              while (BotSettings.pauseAtPokeStop)
              {
                  foreach (var pokestop in pokestopsWithinRangeStanding)
                  {
  
                      if (BotSettings.RelocateDefaultLocation) break;
  
                      CatchingLogic.Execute();
  
                      var fortInfo = objClient.Fort.GetFort(pokestop.Id, pokestop.Latitude, pokestop.Longitude).Result;
  
                      if (BotSettings.UseLureGUIClick || (BotSettings.UseLureAtBreak && !pokestop.ActiveFortModifier.Any() && !addedlure))
                      {
                          if (objClient.Inventory.GetItemAmountByType(ItemId.ItemTroyDisk) > 0)
                          {
                              BotSettings.UseLureGUIClick = false;
  
                              Logger.ColoredConsoleWrite(ConsoleColor.Magenta, "Adding lure and setting resume walking to 30 minutes");
  
                              objClient.Fort.AddFortModifier(fortInfo.FortId, ItemId.ItemTroyDisk);
  
                              Setout.resumetimestamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalMilliseconds + 30000;
                              addedlure = true;
                          }
                      }
  
                      var farmed = CheckAndFarmNearbyPokeStop(pokestop, objClient, fortInfo);
                      if (farmed)
                      {
                          pokestop.CooldownCompleteTimestampMs = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalMilliseconds + 300500;
                      }
  
                      Setout.SetCheckTimeToRun();
  
                      RandomHelper.RandomSleep(30000, 40000);
  
                      // wait for a bit before repeating farm cycle to avoid spamming 
                  }
  
                  if (!BotSettings.RelocateDefaultLocation) continue;
  
                  Setout.resumetimestamp = -10000;
                  BotSettings.pauseAtPokeStop = false;
  
                  Logger.ColoredConsoleWrite(ConsoleColor.Magenta, "Exit Command detected - Ending break");
              }
          }
  
          private int GetRandomWalkspeed()
          {
              BotSettings.WalkingSpeedInKilometerPerHour = GlobalVars.WalkingSpeedInKilometerPerHour;
              BotSettings.MinWalkSpeed = GlobalVars.MinWalkSpeed;
  
              var walkspeed = (int)BotSettings.WalkingSpeedInKilometerPerHour;
              if (!BotSettings.RandomReduceSpeed) return walkspeed;
  
              var randomWalkSpeed = new Random();
              if ((int)BotSettings.WalkingSpeedInKilometerPerHour - BotSettings.MinWalkSpeed > 1)
              {
                  walkspeed = randomWalkSpeed.Next((int)BotSettings.MinWalkSpeed,
                      (int)BotSettings.WalkingSpeedInKilometerPerHour + 1);
              }
              Logger.Debug($"WalkingSpeedInKilometerPerHour: {BotSettings.WalkingSpeedInKilometerPerHour}, MinWalkSpeed: {BotSettings.MinWalkSpeed}, walkspeed: {walkspeed}");
              return walkspeed;
          }
  
          #region Execute Functions
          
          public void PostLoginExecute()
          {
              try
              {
                  //update user location on map
                  Task.Factory.StartNew(() => Logic.Instance.infoObservable.PushNewGeoLocations(new GeoCoordinate(GlobalVars.latitude, GlobalVars.longitude)));
                  GetPlayerResponse profil = objClient.Player.GetPlayer();
                  objClient.Inventory.ExportPokemonToCSV(profil.PlayerData);
                  Setout.Execute();
                  while (GlobalVars.pauseAtPokeStop)
                  {
                      RandomHelper.RandomDelay(2000).Wait();
                  }
                  ExecuteFarmingPokestopsAndPokemons(objClient);
              }
              catch (AccessTokenExpiredException)
              {
                  throw new AccessTokenExpiredException();
              }
              catch (InvalidPlatformException)
              {
                  throw new InvalidPlatformException();
              }
              catch (Exception ex)
              {
                  Logger.Error($"Exception: {ex}");
  
                  if (BotSettings.RelocateDefaultLocation)
                  {
                      Logger.Info("Detected User Request to Relocate to a new farming spot!");
                  }
              }
          }
  
          #endregion
  
          #region Catch, Farm and Walk Logic
  
  
          #region Archimedean Spiral
  
          private void Espiral(Client client, FortData[] pokeStops, int MaxWalkingRadiusInMeters)
          {
              CatchingLogic.Execute();
  
              Logger.ColoredConsoleWrite(ConsoleColor.Blue, "Starting Archimedean spiral");
  
              var i2 = 0;
              var salir = true;
              var cantidadvar = 0.0001;
              double recorrido = MaxWalkingRadiusInMeters;
  
              pokeStops = pokeStops.Where(i => LocationUtils.CalculateDistanceInMeters(objClient.CurrentLatitude, objClient.CurrentLongitude, i.Latitude, i.Longitude) <= BotSettings.MaxWalkingRadiusInMeters).ToArray();
  
              var centerx = objClient.CurrentLatitude;
              var centery = objClient.CurrentLongitude;
  
              if (recorrido <= 100) cantidadvar = 0.00008;
              if (recorrido > 100 && recorrido <= 500) cantidadvar = 0.00009;
              if (recorrido > 500 && recorrido <= 1000) cantidadvar = 0.0001;
              if (recorrido > 1000) cantidadvar = 0.0002;
  
              while (salir)
              {
                  if (BotSettings.RelocateDefaultLocation) break;
  
                  var angle = 0.3 * i2;
                  var xx = centerx + cantidadvar * angle * Math.Cos(angle);
                  var yy = centery + cantidadvar * angle * Math.Sin(angle);
                  var distancia = Navigation.DistanceBetween2Coordinates(centerx, centery, xx, yy);
  
                  if (distancia > recorrido)
                  {
                      salir = false;
  
                      Logger.ColoredConsoleWrite(ConsoleColor.Green, "Returning to the starting point...");
  
                      navigation.HumanLikeWalking(new GeoCoordinate(BotSettings.DefaultLatitude, BotSettings.DefaultLongitude), BotSettings.WalkingSpeedInKilometerPerHour, CatchingLogic.Execute);
  
                      break;
                  }
  
                  if (i2 % 10 == 0)
                  {
                      Logger.ColoredConsoleWrite(ConsoleColor.Blue, "Distance from starting point: " + distancia + " metros...");
                  }
  
                  navigation.HumanLikeWalking(
                      new GeoCoordinate(xx, yy),
                      BotSettings.WalkingSpeedInKilometerPerHour,
                      CatchingLogic.Execute);
  
                  Logger.ColoredConsoleWrite(ConsoleColor.Blue, "Looking PokeStops who are less than 30 meters...");
  
                  FncPokeStop(client, pokeStops, true);
  
                  i2++;
              }
          }
  
          #endregion
  
          private void ExecuteFarmingPokestopsAndPokemons(Client client)
          {
  
              #region Check and report
              var verifiedLocation = VerifyLocation();
              var pokeStops = GetNearbyPokeStops();
              var tries = 3;
              do
              {
                  // make sure we found pokestops and log if none found
                  if (BotSettings.MaxWalkingRadiusInMeters != 0)
                  {
                      if (tries < 3)
                      {
                          RandomHelper.RandomSleep(5000, 6000);
                          pokeStops = GetNearbyPokeStops();
                      }
  
                      pokeStops = pokeStops.Where(i => LocationUtils.CalculateDistanceInMeters(BotSettings.DefaultLatitude, BotSettings.DefaultLongitude, i.Latitude, i.Longitude) <= BotSettings.MaxWalkingRadiusInMeters).ToArray();
  
                      if (!pokeStops.Any())
                      {
                          Logger.ColoredConsoleWrite(ConsoleColor.Red, "We can't find any PokeStops in a range of " + BotSettings.MaxWalkingRadiusInMeters + "m!");
                      }
                  }
  
                  if (!pokeStops.Any())
                  {
                      Logger.ColoredConsoleWrite(ConsoleColor.Red, "We can't find any PokeStops, which are unused! Probably the server are unstable, or you visted them all. Retrying..");
                      tries--;
                  }
                  else
                  {
                      Logger.ColoredConsoleWrite(ConsoleColor.Yellow, pokeStops.Count() + " usable PokeStops found near your current location.");
                      tries = 0;
                  }
              } while (tries > 0);
  
              #endregion
  
              #region Start Walk
  
              // Walk Spiral if enabled
              if (BotSettings.Espiral)
              {
                  Espiral(client, pokeStops, BotSettings.MaxWalkingRadiusInMeters);
  
                  return;
              }
  
              //Normal Walk and Catch between pokestops
              FncPokeStop(objClient, pokeStops, false);
  
              #endregion
          }
  
          private FortData[] GetNearbyPokeStops(bool updateMap = true, GetMapObjectsResponse mapObjectsResponse = null)
          {
  
              //Query nearby objects for mapData
              if (mapObjectsResponse == null)
                  mapObjectsResponse = objClient.Map.GetMapObjects().Result;
  
              //narrow map data to pokestops within walking distance
  
              var unixNow = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalMilliseconds;
  
              var pokeStops = mapObjectsResponse.MapCells.SelectMany(i => i.Forts)
                  .Where(i => i.Type == FortType.Checkpoint && i.CooldownCompleteTimestampMs < unixNow);
  
              IEnumerable<FortData> pokeGyms = new List<FortData>();
  
              if (GlobalVars.Gyms.Farm || GlobalVars.Gyms.Spin)
                  pokeGyms = mapObjectsResponse.MapCells.SelectMany(i => i.Forts)
                   .Where(i => i.Type == FortType.Gym && i.CooldownCompleteTimestampMs < unixNow);
  
              var both = pokeStops.Concat(pokeGyms)
                  .OrderBy(i => LocationUtils.CalculateDistanceInMeters(objClient.CurrentLatitude, objClient.CurrentLongitude, i.Latitude, i.Longitude));
                  
              FortData[] inRange = null;
  
              if (GlobalVars.SkipRadius)
                  inRange = both.ToArray();
              else
                  inRange = both
                      .Where(i => LocationUtils.CalculateDistanceInMeters(GlobalVars.latitude, GlobalVars.longitude, i.Latitude, i.Longitude) <= GlobalVars.radius)
                      .ToArray();
  
              var forts = Navigation.pathByNearestNeighbour(inRange);
  
              if (updateMap)
              {
                  if (pokeStops.Any())
                      Task.Factory.StartNew(() => infoObservable.PushAvailablePokeStopLocations(pokeStops.ToArray()));
                  if (pokeGyms.Any())
                      Task.Factory.StartNew(() => infoObservable.PushAvailablePokeGymsLocations(pokeGyms.ToArray()));
              }
  
              return forts;
          }
  
          private Queue<FortData> RefreshNearbyPokeStops(Queue<FortData> fortQueue)
          {
              FortData[] newPokeStops = GetNearbyPokeStops();
  
              if (newPokeStops.Any())
              {
                  var rnd = new Random();
                  var pokeStops = GlobalVars.WalkRandomly ? newPokeStops.OrderBy(x => rnd.Next()).ToArray() : newPokeStops;
  
                  var pokestopsQueue = new Queue<FortData>();
                  foreach (var pokeStop in pokeStops)
                      pokestopsQueue.Enqueue(pokeStop);
  
                  if (GlobalVars.WalkInLoop)
                      for (var i = pokeStops.Length - 2; i > 0; i--)
                          pokestopsQueue.Enqueue(pokeStops[i]);
  
                  return pokestopsQueue;
              }
              else return fortQueue;
          }
  
          private void FncPokeStop(Client client, FortData[] pokeStopsIn, bool metros30)
          {
              int PokeStopVisitCounter = 0;
  
              var distanceFromStart = LocationUtils
                  .CalculateDistanceInMeters(
                      BotSettings.DefaultLatitude,
                      BotSettings.DefaultLongitude,
                      objClient.CurrentLatitude,
                      objClient.CurrentLongitude);
  
              lureEncounters.Clear();
  
              var rnd = new Random();
              var pokeStops = GlobalVars.WalkRandomly ? pokeStopsIn.OrderBy(x => rnd.Next()).ToArray() : pokeStopsIn;
  
              var pokestopsQueue = new Queue<FortData>();
              foreach (var pokeStop in pokeStops)
                  pokestopsQueue.Enqueue(pokeStop);
  
  
              if (GlobalVars.WalkInLoop)
                  for (var i = pokeStops.Length - 2; i > 0; i--)
                      pokestopsQueue.Enqueue(pokeStops[i]);
  
              //walk between pokestops in default collection
              while (pokestopsQueue.Any())
              {
                  var pokeStop = pokestopsQueue.Dequeue();
                  if (GlobalVars.WalkInLoop)
                      pokestopsQueue.Enqueue(pokeStop);
  
                  //make sure user defined limits have not been reached
                  Setout.SetCheckTimeToRun();
  
                  #region Walk defined Route
  
                  if (GlobalVars.NextDestinationOverride.Count > 0)
                  {
                      try
                      {
                          do
                          {
                              WalkUserRoute(pokeStops);
  
                              #region Check for Exit Command
  
                              if (BotSettings.RelocateDefaultLocation)
                              {
                                  break;
                              }
  
                              #endregion
  
                              if (!BotSettings.RepeatUserRoute) continue;
  
                              foreach (var geocoord in GlobalVars.RouteToRepeat)
                              {
                                  GlobalVars.NextDestinationOverride.AddLast(geocoord);
                              }
                          } while (BotSettings.RepeatUserRoute);
                      }
                      catch (Exception e)
                      {
                          Logger.ColoredConsoleWrite(ConsoleColor.DarkRed, "Ignore this: sending exception information to log file.");
                          Logger.AddLog(string.Format("Error in Walk Defined Route: " + e));
                      }
                  }
  
                  #endregion
  
                  #region Check for Exit Command 
  
  
                  if (BotSettings.RelocateDefaultLocation)
                  {
                      break;
                  }
  
                  #endregion
  
                  //get destination pokestop information
                  var distance = LocationUtils
                      .CalculateDistanceInMeters(
                          objClient.CurrentLatitude,
                          objClient.CurrentLongitude,
                          pokeStop.Latitude,
                          pokeStop.Longitude);
  
                  FortDetailsResponse fortInfo = objClient.Fort.GetFort(pokeStop.Id, pokeStop.Latitude, pokeStop.Longitude).Result;
  
                  //log error if pokestop not found
                  if (fortInfo == null)
                  {
                      Task.Factory.StartNew(() => infoObservable.PushPokeStopInfoUpdate(pokeStop, "!!Can't Get PokeStop Information!!"));
                      continue;
                  }
  
                  Logger.ColoredConsoleWrite(ConsoleColor.Green, $"Next Pokestop is {fortInfo.Name} at {distance:0.##}m. ({fortInfo.Longitude}:{fortInfo.Latitude}) [{PokeStopVisitCounter}]");
  
                  #region Break At Lure Logic  
  
                  //check if user wants to break at lured pokestop
                  if (BotSettings.BreakAtLure && fortInfo.Modifiers.Any())
                  {
                      Setout.pausetimestamp = -10000;
                      Setout.resumetimestamp = fortInfo.Modifiers.First().ExpirationTimestampMs;
                      var timeRemaining = Setout.resumetimestamp - (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalMilliseconds;
  
                      Logger.ColoredConsoleWrite(ConsoleColor.Magenta, "Active Lure at next Pokestop - Pausing walk for " + Math.Round(timeRemaining / 60 / 1000, 2) + " Minutes");
  
                      BotSettings.pauseAtPokeStop = true;
                  }
  
                  #endregion
  
                  try
                  {
                      WalkWithRouting(pokeStop.Latitude, pokeStop.Longitude);
                  }
                  catch (Exception e)
                  {
                      Logger.ExceptionInfo(string.Format("Error in Walk Default Route: " + e));
                  }
  
                  // Pause and farm nearby pokestops
                  if (BotSettings.pauseAtPokeStop)
                  {
                      FarmPokestopOnBreak(pokeStops, client);
                  }
  
                  PokeStopVisitCounter++;
                  if ((PokeStopVisitCounter % 10) == 0)
                  {
                      pokestopsQueue = RefreshNearbyPokeStops(pokestopsQueue);
                  }
              }
          }
  
          #endregion
  
          #region Walk with Routing Functions
  
          private void WalkUserRoute(FortData[] pokeStops)
          {
              do
              {
                  #region Check for Exit Command
  
                  if (BotSettings.RelocateDefaultLocation)
                  {
                      Logger.ColoredConsoleWrite(ConsoleColor.Yellow, "Relocate Command Detected - Clearing User Defined Route");
  
                      GlobalVars.NextDestinationOverride.Clear();
                      GlobalVars.RouteToRepeat.Clear();
                      BotSettings.RepeatUserRoute = false;
  
                      break;
                  }
  
                  #endregion
  
                  try
                  {
                      if (BotSettings.pauseAtPokeStop)
                      {
                          FarmPokestopOnBreak(pokeStops, objClient);
                      }
  
                      var pokestopCoords = GlobalVars.NextDestinationOverride.First();
                      GlobalVars.NextDestinationOverride.RemoveFirst();
  
                      Logger.ColoredConsoleWrite(ConsoleColor.Yellow, $"Path Override detected! Rerouting to user-selected pokeStop...");
  
                      WalkWithRouting(pokestopCoords.Latitude, pokestopCoords.Longitude);
                  }
                  catch (Exception ex1)
                  {
                      Logger.ExceptionInfo(ex1.ToString());
                  }
              } while (GlobalVars.NextDestinationOverride.Count > 0);
          }
  
          public void WalkWithRouting(double latitude, double longitude)
          {
              if (BotSettings.UseGoogleMapsAPI)
              {
                  DoRouteWalking(latitude, longitude);
              }
              else
              {
                  var walkspeed = GetRandomWalkspeed();
  
                  navigation.HumanLikeWalking(new GeoCoordinate(latitude, longitude), walkspeed, ExecuteCatchandFarm);
              }
          }
  
          private void DoRouteWalking(double latitude, double longitude)
          {
              var walkspeed = GetRandomWalkspeed();
  
              Logger.ColoredConsoleWrite(ConsoleColor.Yellow, $"Getting Google Maps Routing");
  
              if (BotSettings.GoogleMapsAPIKey != null)
              {
                  #region Normalize Lat Long for Google Directions Request
  
                  var longstring = longitude.ToString(CultureInfo.InvariantCulture).Replace(",", ".");
                  var latstring = latitude.ToString(CultureInfo.InvariantCulture).Replace(",", ".");
                  var sourcelongstring = objClient.CurrentLongitude.ToString(CultureInfo.InvariantCulture).Replace(",", ".");
                  var sourcelatstring = objClient.CurrentLatitude.ToString(CultureInfo.InvariantCulture).Replace(",", ".");
  
                  #endregion
  
                  #region Google Directions API Request
  
                  var directionsRequest = new DirectionsRequest
                  {
                      ApiKey = BotSettings.GoogleMapsAPIKey,
                      TravelMode = TravelMode.Walking
                  };
  
                  #region Set Directions Request Variables based on client settings
  
                  if (walkspeed > 10 && walkspeed < 20)
                  {
                      directionsRequest.TravelMode = TravelMode.Bicycling;
  
                      Logger.ColoredConsoleWrite(ConsoleColor.Yellow, "Using Directions For Bicycling due to max speed setting > 10km/h");
                  }
  
                  if (walkspeed > 20)
                  {
                      directionsRequest.TravelMode = TravelMode.Bicycling;
                      Logger.ColoredConsoleWrite(ConsoleColor.Yellow, "Using Directions For Driving due to max speed setting > 20km/h");
                  }
  
                  if (BotSettings.SelectedLanguage == "de")
                      directionsRequest.Language = "de";
                  if (BotSettings.SelectedLanguage == "spain")
                      directionsRequest.Language = "es";
                  if (BotSettings.SelectedLanguage == "ptBR")
                      directionsRequest.Language = "pt-BR";
                  if (BotSettings.SelectedLanguage == "tr")
                      directionsRequest.Language = "tr";
                  if (BotSettings.SelectedLanguage == "ru")
                      directionsRequest.Language = "ru";
  
gt:                 if (BotSettings.SelectedLanguage == "france")