using System.Collections.Immutable; namespace TaikoWebUI.Services; public class GameDataService : IGameDataService { private readonly HttpClient client; private ImmutableDictionary<uint, DanData> danMap = ImmutableDictionary<uint, DanData>.Empty; private Dictionary<uint, MusicDetail>? musicDetailDictionary = new(); private List<Costume>? costumeList; private Dictionary<uint,Title>? titleDictionary = new(); private bool musicDetailInitialized; private bool costumesInitialized; private bool titlesInitialized; private Dictionary<string, List<uint>>? lockedCostumeDataDictionary = new(); private Dictionary<string, List<uint>>? lockedTitleDataDictionary = new(); public GameDataService(HttpClient client) { this.client = client; } public async Task InitializeAsync(string dataBaseUrl) { dataBaseUrl = dataBaseUrl.TrimEnd('/'); var danData = await client.GetFromJsonAsync<List<DanData>>($"{dataBaseUrl}/data/dan_data.json"); danData.ThrowIfNull(); danMap = danData.ToImmutableDictionary(data => data.DanId); } public async Task<Dictionary<uint, MusicDetail>> GetMusicDetailDictionary() { if (!musicDetailInitialized) { await InitializeMusicDetailAsync(); } return musicDetailDictionary ?? new Dictionary<uint, MusicDetail>(); } public async Task<List<Costume>> GetCostumeList() { if (!costumesInitialized) { await InitializeCostumesAsync(); } return costumeList ?? new List<Costume>(); } public async Task<Dictionary<uint, Title>> GetTitleDictionary() { if (!titlesInitialized) { await InitializeTitlesAsync(); } return titleDictionary ?? new Dictionary<uint, Title>(); } public async Task<Dictionary<string, List<uint>>> GetLockedCostumeDataDictionary() { if (!costumesInitialized) { await InitializeCostumesAsync(); } return lockedCostumeDataDictionary ?? new Dictionary<string, List<uint>>(); } public async Task<Dictionary<string, List<uint>>> GetLockedTitleDataDictionary() { if (!titlesInitialized) { await InitializeTitlesAsync(); } return lockedTitleDataDictionary ?? new Dictionary<string, List<uint>>(); } public string GetMusicNameBySongId(Dictionary<uint, MusicDetail> musicDetails, uint songId, string? language = "ja") { return musicDetails.TryGetValue(songId, out var musicDetail) ? language switch { "ja" => musicDetail.SongName, "en-US" => musicDetail.SongNameEN, "zh-Hans" => musicDetail.SongNameCN, "zh-Hant" => musicDetail.SongNameCN, "ko" => musicDetail.SongNameKO, _ => musicDetail.SongName } : string.Empty; } public string GetMusicArtistBySongId(Dictionary<uint, MusicDetail> musicDetails, uint songId, string? language = "ja") { return musicDetails.TryGetValue(songId, out var musicDetail) ? language switch { "jp" => musicDetail.ArtistName, "en-US" => musicDetail.ArtistNameEN, "zh-Hans" => musicDetail.ArtistNameCN, "zh-Hant" => musicDetail.ArtistNameCN, "ko" => musicDetail.ArtistNameKO, _ => musicDetail.ArtistName } : string.Empty; } public SongGenre GetMusicGenreBySongId(Dictionary<uint, MusicDetail> musicDetails, uint songId) { return musicDetails.TryGetValue(songId, out var musicDetail) ? musicDetail.Genre : SongGenre.Variety; } public int GetMusicIndexBySongId(Dictionary<uint, MusicDetail> musicDetails, uint songId) { return musicDetails.TryGetValue(songId, out var musicDetail) ? musicDetail.Index : int.MaxValue; } public ImmutableDictionary<uint, DanData> GetDanMap() { return danMap; } public int GetMusicStarLevel(Dictionary<uint, MusicDetail> musicDetails, uint songId, Difficulty difficulty) { var success = musicDetails.TryGetValue(songId, out var musicDetail); return difficulty switch { Difficulty.None => throw new ArgumentException("Difficulty cannot be none"), Difficulty.Easy => success ? musicDetail!.StarEasy : 0, Difficulty.Normal => success ? musicDetail!.StarNormal : 0, Difficulty.Hard => success ? musicDetail!.StarHard : 0, Difficulty.Oni => success ? musicDetail!.StarOni : 0, Difficulty.UraOni => success ? musicDetail!.StarUra : 0, _ => throw new ArgumentOutOfRangeException(nameof(difficulty), difficulty, null) }; } public string GetHeadTitle(IEnumerable<Costume> costumes, uint index) { return costumes.FirstOrDefault(costume => costume.CostumeType == "head" && costume.CostumeId == index)?.CostumeName ?? string.Empty; } public string GetKigurumiTitle(IEnumerable<Costume> costumes, uint index) { return costumes.FirstOrDefault(costume => costume.CostumeType == "kigurumi" && costume.CostumeId == index)?.CostumeName ?? string.Empty; } public string GetBodyTitle(IEnumerable<Costume> costumes, uint index) { return costumes.FirstOrDefault(costume => costume.CostumeType == "body" && costume.CostumeId == index)?.CostumeName ?? string.Empty; } public string GetFaceTitle(IEnumerable<Costume> costumes, uint index) { return costumes.FirstOrDefault(costume => costume.CostumeType == "face" && costume.CostumeId == index)?.CostumeName ?? string.Empty; } public string GetPuchiTitle(IEnumerable<Costume> costumes, uint index) { return costumes.FirstOrDefault(costume => costume.CostumeType == "puchi" && costume.CostumeId == index)?.CostumeName ?? string.Empty; } private async Task InitializeMusicDetailAsync() { musicDetailDictionary = await client.GetFromJsonAsync<Dictionary<uint, MusicDetail>>("api/GameData/MusicDetails"); musicDetailInitialized = true; } private async Task InitializeCostumesAsync() { costumeList = await client.GetFromJsonAsync<List<Costume>>("api/GameData/Costumes"); lockedCostumeDataDictionary = await client.GetFromJsonAsync<Dictionary<string, List<uint>>>("api/GameData/LockedCostumes"); costumesInitialized = true; } private async Task InitializeTitlesAsync() { titleDictionary = await client.GetFromJsonAsync<Dictionary<uint, Title>>("api/GameData/Titles"); lockedTitleDataDictionary = await client.GetFromJsonAsync<Dictionary<string, List<uint>>>("api/GameData/LockedTitles"); titlesInitialized = true; } }