// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. #nullable disable using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Bindables; using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Database; using osu.Game.IO.Archives; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; using osu.Game.Rulesets.Scoring; using osu.Game.Online.API; using osu.Game.Scoring.Legacy; namespace osu.Game.Scoring { public class ScoreManager : ModelManager, IModelImporter { private readonly OsuConfigManager configManager; private readonly ScoreImporter scoreImporter; private readonly LegacyScoreExporter scoreExporter; public override bool PauseImports { get => base.PauseImports; set { base.PauseImports = value; scoreImporter.PauseImports = value; } } public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, RealmAccess realm, IAPIProvider api, OsuConfigManager configManager = null) : base(storage, realm) { this.configManager = configManager; scoreImporter = new ScoreImporter(rulesets, beatmaps, storage, realm, api) { PostNotification = obj => PostNotification?.Invoke(obj) }; scoreExporter = new LegacyScoreExporter(storage) { PostNotification = obj => PostNotification?.Invoke(obj) }; } public Score GetScore(ScoreInfo score) => scoreImporter.GetScore(score); /// /// Perform a lookup query on available s. /// /// The query. /// The first result for the provided query, or null if no results were found. public ScoreInfo Query(Expression> query) { return Realm.Run(r => r.All().FirstOrDefault(query)?.Detach()); } /// /// Retrieves a bindable that represents the total score of a . /// /// /// Responds to changes in the currently-selected . /// /// The to retrieve the bindable for. /// The bindable containing the total score. public Bindable GetBindableTotalScore([NotNull] ScoreInfo score) => new TotalScoreBindable(score, configManager); /// /// Retrieves a bindable that represents the formatted total score string of a . /// /// /// Responds to changes in the currently-selected . /// /// The to retrieve the bindable for. /// The bindable containing the formatted total score string. public Bindable GetBindableTotalScoreString([NotNull] ScoreInfo score) => new TotalScoreStringBindable(GetBindableTotalScore(score)); /// /// Provides the total score of a . Responds to changes in the currently-selected . /// private class TotalScoreBindable : Bindable { private readonly Bindable scoringMode = new Bindable(); /// /// Creates a new . /// /// The to provide the total score of. /// The config. public TotalScoreBindable(ScoreInfo score, OsuConfigManager configManager) { configManager?.BindWith(OsuSetting.ScoreDisplayMode, scoringMode); scoringMode.BindValueChanged(mode => Value = score.GetDisplayScore(mode.NewValue), true); } } /// /// Provides the total score of a as a formatted string. Responds to changes in the currently-selected . /// private class TotalScoreStringBindable : Bindable { // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable (need to hold a reference) private readonly IBindable totalScore; public TotalScoreStringBindable(IBindable totalScore) { this.totalScore = totalScore; this.totalScore.BindValueChanged(v => Value = v.NewValue.ToString("N0"), true); } } public void Delete([CanBeNull] Expression> filter = null, bool silent = false) { Realm.Run(r => { var items = r.All() .Where(s => !s.DeletePending); if (filter != null) items = items.Where(filter); Delete(items.ToList(), silent); }); } public void Delete(BeatmapInfo beatmap, bool silent = false) { Realm.Run(r => { var beatmapScores = r.Find(beatmap.ID)!.Scores.ToList(); Delete(beatmapScores, silent); }); } public Task Import(params string[] paths) => scoreImporter.Import(paths); public Task Import(ImportTask[] imports, ImportParameters parameters = default) => scoreImporter.Import(imports, parameters); public override bool IsAvailableLocally(ScoreInfo model) => Realm.Run(realm => realm.All().Any(s => s.OnlineID == model.OnlineID)); public IEnumerable HandledExtensions => scoreImporter.HandledExtensions; public Task>> Import(ProgressNotification notification, ImportTask[] tasks, ImportParameters parameters = default) => scoreImporter.Import(notification, tasks); public Task Export(ScoreInfo score) => scoreExporter.ExportAsync(score.ToLive(Realm)); public Task> ImportAsUpdate(ProgressNotification notification, ImportTask task, ScoreInfo original) => scoreImporter.ImportAsUpdate(notification, task, original); public Live Import(ScoreInfo item, ArchiveReader archive = null, ImportParameters parameters = default, CancellationToken cancellationToken = default) => scoreImporter.ImportModel(item, archive, parameters, cancellationToken); /// /// Populates the for a given . /// /// The score to populate the statistics of. public void PopulateMaximumStatistics(ScoreInfo score) => scoreImporter.PopulateMaximumStatistics(score); #region Implementation of IPresentImports public Action>> PresentImport { set => scoreImporter.PresentImport = value; } #endregion } }