context: #region using directives
  
  using System;
  using System.Threading;
  using System.Threading.Tasks;
  using PoGo.NecroBot.Logic.Common;
  using PoGo.NecroBot.Logic.Event;
  using PoGo.NecroBot.Logic.Logging;
  using PoGo.NecroBot.Logic.State;
  using PoGo.NecroBot.Logic.Utils;
  using POGOProtos.Inventory.Item;
  
  #endregion
  
  namespace PoGo.NecroBot.Logic.Tasks
  {
      public class RecycleItemsTask
      {
          private static int _diff;
          private static Random rnd = new Random();
  
          public static async Task Execute(ISession session, CancellationToken cancellationToken)
          {
              cancellationToken.ThrowIfCancellationRequested();
  
              await session.Inventory.RefreshCachedInventory();
  
              var currentTotalItems = await session.Inventory.GetTotalItemCount();
              if ((session.Profile.PlayerData.MaxItemStorage * session.LogicSettings.RecycleInventoryAtUsagePercentage / 100.0f) > currentTotalItems)
                  return;
  
              var currentAmountOfPokeballs = await session.Inventory.GetItemAmountByType(ItemId.ItemPokeBall);
              var currentAmountOfGreatballs = await session.Inventory.GetItemAmountByType(ItemId.ItemGreatBall);
              var currentAmountOfUltraballs = await session.Inventory.GetItemAmountByType(ItemId.ItemUltraBall);
              var currentAmountOfMasterballs = await session.Inventory.GetItemAmountByType(ItemId.ItemMasterBall);
  
              if (session.LogicSettings.DetailedCountsBeforeRecycling)
                  Logger.Write(session.Translation.GetTranslation(TranslationString.CurrentPokeballInv,
                      currentAmountOfPokeballs, currentAmountOfGreatballs, currentAmountOfUltraballs,
                      currentAmountOfMasterballs));
  
              var currentPotions = await session.Inventory.GetItemAmountByType(ItemId.ItemPotion);
              var currentSuperPotions = await session.Inventory.GetItemAmountByType(ItemId.ItemSuperPotion);
              var currentHyperPotions = await session.Inventory.GetItemAmountByType(ItemId.ItemHyperPotion);
              var currentMaxPotions = await session.Inventory.GetItemAmountByType(ItemId.ItemMaxPotion);
  
              var currentAmountOfPotions = currentPotions + currentSuperPotions + currentHyperPotions + currentMaxPotions;
  
              if (session.LogicSettings.DetailedCountsBeforeRecycling)
                  Logger.Write(session.Translation.GetTranslation(TranslationString.CurrentPotionInv,
                      currentPotions, currentSuperPotions, currentHyperPotions, currentMaxPotions));
              
              var currentRevives = await session.Inventory.GetItemAmountByType(ItemId.ItemRevive);
              var currentMaxRevives = await session.Inventory.GetItemAmountByType(ItemId.ItemMaxRevive);
  
              var currentAmountOfRevives = currentRevives + currentMaxRevives;
  
              if (session.LogicSettings.DetailedCountsBeforeRecycling)
                  Logger.Write(session.Translation.GetTranslation(TranslationString.CurrentReviveInv,
                      currentRevives, currentMaxRevives));
  
              var currentAmountOfBerries = await session.Inventory.GetItemAmountByType(ItemId.ItemRazzBerry) +
                  await session.Inventory.GetItemAmountByType(ItemId.ItemBlukBerry) +
                  await session.Inventory.GetItemAmountByType(ItemId.ItemNanabBerry) +
                  await session.Inventory.GetItemAmountByType(ItemId.ItemWeparBerry) +
                  await session.Inventory.GetItemAmountByType(ItemId.ItemPinapBerry);
              var currentAmountOfIncense = await session.Inventory.GetItemAmountByType(ItemId.ItemIncenseOrdinary) +
                  await session.Inventory.GetItemAmountByType(ItemId.ItemIncenseSpicy) +
                  await session.Inventory.GetItemAmountByType(ItemId.ItemIncenseCool) +
                  await session.Inventory.GetItemAmountByType(ItemId.ItemIncenseFloral);
              var currentAmountOfLuckyEggs = await session.Inventory.GetItemAmountByType(ItemId.ItemLuckyEgg);
              var currentAmountOfLures = await session.Inventory.GetItemAmountByType(ItemId.ItemTroyDisk);
  
              if (session.LogicSettings.DetailedCountsBeforeRecycling)
                  Logger.Write(session.Translation.GetTranslation(TranslationString.CurrentMiscItemInv,
                      currentAmountOfBerries, currentAmountOfIncense, currentAmountOfLuckyEggs, currentAmountOfLures));
  
              if (session.LogicSettings.TotalAmountOfPokeballsToKeep != 0)
                  await OptimizedRecycleBalls(session, cancellationToken);
  
              if (!session.LogicSettings.VerboseRecycling)
                  Logger.Write(session.Translation.GetTranslation(TranslationString.RecyclingQuietly), LogLevel.Recycling);
  
              if (session.LogicSettings.TotalAmountOfPotionsToKeep>=0)
                  await OptimizedRecyclePotions(session, cancellationToken);
  
              if (session.LogicSettings.TotalAmountOfRevivesToKeep>=0)
                  await OptimizedRecycleRevives(session, cancellationToken);
  
              if (session.LogicSettings.TotalAmountOfBerriesToKeep >= 0)
                  await OptimizedRecycleBerries(session, cancellationToken);
              
              await session.Inventory.RefreshCachedInventory();
              currentTotalItems = await session.Inventory.GetTotalItemCount();
              if ((session.Profile.PlayerData.MaxItemStorage * session.LogicSettings.RecycleInventoryAtUsagePercentage / 100.0f) > currentTotalItems)
                  return;
  
              var items = await session.Inventory.GetItemsToRecycle(session);
  
              foreach (var item in items)
              {
                  cancellationToken.ThrowIfCancellationRequested();
  
                  await session.Client.Inventory.RecycleItem(item.ItemId, item.Count);
  
                  if (session.LogicSettings.VerboseRecycling)
                      session.EventDispatcher.Send(new ItemRecycledEvent { Id = item.ItemId, Count = item.Count });
                  if (session.LogicSettings.DelayBetweenRecycleActions)
                      DelayingUtils.Delay(session.LogicSettings.DelayBetweenPlayerActions, 500);
              }
              await session.Inventory.RefreshCachedInventory();
          }
  
          private static async Task RecycleItems(ISession session, CancellationToken cancellationToken, int itemCount, ItemId item)
          {
              int itemsToRecycle = 0;
              int itemsToKeep = itemCount - _diff;
              if (itemsToKeep < 0)
                  itemsToKeep = 0;
              itemsToRecycle = itemCount - itemsToKeep;
              if (itemsToRecycle != 0)
              {
                  _diff -= itemsToRecycle;
                  cancellationToken.ThrowIfCancellationRequested();
                  await session.Client.Inventory.RecycleItem(item, itemsToRecycle);
                  if (session.LogicSettings.VerboseRecycling)
                      session.EventDispatcher.Send(new ItemRecycledEvent { Id = item, Count = itemsToRecycle });
                  if (session.LogicSettings.DelayBetweenRecycleActions)
                      DelayingUtils.Delay(session.LogicSettings.DelayBetweenPlayerActions, 500);
              }
          }
  
          private static async Task OptimizedRecycleBalls(ISession session, CancellationToken cancellationToken)
          {
              var pokeBallsCount = await session.Inventory.GetItemAmountByType(ItemId.ItemPokeBall);
              var greatBallsCount = await session.Inventory.GetItemAmountByType(ItemId.ItemGreatBall);
              var ultraBallsCount = await session.Inventory.GetItemAmountByType(ItemId.ItemUltraBall);
              var masterBallsCount = await session.Inventory.GetItemAmountByType(ItemId.ItemMasterBall);
  
              int totalBallsCount = pokeBallsCount + greatBallsCount + ultraBallsCount + masterBallsCount;
              int random = rnd.Next(-1 * session.LogicSettings.RandomRecycleValue, session.LogicSettings.RandomRecycleValue + 1);
  
              if (totalBallsCount > session.LogicSettings.TotalAmountOfPokeballsToKeep)
              {
                  if (session.LogicSettings.RandomizeRecycle)
                  {
                      _diff = totalBallsCount - session.LogicSettings.TotalAmountOfPokeballsToKeep + random;
                  } else {
                      _diff = totalBallsCount - session.LogicSettings.TotalAmountOfPokeballsToKeep;
                  }
                  
                  if (_diff > 0)
                  {
                      await RecycleItems(session, cancellationToken, pokeBallsCount, ItemId.ItemPokeBall);
                  }
                  if (_diff > 0)
                  {
                      await RecycleItems(session, cancellationToken, greatBallsCount, ItemId.ItemGreatBall); 
                  }
                  if (_diff > 0)
                  {
                      await RecycleItems(session, cancellationToken, ultraBallsCount, ItemId.ItemUltraBall);
                  }
                  if (_diff > 0)
                  {
                      await RecycleItems(session, cancellationToken, masterBallsCount, ItemId.ItemMasterBall);
                  }
              }
          }
  
          private static async Task OptimizedRecyclePotions(ISession session, CancellationToken cancellationToken)
          {
              var potionCount = await session.Inventory.GetItemAmountByType(ItemId.ItemPotion);
              var superPotionCount = await session.Inventory.GetItemAmountByType(ItemId.ItemSuperPotion);
              var hyperPotionsCount = await session.Inventory.GetItemAmountByType(ItemId.ItemHyperPotion);
              var maxPotionCount = await session.Inventory.GetItemAmountByType(ItemId.ItemMaxPotion);
              
              int totalPotionsCount = potionCount + superPotionCount + hyperPotionsCount + maxPotionCount;
              int random = rnd.Next(-1 * session.LogicSettings.RandomRecycleValue, session.LogicSettings.RandomRecycleValue + 1);
              if (totalPotionsCount > session.LogicSettings.TotalAmountOfPotionsToKeep)
              {
                  if (session.LogicSettings.RandomizeRecycle)
                  {
                      _diff = totalPotionsCount - session.LogicSettings.TotalAmountOfPotionsToKeep + random;
                  }
                  else
                  {
                      _diff = totalPotionsCount - session.LogicSettings.TotalAmountOfPotionsToKeep;
                  }
                  
                  if (_diff > 0)
                  {
                      await RecycleItems(session, cancellationToken, potionCount, ItemId.ItemPotion);
                  }
  
                  if (_diff > 0)
                  {
                      await RecycleItems(session, cancellationToken, superPotionCount, ItemId.ItemSuperPotion);
                  }
  
  
gt:                 if (_diff > 0)