text: using System;
  using System.Collections.Generic;
  using System.Linq;
  using Microsoft.Xna.Framework;
  using Microsoft.Xna.Framework.Input;
  using Quaver.API.Enums;
  using Quaver.API.Helpers;
  using Quaver.Server.Common.Enums;
  using Quaver.Server.Common.Objects;
  using Quaver.Shared.Audio;
  using Quaver.Shared.Config;
  using Quaver.Shared.Database.Maps;
  using Quaver.Shared.Database.Playlists;
  using Quaver.Shared.Database.Scores;
  using Quaver.Shared.Database.Settings;
  using Quaver.Shared.Discord;
  using Quaver.Shared.Graphics.Notifications;
  using Quaver.Shared.Graphics.Transitions;
  using Quaver.Shared.Modifiers;
  using Quaver.Shared.Modifiers.Mods;
  using Quaver.Shared.Online;
  using Quaver.Shared.Scheduling;
  using Quaver.Shared.Screens.Download;
  using Quaver.Shared.Screens.Edit;
  using Quaver.Shared.Screens.Editor;
  using Quaver.Shared.Screens.Gameplay;
  using Quaver.Shared.Screens.Importing;
  using Quaver.Shared.Screens.Loading;
  using Quaver.Shared.Screens.Main;
  using Quaver.Shared.Screens.Menu;
  using Quaver.Shared.Screens.Multi;
  using Quaver.Shared.Screens.Multiplayer;
  using Quaver.Shared.Screens.Select.UI.Leaderboard;
  using Quaver.Shared.Screens.Selection.UI;
  using Quaver.Shared.Screens.Selection.UI.Dialogs;
  using Quaver.Shared.Screens.Selection.UI.FilterPanel.Search;
  using Quaver.Shared.Screens.Selection.UI.Maps;
  using Quaver.Shared.Screens.Selection.UI.Mapsets;
  using Quaver.Shared.Screens.Tournament;
  using Wobble;
  using Wobble.Audio.Tracks;
  using Wobble.Bindables;
  using Wobble.Graphics;
  using Wobble.Graphics.UI.Dialogs;
  using Wobble.Input;
  using Wobble.Logging;
  
  namespace Quaver.Shared.Screens.Selection
  {
      public sealed class SelectionScreen : QuaverScreen, IHasLeftPanel
      {
          /// <inheritdoc />
          /// <summary>
          /// </summary>
          public override QuaverScreenType Type { get; } = QuaverScreenType.Select;
  
          /// <summary>
          ///     If the user is in multiplayer, this is the current screen
          /// </summary>
          public bool IsMultiplayer { get; }
  
          /// <summary>
          ///     Stores the currently available mapsets to play in the screen
          /// </summary>
          public Bindable<List<Mapset>> AvailableMapsets { get; private set; }
  
          /// <summary>
          ///    The user's search term/query
          /// </summary>
          public Bindable<string> CurrentSearchQuery { get; private set; }
  
          /// <summary>
          ///     The currently active panel on the left side of the screen
          /// </summary>
          public Bindable<SelectContainerPanel> ActiveLeftPanel { get; set; }
  
          /// <summary>
          ///     The currently active scroll container on the right-side of the screen
          /// </summary>
          public Bindable<SelectScrollContainerType> ActiveScrollContainer { get; private set; }
  
          /// <summary>
          /// </summary>
          private Random Rng { get; } = new Random();
  
          /// <summary>
          ///     Invoked when a random mapset has been selected
          /// </summary>
          public static event EventHandler<RandomMapsetSelectedEventArgs> RandomMapsetSelected;
  
          /// <summary>
          ///     If the user is currently exporting a mapset
          /// </summary>
          private bool IsExportingMapset { get; set; }
  
          /// <summary>
          ///     Keeps track of if the user is play testing in the map preview
          /// </summary>
          public Bindable<bool> IsPlayTestingInPreview { get; private set; }
  
          /// <summary>
          /// </summary>
          public SelectionScreen()
          {
              IsMultiplayer = OnlineManager.CurrentGame != null;
  
              // Go to the import screen if we've imported a map not on the select screen
              if (MapsetImporter.Queue.Count > 0 || QuaverSettingsDatabaseCache.OutdatedMaps.Count != 0
                                                 || MapDatabaseCache.MapsToUpdate.Count != 0)
              {
                  Exit(() => new ImportingScreen(null, true));
                  return;
              }
  
              if (IsMultiplayer)
                  OnlineManager.Client?.SetGameCurrentlySelectingMap(true);
              else
                  SetRichPresence();
  
              InitializeSearchQueryBindable();
              InitializeAvailableMapsetsBindable();
              InitializeActiveLeftPanelBindable();
              InitializeActiveScrollContainerBindable();
              InitializeTestPlayingBindable();
              InitializeSelectedPlaylist();
  
              // Do initial filtering of mapsets for the screen
              AvailableMapsets.Value = MapsetHelper.FilterMapsets(CurrentSearchQuery);
  
              MapManager.MapsetDeleted += OnMapsetDeleted;
              MapManager.MapDeleted += OnMapDeleted;
              MapManager.MapUpdated += OnMapUpdated;
              MapManager.SongRequestPlayed += OnSongRequestPlayed;
              ConfigManager.AutoLoadOsuBeatmaps.ValueChanged += OnAutoLoadOsuBeatmapsChanged;
  
              View = new SelectionScreenView(this);
          }
  
          /// <inheritdoc />
          /// <summary>
          /// </summary>
          public override void OnFirstUpdate()
          {
              GameBase.Game.GlobalUserInterface.Cursor.Alpha = 1;
              FadeAudioTrackIn();
              base.OnFirstUpdate();
          }
  
          /// <inheritdoc />
          /// <summary>
          /// <summary>
          /// </summary>
          /// <param name="gameTime"></param>
          public override void Update(GameTime gameTime)
          {
              if (!Exiting)
                  GameBase.Game.GlobalUserInterface.Cursor.Alpha = 1;
  
              HandleInput(gameTime);
              base.Update(gameTime);
          }
  
          /// <inheritdoc />
          /// <summary>
          /// </summary>
          public override void Destroy()
          {
              CurrentSearchQuery?.Dispose();
              AvailableMapsets?.Dispose();
              ActiveLeftPanel?.Dispose();
              ActiveScrollContainer?.Dispose();
              IsPlayTestingInPreview?.Dispose();
              RandomMapsetSelected = null;
              MapManager.MapsetDeleted -= OnMapsetDeleted;
              MapManager.MapDeleted -= OnMapDeleted;
              MapManager.MapUpdated -= OnMapUpdated;
              MapManager.SongRequestPlayed -= OnSongRequestPlayed;
  
              // ReSharper disable once DelegateSubtraction
              ConfigManager.AutoLoadOsuBeatmaps.ValueChanged -= OnAutoLoadOsuBeatmapsChanged;
  
              base.Destroy();
          }
  
          /// <summary>
          ///     Initializes the bindable which stores the user's search query <see cref="CurrentSearchQuery"/>
          /// </summary>
          private void InitializeSearchQueryBindable()
              => CurrentSearchQuery = new Bindable<string>(null) { Value = FilterPanelSearchBox.PreviousSearchTerm };
  
          /// <summary>
          ///     Initializes the bindable which stores the available mapsets for the screen <see cref="AvailableMapsets"/>
          /// </summary>
          private void InitializeAvailableMapsetsBindable()
              => AvailableMapsets = new Bindable<List<Mapset>>(null) { Value = new List<Mapset>() };
  
          /// <summary>
          ///     Initializes the bindable which keeps track of which panel on the left side of the screen is active
          /// </summary>
          private void InitializeActiveLeftPanelBindable()
          {
              ActiveLeftPanel = new Bindable<SelectContainerPanel>(SelectContainerPanel.Leaderboard)
              {
                  Value = SelectContainerPanel.Leaderboard
              };
          }
  
          /// <summary>
          ///     Initializes the bindable which keeps track of which scroll container is active
          /// </summary>
          private void InitializeActiveScrollContainerBindable()
          {
              ActiveScrollContainer = new Bindable<SelectScrollContainerType>(SelectScrollContainerType.Mapsets)
              {
                  Value = SelectScrollContainerType.Mapsets
              };
  
              if (ConfigManager.SelectGroupMapsetsBy.Value == GroupMapsetsBy.Playlists)
                  ActiveScrollContainer.Value = SelectScrollContainerType.Playlists;
  
              // If the user is playing maps from a playlist, then automatically use the mapset container
              if (PlaylistManager.Selected.Value != null && ConfigManager.SelectGroupMapsetsBy.Value == GroupMapsetsBy.Playlists)
                  ActiveScrollContainer.Value = SelectScrollContainerType.Mapsets;
          }
  
          /// <summary>
          ///     Initializes the bindable that determines if we're currently play testing
          /// </summary>
          /// <exception cref="NotImplementedException"></exception>
          private void InitializeTestPlayingBindable()
              => IsPlayTestingInPreview = new Bindable<bool>(false) { Value = false };
  
          /// <summary>
          ///     If the initial playlist is null this will set it apporpriately
          /// </summary>
          private void InitializeSelectedPlaylist()
          {
              if (PlaylistManager.Selected.Value == null && PlaylistManager.Playlists.Count != 0)
                  PlaylistManager.Selected.Value = PlaylistManager.Playlists.First();
          }
  
          /// <summary>
          ///     Handles all input for the screen
          /// </summary>
          /// <param name="gameTime"></param>
          private void HandleInput(GameTime gameTime)
          {
              if (DialogManager.Dialogs.Count != 0)
                  return;
  
              HandleKeyPressEscape();
              HandleKeyPressF1();
              HandleKeyPressF2();
              HandleKeyPressF3();
              HandleKeyPressF4();
              HandleKeyPressF5();
              HandleKeyPressEnter();
              HandleKeyPressControlInput();
              HandleThumb1MouseButtonClick();
  
              if (ActiveLeftPanel.Value == SelectContainerPanel.Leaderboard)
                  HandleKeyPressTab();
          }
  
          /// <summary>
          ///     Handles when the user presses escape
          /// </summary>
          private void HandleKeyPressEscape()
          {
              if (!KeyboardManager.IsUniqueKeyPress(Keys.Escape))
                  return;
  
              switch (ActiveLeftPanel.Value)
              {
                  case SelectContainerPanel.Leaderboard:
                  case SelectContainerPanel.MapPreview:
                      if (ActiveScrollContainer.Value == SelectScrollContainerType.Maps)
                      {
                          ActiveScrollContainer.Value = SelectScrollContainerType.Mapsets;
                          return;
                      }
  
                      if (ActiveScrollContainer.Value == SelectScrollContainerType.Mapsets &&
                          ConfigManager.SelectGroupMapsetsBy.Value == GroupMapsetsBy.Playlists)
                      {
                          ActiveScrollContainer.Value = SelectScrollContainerType.Playlists;
                          return;
                      }
  
                      if (ActiveLeftPanel.Value == SelectContainerPanel.Leaderboard)
                          ExitToMenu();
  
                      if (ActiveLeftPanel.Value == SelectContainerPanel.MapPreview)
                          ActiveLeftPanel.Value = SelectContainerPanel.Leaderboard;
                      break;
                  default:
                      ActiveLeftPanel.Value = SelectContainerPanel.Leaderboard;
                      break;
              }
          }
  
          /// <summary>
          ///     Handles when the user presses F1
          /// </summary>
          private void HandleKeyPressF1()
          {
              if (!KeyboardManager.IsUniqueKeyPress(Keys.F1))
                  return;
  
              if (ActiveLeftPanel.Value == SelectContainerPanel.Modifiers)
                  ActiveLeftPanel.Value = SelectContainerPanel.Leaderboard;
              else
                  ActiveLeftPanel.Value = SelectContainerPanel.Modifiers;
          }
  
          /// <summary>
          ///     Handles random map selection through key press
          /// </summary>
          private void HandleKeyPressF2()
          {
              if (!KeyboardManager.IsUniqueKeyPress(Keys.F2))
                  return;
  
              SelectRandomMap();
          }
  
          /// <summary>
          ///    Handles exporting mapsets through F3 key press
          /// </summary>
          private void HandleKeyPressF3()
          {
              if (KeyboardManager.CurrentState.IsKeyDown(Keys.LeftControl) || KeyboardManager.CurrentState.IsKeyDown(Keys.RightControl))
                  return;
  
              if (!KeyboardManager.IsUniqueKeyPress(Keys.F3))
                  return;
  
              if (ActiveLeftPanel.Value == SelectContainerPanel.MapPreview)
                  ActiveLeftPanel.Value = SelectContainerPanel.Leaderboard;
              else
                  ActiveLeftPanel.Value = SelectContainerPanel.MapPreview;
          }
  
          /// <summary>
          /// </summary>
          private void HandleKeyPressF4()
          {
              if (KeyboardManager.CurrentState.IsKeyDown(Keys.LeftControl) || KeyboardManager.CurrentState.IsKeyDown(Keys.RightControl))
                  return;
  
              if (!KeyboardManager.IsUniqueKeyPress(Keys.F4))
                  return;
  
              if (ActiveLeftPanel.Value == SelectContainerPanel.UserProfile)
                  ActiveLeftPanel.Value = SelectContainerPanel.Leaderboard;
              else
                  ActiveLeftPanel.Value = SelectContainerPanel.UserProfile;
          }
  
          /// <summary>
          ///		Prompts the user to begin a force refresh for mapsets.
          ///	</summary>
          private void HandleKeyPressF5()
          {
              if (KeyboardManager.CurrentState.IsKeyDown(Keys.LeftControl) || KeyboardManager.CurrentState.IsKeyDown(Keys.RightControl))
                  return;
  
              if (!KeyboardManager.IsUniqueKeyPress(Keys.F5))
                  return;
  
              DialogManager.Show(new RefreshDialog());
          }
  
          /// <summary>
          ///     Handles when the user presses the enter key
          /// </summary>
          private void HandleKeyPressEnter()
          {
              if (!KeyboardManager.IsUniqueKeyPress(Keys.Enter))
                  return;
  
              switch (ActiveScrollContainer.Value)
              {
                  case SelectScrollContainerType.Mapsets:
                      if (MapsetHelper.IsSingleDifficultySorted())
                          ExitToGameplay();
                      else
                          ActiveScrollContainer.Value = SelectScrollContainerType.Maps;
                      break;
                  case SelectScrollContainerType.Maps:
                      ExitToGameplay();
                      break;
                  case SelectScrollContainerType.Playlists:
                      ActiveScrollContainer.Value = SelectScrollContainerType.Mapsets;
                      break;
                  default:
                      throw new ArgumentOutOfRangeException();
              }
          }
  
          /// <summary>
          ///     Handles when the user presses the tab key
          /// </summary>
          public static void HandleKeyPressTab()
          {
              if (!KeyboardManager.IsUniqueKeyPress(Keys.Tab))
                  return;
  
              var index = (int)ConfigManager.LeaderboardSection.Value;
              var length = Enum.GetNames(typeof(LeaderboardType)).Length;
  
              int newIndex;
              if (KeyboardManager.IsShiftDown())
              {
                  if (index - 1 >= 0)
                      newIndex = index - 1;
                  else
                      newIndex = length - 1;
              }
              else
              {
                  if (index + 1 < length)
                      newIndex = index + 1;
                  else
                      newIndex = 0;
              }
  
              ConfigManager.LeaderboardSection.Value = (LeaderboardType)newIndex;
          }
  
          /// <summary>
          ///     Handles when the user holds control down and performs input actions
          /// </summary>
          private void HandleKeyPressControlInput()
          {
              if (!KeyboardManager.CurrentState.IsKeyDown(Keys.LeftControl) &&
                  !KeyboardManager.CurrentState.IsKeyDown(Keys.RightControl))
                  return;
  
              var shiftHeld = KeyboardManager.IsShiftDown();
  
              // Increase rate.
              if (KeyboardManager.IsUniqueKeyPress(ConfigManager.KeyIncreaseGameplayAudioRate.Value))
                  ModManager.AddSpeedMods(GetNextRate(true, shiftHeld));
  
              // Decrease Rate
              if (KeyboardManager.IsUniqueKeyPress(ConfigManager.KeyDecreaseGameplayAudioRate.Value))
                  ModManager.AddSpeedMods(GetNextRate(false, shiftHeld));
  
              // Change from pitched to non-pitched
              if (KeyboardManager.IsUniqueKeyPress(Keys.D0))
                  ConfigManager.Pitched.Value = !ConfigManager.Pitched.Value;
  
              ChangeScrollSpeed();
          }
  
          /// <summary>
          ///     Handles when the user clicks the thumb1 mouse button.
          ///     If the user has the maps container open, this'll bring back the mapset container
          /// </summary>
          private void HandleThumb1MouseButtonClick()
          {
              if (!MouseManager.IsUniqueClick(MouseButton.Thumb1))
                  return;
  
              var view = (SelectionScreenView)View;
  
              switch (ActiveScrollContainer.Value)
              {
                  case SelectScrollContainerType.Mapsets:
                      if (ConfigManager.SelectGroupMapsetsBy.Value != GroupMapsetsBy.Playlists)
                          return;
  
                      if (!view.MapsetContainer.IsHovered())
                          return;
  
                      ActiveScrollContainer.Value = SelectScrollContainerType.Playlists;
                      break;
                  case SelectScrollContainerType.Maps:
                      if (!view.MapContainer.IsHovered())
                          return;
  
                      ActiveScrollContainer.Value = SelectScrollContainerType.Mapsets;
                      break;
                  case SelectScrollContainerType.Playlists:
                      return;
              }
          }
  
          /// <summary>
          ///     Gets the adjacent rate value.
          ///
          ///     For example, if the current rate is 1.0x, the adjacent value would be either 0.95x or 1.1x,
          ///     depending on the argument.
          /// </summary>
          /// <param name="faster">If true, returns the higher rate, otherwise the lower rate.</param>
          /// <param name="forceHalfRate"></param>
          /// <returns></returns>
          public static float GetNextRate(bool faster, bool forceHalfRate = false)
          {
              var current = ModHelper.GetRateFromMods(ModManager.Mods);
              var adjustment = 0.1f;
  
              // ReSharper disable once CompareOfFloatsByEqualityOperator
              if (current < 1.0f || forceHalfRate || current == 1.0f && !faster)
                  adjustment = 0.05f;
  
              var next = current + adjustment * (faster ? 1f : -1f);
              next = Math.Clamp(next, ModSpeed.MinSpeed, ModSpeed.MaxSpeed);
              return (float)Math.Round(next, 2);
          }
  
          /// <summary>
          ///     Fades the track back to the config setting
          /// </summary>
          private void FadeAudioTrackIn()
          {
              if (ConfigManager.VolumeMusic == null)
                  return;
  
              if (AudioEngine.Track != null && AudioEngine.Track.IsPlaying)
                  AudioEngine.Track?.Fade(100, 300);
          }
  
          /// <summary>
          ///     Changes the user's scroll speed for the selected game mode
          ///     CTRL+F3/CTRL+F4
          /// </summary>
          /// <exception cref="ArgumentOutOfRangeException"></exception>
          private void ChangeScrollSpeed()
          {
              BindableInt scrollSpeed;
  
              switch (MapManager.Selected.Value.Mode)
              {
                  case GameMode.Keys4:
                      scrollSpeed = ConfigManager.ScrollSpeed4K;
                      break;
                  case GameMode.Keys7:
                      scrollSpeed = ConfigManager.ScrollSpeed7K;
                      break;
                  default:
                      throw new ArgumentOutOfRangeException();
              }
  
              var changed = false;
  
              var speedIncrease = KeyboardManager.IsShiftDown() ? 1 : 10;
  
              // Change scroll speed down
              if (KeyboardManager.IsUniqueKeyPress(Keys.F3))
              {
                  scrollSpeed.Value -= speedIncrease;
                  changed = true;
              }
              else if (KeyboardManager.IsUniqueKeyPress(Keys.F4))
              {
                  scrollSpeed.Value += speedIncrease;
                  changed = true;
              }
  
              if (changed)
              {
                  NotificationManager.Show(NotificationLevel.Info, $"Your {ModeHelper.ToShortHand(MapManager.Selected.Value.Mode)} " +
                                                                   $"scroll speed has been changed to: {scrollSpeed.Value / 10f:0.0}");
              }
          }
  
          /// <summary>
          ///     Selects a random map
          /// </summary>
          public void SelectRandomMap()
          {
              if (AvailableMapsets.Value.Count == 0)
                  return;
  
              ActiveScrollContainer.Value = SelectScrollContainerType.Mapsets;
  
              var index = Rng.Next(AvailableMapsets.Value.Count);
              var mapIndex = Rng.Next(AvailableMapsets.Value[index].Maps.Count);
  
              MapManager.Selected.Value = AvailableMapsets.Value[index].Maps[mapIndex];
              RandomMapsetSelected?.Invoke(this, new RandomMapsetSelectedEventArgs(AvailableMapsets.Value[index], index));
          }
  
          /// <summary>
          ///     Exits the screen to gameplay
          /// </summary>
          public void ExitToGameplay()
          {
              if (MapManager.Selected.Value == null)
                  return;
  
              if (OnlineManager.CurrentGame != null)
              {
                  SelectMultiplayerMap();
                  return;
              }
  
              if (OnlineManager.IsSpectatingSomeone)
                  OnlineManager.Client?.StopSpectating();
  
              if (ModManager.IsActivated(ModIdentifier.Coop))
              {
                  Exit(() => new TournamentScreen(2));
                  return;
              }
  
              Exit(() => new MapLoadingScreen(new List<Score>()));
          }
  
          /// <summary>
          ///     Exits the current screen back to menu
          /// </summary>
          public void ExitToMenu()
          {
              Exit(() =>
              {
                  if (IsMultiplayer)
                  {
                      OnlineManager.Client?.SetGameCurrentlySelectingMap(false);
                      return new MultiplayerGameScreen();
                  }
  
                  return new MainMenuScreen();
              });
          }
  
          /// <summary>
          ///     Exits the current screen and goes to the editor
          /// </summary>
          public void ExitToEditor()
          {
              if (MapManager.Selected.Value == null)
                  return;
  
              if (AudioEngine.Track != null && !AudioEngine.Track.IsStopped)
                  AudioEngine.Track.Stop();
  
              if (OnlineManager.CurrentGame != null)
              {
                  NotificationManager.Show(NotificationLevel.Error, "You cannot use the editor while playing multiplayer.");
                  return;
              }
  
              if (OnlineManager.IsSpectatingSomeone)
                  OnlineManager.Client?.StopSpectating();
  
              try
              {
                  IAudioTrack track;
  
                  try
                  {
                      track = new AudioTrack(MapManager.GetAudioPath(MapManager.Selected.Value), false, false);
                  }
                  catch (Exception)
                  {
                      track = new AudioTrackVirtual(MapManager.Selected.Value.SongLength + 5000);
                  }
  
                  ModManager.RemoveAllMods();
                  Exit(() => new EditScreen(MapManager.Selected.Value, track));
              }
              catch (Exception e)
              {
                  Logger.Error(e, LogType.Runtime);
                  NotificationManager.Show(NotificationLevel.Error, "Failed to load the editor with this map!");
              }
          }
  
          /// <summary>
          ///     Handles the selection of multiplayer maps
          /// </summary>
          private void SelectMultiplayerMap()
          {
              var map = MapManager.Selected.Value;
  
              if (!CheckMultiplayerDifficultyRange())
                  return;
  
              if (!CheckMultiplayerSongLength())
                  return;
  
              if (!CheckMultiplayerGameMode())
                  return;
  
              if (!CheckMultiplayerLongNotePercentage())
                  return;
  
              // Start the fade out early to make it look like the screen is loading
              Transitioner.FadeIn();
  
              ThreadScheduler.Run(() =>
              {
                  OnlineManager.Client.ChangeMultiplayerGameMap(map.Md5Checksum, map.MapId,
                      map.MapSetId, map.ToString(), (byte)map.Mode, map.DifficultyFromMods(ModManager.Mods),
                      map.GetDifficultyRatings(), map.GetJudgementCount(), MapManager.Selected.Value.GetAlternativeMd5());
  
                  OnlineManager.Client.SetGameCurrentlySelectingMap(false);
  
                  Exit(() => new MultiplayerGameScreen());
              });
          }
  
          /// <summary>
          /// </summary>
          /// <returns></returns>
          private bool CheckMultiplayerDifficultyRange()
          {
              var diff = MapManager.Selected.Value.DifficultyFromMods(ModManager.Mods);
  
              // Prevent host from picking a map not within difficulty range
              if (diff < OnlineManager.CurrentGame.MinimumDifficultyRating || diff > OnlineManager.CurrentGame.MaximumDifficultyRating)
              {
                  NotificationManager.Show(NotificationLevel.Error,
                      $"Difficulty rating must be between {OnlineManager.CurrentGame.MinimumDifficultyRating} " +
                      $"and {OnlineManager.CurrentGame.MaximumDifficultyRating} for this multiplayer match!");
  
                  return false;
              }
  
              return true;
          }
  
          /// <summary>
          ///     Checks if the host is selecting a map that fits the length criteria
          /// </summary>
          /// <returns></returns>
          private bool CheckMultiplayerSongLength()
          {
              var length = MapManager.Selected.Value.SongLength * ModHelper.GetRateFromMods(ModManager.Mods) / 1000;
  
              // Pevent host from picking a map not in max song length range
              if (length > OnlineManager.CurrentGame.MaximumSongLength)
              {
                  NotificationManager.Show(NotificationLevel.Error,
                      $"The maximum length allowed for this multiplayer match is: {OnlineManager.CurrentGame.MaximumSongLength} seconds");
  
                  return false;
              }
  
              return true;
          }
  
          /// <summary>
          ///     Checks if the host is selecting a map that fits the game mode criteria
          /// </summary>
          /// <returns></returns>
          private bool CheckMultiplayerGameMode()
          {
              // Prevent disallowed game modes from being selected
              if (!OnlineManager.CurrentGame.AllowedGameModes.Contains((byte)MapManager.Selected.Value.Mode))
              {
                  NotificationManager.Show(NotificationLevel.Error, "You cannot pick maps of this game mode in this multiplayer match!");
                  return false;
              }
  
              return true;
          }
  
          /// <summary>
          ///     Checks if the host is selecting a map within the LN% range
          /// </summary>
          /// <returns></returns>
          private bool CheckMultiplayerLongNotePercentage()
          {
              var map = MapManager.Selected.Value;
  
              // Prevent maps not in range of the minimum and maximum LN%
              if (map.LNPercentage < OnlineManager.CurrentGame.MinimumLongNotePercentage || map.LNPercentage > OnlineManager.CurrentGame.MaximumLongNotePercentage)
              {
                  NotificationManager.Show(NotificationLevel.Error,
                      $"You cannot select this map. The long note percentage must be between " +
                      $"{OnlineManager.CurrentGame.MinimumLongNotePercentage}%-{OnlineManager.CurrentGame.MaximumLongNotePercentage}% " +
                                                                    $"for this multiplayer match.");
  
                  return false;
              }
  
              return true;
          }
  
          /// <summary>
          ///     Exports the current mapset to a zip file and opens it in the file manager
          /// </summary>
          public void ExportSelectedMapset()
          {
              if (MapManager.Selected.Value == null)
                  return;
  
              if (IsExportingMapset)
              {
                  NotificationManager.Show(NotificationLevel.Warning, "Slow down! You must wait for your previous mapset to export");
                  return;
              }
  
              IsExportingMapset = true;
  
              ThreadScheduler.Run(() =>
              {
                  NotificationManager.Show(NotificationLevel.Info, "Exporting mapset to zip archive. Please wait!");
  
                  MapManager.Selected.Value.Mapset.ExportToZip();
                  IsExportingMapset = false;
  
                  NotificationManager.Show(NotificationLevel.Success,
                      $"Successfully exported {MapManager.Selected.Value.Mapset.Artist} - {MapManager.Selected.Value.Mapset.Title}!");
              });
          }
  
          /// <summary>
          /// </summary>
          private void SetRichPresence()
          {
              DiscordHelper.Presence.Details = "Selecting a song";
              DiscordHelper.Presence.State = "In the menus";
              DiscordHelper.Presence.PartySize = 0;
              DiscordHelper.Presence.PartyMax = 0;
              DiscordHelper.Presence.StartTimestamp = 0;
              DiscordHelper.Presence.EndTimestamp = 0;
              DiscordHelper.Presence.LargeImageText = OnlineManager.GetRichPresenceLargeKeyText(ConfigManager.SelectedGameMode.Value);
              DiscordHelper.Presence.SmallImageKey = ModeHelper.ToShortHand(ConfigManager.SelectedGameMode.Value).ToLower();
              DiscordHelper.Presence.SmallImageText = ModeHelper.ToLongHand(ConfigManager.SelectedGameMode.Value);
              DiscordRpc.UpdatePresence(ref DiscordHelper.Presence);
          }
  
          /// <summary>
          ///     When a mapset has been deleted, refilter the mapsets
          /// </summary>
          /// <param name="sender"></param>
          /// <param name="e"></param>
          private void OnMapsetDeleted(object sender, MapsetDeletedEventArgs e)
          {
              ThreadScheduler.Run(() =>
              {
                  var index = 0;
  
                  if (e.Index == -1)
                      index = 0;
  
                  if (e.Index - 1 >= 0)
                      index = e.Index - 1;
                  ;
                  lock (AvailableMapsets.Value)
                      AvailableMapsets.Value = MapsetHelper.FilterMapsets(CurrentSearchQuery);
  
                  // Change the map
                  if (index != -1)
                  {
                      MapManager.Selected.Value = AvailableMapsets.Value[index].Maps.First();
                      return;
                  }
  
                  // Stop the current track if there are no more mapsets left
                  lock (AudioEngine.Track)
                  {
                      if (AudioEngine.Track.IsDisposed && !AudioEngine.Track.IsStopped)
                          AudioEngine.Track.Dispose();
                  }
              });
          }
  
          /// <summary>
          ///     Called when a map has been deleted
          /// </summary>
          /// <param name="sender"></param>
          /// <param name="e"></param>
          private void OnMapDeleted(object sender, MapDeletedEventArgs e)
          {
              ThreadScheduler.Run(() =>
              {
                  lock (AvailableMapsets.Value)
                      AvailableMapsets.Value = MapsetHelper.FilterMapsets(CurrentSearchQuery);
  
                  var mapsetIndex = AvailableMapsets.Value.FindIndex(x => x.Maps.Contains(e.Map));
  
                  if (mapsetIndex == -1 && AvailableMapsets.Value.Count != 0)
                  {
                      MapManager.Selected.Value = AvailableMapsets.Value.First().Maps.First();
                      return;
                  }
  
                  // Stop the current track if there are no more mapsets left
                  lock (AudioEngine.Track)
                  {
                      if (AudioEngine.Track.IsDisposed && !AudioEngine.Track.IsStopped)
                          AudioEngine.Track.Dispose();
                  }
              });
          }
  
          /// <summary>
          /// </summary>
          /// <param name="sender"></param>
          /// <param name="e"></param>
          private void OnMapUpdated(object sender, MapUpdatedEventArgs e) => AvailableMapsets.Value = MapsetHelper.FilterMapsets(CurrentSearchQuery);
  
          /// <summary>
          /// </summary>
          /// <param name="sender"></param>
          /// <param name="e"></param>
          private void OnSongRequestPlayed(object sender, SongRequestPlayedEventArgs e)
          {
              ThreadScheduler.Run(() =>
              {
                  MapManager.Selected.Value = e.Map;
  
                  lock (AvailableMapsets.Value)
                      AvailableMapsets.Value = MapsetHelper.FilterMapsets(CurrentSearchQuery);
              });
          }
  
          /// <summary>
          /// </summary>
          /// <param name="sender"></param>
          /// <param name="e"></param>
          /// <exception cref="NotImplementedException"></exception>
          private void OnAutoLoadOsuBeatmapsChanged(object sender, BindableValueChangedEventArgs<bool> e)
              => Exit(() => new ImportingScreen(null, true));
  
          /// <inheritdoc />
          /// <summary>
          /// </summary>
          /// <returns></returns>
          public override UserClientStatus GetClientStatus() => new UserClientStatus(ClientStatus.Selecting, -1, "", 0, "", 0);
      }
  }
  