using DataModel.Database; using DataModel.Database.Common; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; namespace Processor; public static class AppSettings { #region Common DB Code private static readonly Dictionary CACHES = new(); private static readonly Dictionary CACHE_LOADED = new(); private static async Task GetSetting(string settingName, T defaultValue) { // When possible, use the cache: if (CACHE_LOADED.ContainsKey(settingName) && CACHE_LOADED[settingName]) return (T)CACHES[settingName]; var settingValue = defaultValue; try { // Get the database: await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); // Check, if the setting is already set: if (await db.Settings.FirstOrDefaultAsync(n => n.Code == settingName) is { } existingSetting) { settingValue = settingValue switch { bool => (T)(object)existingSetting.BoolValue, string => (T)(object)existingSetting.TextValue, int => (T)(object)existingSetting.IntegerValue, Guid => (T)(object)existingSetting.GuidValue, SettingDeepLMode or SettingDeepLAction or SettingGeneratorMode => (T)(object)existingSetting.IntegerValue, _ => defaultValue, }; return settingValue; } // Does not exist, so create it: var setting = new Setting { Code = settingName, }; switch (settingValue) { case bool: setting.BoolValue = (bool)(object)settingValue; break; case string: setting.TextValue = (string)(object)settingValue; break; case int: setting.IntegerValue = (int)(object)settingValue; break; case Guid: setting.GuidValue = (Guid)(object)settingValue; break; case SettingDeepLMode: case SettingDeepLAction: case SettingGeneratorMode: setting.IntegerValue = (int)(object)settingValue; break; } await db.Settings.AddAsync(setting); await db.SaveChangesAsync(); return settingValue; } finally { CACHE_LOADED[settingName] = true; CACHES[settingName] = settingValue!; } } public static async Task SetSetting(string settingName, T settingValue) { // Update the cache: CACHES[settingName] = settingValue!; CACHE_LOADED[settingName] = true; // Get the database: await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); // Check, if the setting is already set: if (await db.Settings.FirstOrDefaultAsync(n => n.Code == settingName) is { } existingSetting) { switch (settingValue) { case bool: existingSetting.BoolValue = (bool)(object)settingValue; break; case string: existingSetting.TextValue = (string)(object)settingValue; break; case int: existingSetting.IntegerValue = (int)(object)settingValue; break; case Guid: existingSetting.GuidValue = (Guid)(object)settingValue; break; case SettingDeepLMode: case SettingDeepLAction: case SettingGeneratorMode: existingSetting.IntegerValue = (int)(object)settingValue; break; } await db.SaveChangesAsync(); } // Does not exist, so create it: else { var setting = new Setting { Code = SettingNames.GENERATOR_DOTNET_ENABLED, }; switch (settingValue) { case bool: setting.BoolValue = (bool)(object)settingValue; break; case string: setting.TextValue = (string)(object)settingValue; break; case int: setting.IntegerValue = (int)(object)settingValue; break; case Guid: setting.GuidValue = (Guid)(object)settingValue; break; case SettingDeepLMode: case SettingDeepLAction: case SettingGeneratorMode: setting.IntegerValue = (int)(object)settingValue; break; } await db.Settings.AddAsync(setting); await db.SaveChangesAsync(); } } #endregion #region DeepL Settings #region DeepL Mode public static async Task SetDeepLMode(SettingDeepLMode mode) { DeepL.ResetState(); await AppSettings.SetSetting(SettingNames.DEEPL_MODE, mode); } public static async Task GetDeepLMode() => await AppSettings.GetSetting(SettingNames.DEEPL_MODE, SettingDeepLMode.DISABLED); #endregion #region DeepL API Key public static async Task SetDeepLAPIKey(string apiKey) { DeepL.ResetState(); await AppSettings.SetSetting(SettingNames.DEEPL_API_KEY, apiKey); } public static async Task GetDeepLAPIKey() => await AppSettings.GetSetting(SettingNames.DEEPL_API_KEY, string.Empty); #endregion #region DeepL Action public static async Task SetDeepLAction(SettingDeepLAction action) => await AppSettings.SetSetting(SettingNames.DEEPL_ACTION, action); public static async Task GetDeepLAction() => await AppSettings.GetSetting(SettingNames.DEEPL_ACTION, SettingDeepLAction.MANUAL); #endregion #region DeepL Source Culture public static async Task SetDeepLSourceCultureIndex(int cultureIndex) => await AppSettings.SetSetting(SettingNames.DEEPL_SOURCE_CULTURE, cultureIndex); public static async Task GetDeepLSourceCultureIndex() => await AppSettings.GetSetting(SettingNames.DEEPL_SOURCE_CULTURE, -1); #endregion #endregion #region Culture Settings #region List of culture indices private static List CACHE_CULTURES_INDICES = new(); public static async Task> GetCultureIndices() { // When possible, use the cache: if (CACHE_CULTURES_INDICES.Count > 0) return CACHE_CULTURES_INDICES; // Get the database: await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); // Count the number of cultures: var list = await db.Settings.Where(n => n.Code.StartsWith(SettingNames.CULTURE)).OrderBy(n => n.IntegerValue).Select(n => n.IntegerValue).ToListAsync(); // We have at least one default culture: if(list.Count == 0) { // Add the default culture, which is en-US: await AppSettings.SetCultureCode(1, "en-US"); } else CACHE_CULTURES_INDICES = list; return CACHE_CULTURES_INDICES; } #endregion #region Get a culture // Cache the cultures: private static readonly Dictionary CACHE_CULTURES = new(); public static async Task GetCultureCode(int index) { // Check the cache: if (CACHE_CULTURES.TryGetValue(index, out var cultureCode)) return cultureCode; // Get the database: await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); // Get the culture code: var code = await db.Settings.FirstOrDefaultAsync(n => n.Code.StartsWith(SettingNames.CULTURE) && n.IntegerValue == index); // When the culture code is not set & index = 1, use the default culture en-US: if(code is null && index == 1) { CACHE_CULTURES.Add(index, "en-US"); return "en-US"; } // Update the cache & return the code: var codeText = code?.TextValue ?? string.Empty; CACHE_CULTURES[index] = codeText; return codeText; } #endregion #region Set a culture public static async Task SetCultureCode(int index, string code) { // Keep a copy of the previous name: var previousCode = await AppSettings.GetCultureCode(index); // Update the cache: CACHE_CULTURES[index] = code; // Get the database: await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); // Check, if the setting is already set: if (await db.Settings.FirstOrDefaultAsync(n => n.Code.StartsWith(SettingNames.CULTURE) && n.IntegerValue == index) is {} existingSetting) { existingSetting.TextValue = code; await db.SaveChangesAsync(); } // Does not exist, so create it: else { var setting = new Setting { Code = $"{SettingNames.CULTURE} {index}", IntegerValue = index, TextValue = code, }; await db.Settings.AddAsync(setting); await db.SaveChangesAsync(); // Update the list of cultures indices: CACHE_CULTURES_INDICES.Add(index); } // Next, we need to rename the culture inside all translations as well: var translations = await db.Translations.Where(n => n.Culture == previousCode).ToListAsync(); foreach (var translation in translations) translation.Culture = code; await db.SaveChangesAsync(); } #endregion #region Delete a culture public static async Task DeleteCulture(int index) { // Get the database: await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); // Check, if the setting is already set: if (await db.Settings.FirstOrDefaultAsync(n => n.Code.StartsWith(SettingNames.CULTURE) && n.IntegerValue == index) is {} existingSetting) { db.Settings.Remove(existingSetting); await db.SaveChangesAsync(); } // Update the list of cultures indices: CACHE_CULTURES_INDICES.Remove(index); // Update the cache: CACHE_CULTURES.Remove(index); } #endregion #region Get a list of cultures public readonly record struct CultureInfo(string Code, int Index); public static async Task> GetCultureInfos() { // Get the number of cultures: var cultureIndices = await AppSettings.GetCultureIndices(); // Get the culture codes: var cultureInfos = new List(); foreach (var cultureIndex in cultureIndices) cultureInfos.Add(new CultureInfo { Code = await AppSettings.GetCultureCode(cultureIndex), Index = cultureIndex, }); return cultureInfos; } #endregion #endregion #region Generator Settings #region Generator Mode public static async Task GetGeneratorMode() => await AppSettings.GetSetting(SettingNames.GENERATOR_MODE, SettingGeneratorMode.MANUAL); public static async Task SetGeneratorMode(SettingGeneratorMode mode) => await AppSettings.SetSetting(SettingNames.GENERATOR_MODE, mode); #endregion #region .NET Generator Enabled/Disabled public static async Task GetGeneratorDotnetEnabled() => await AppSettings.GetSetting(SettingNames.GENERATOR_DOTNET_ENABLED, false); public static async Task SetGeneratorDotnetEnabled(bool enabled) => await AppSettings.SetSetting(SettingNames.GENERATOR_DOTNET_ENABLED, enabled); #endregion #region .NET Generator Destination Path public static async Task GetGeneratorDotnetDestinationPath() => await AppSettings.GetSetting(SettingNames.GENERATOR_DOTNET_DESTINATION_PATH, string.Empty); public static async Task SetGeneratorDotnetDestinationPath(string path) => await AppSettings.SetSetting(SettingNames.GENERATOR_DOTNET_DESTINATION_PATH, path); #endregion #region .NET Generator Namespace public static async Task GetGeneratorDotnetNamespace() => await AppSettings.GetSetting(SettingNames.GENERATOR_DOTNET_NAMESPACE, "I18N"); public static async Task SetGeneratorDotnetNamespace(string updatedNamespace) => await AppSettings.SetSetting(SettingNames.GENERATOR_DOTNET_NAMESPACE, updatedNamespace); #endregion #region .NET Generator Default Culture public static async Task GetGeneratorDotnetDefaultCultureIndex() => await AppSettings.GetSetting(SettingNames.GENERATOR_DOTNET_DEFAULT_CULTURE, 0); public static async Task SetGeneratorDotnetDefaultCultureIndex(int updatedCulture) => await AppSettings.SetSetting(SettingNames.GENERATOR_DOTNET_DEFAULT_CULTURE, updatedCulture); #endregion #region Godot Generator Enabled/Disabled public static async Task GetGeneratorGodotEnabled() => await AppSettings.GetSetting(SettingNames.GENERATOR_GODOT_ENABLED, false); public static async Task SetGeneratorGodotEnabled(bool enabled) => await AppSettings.SetSetting(SettingNames.GENERATOR_GODOT_ENABLED, enabled); #endregion #region Godot Generator Destination Path public static async Task GetGeneratorGodotDestinationPath() => await AppSettings.GetSetting(SettingNames.GENERATOR_GODOT_DESTINATION_PATH, string.Empty); public static async Task SetGeneratorGodotDestinationPath(string path) => await AppSettings.SetSetting(SettingNames.GENERATOR_GODOT_DESTINATION_PATH, path); #endregion #endregion #region Auto-Export Settings #region Auto-Export Enabled/Disabled public static async Task GetAutoExportEnabled() => await AppSettings.GetSetting(SettingNames.AUTO_EXPORT_ENABLED, false); public static async Task SetAutoExportEnabled(bool enabled) => await AppSettings.SetSetting(SettingNames.AUTO_EXPORT_ENABLED, enabled); #endregion #region Auto-Export Destination Path public static async Task GetAutoExportDestinationPath() => await AppSettings.GetSetting(SettingNames.AUTO_EXPORT_DESTINATION_PATH, string.Empty); public static async Task SetAutoExportDestinationPath(string path) => await AppSettings.SetSetting(SettingNames.AUTO_EXPORT_DESTINATION_PATH, path); #endregion #region Auto-Export Filename public static async Task GetAutoExportFilename() => await AppSettings.GetSetting(SettingNames.AUTO_EXPORT_FILENAME, "I18NCommander.json"); public static async Task SetAutoExportFilename(string filename) => await AppSettings.SetSetting(SettingNames.AUTO_EXPORT_FILENAME, filename); #endregion #region Auto-Export Sensitive Data public static async Task GetAutoExportSensitiveData() => await AppSettings.GetSetting(SettingNames.AUTO_EXPORT_SENSITIVE_DATA, false); public static async Task SetAutoExportSensitiveData(bool enabled) => await AppSettings.SetSetting(SettingNames.AUTO_EXPORT_SENSITIVE_DATA, enabled); #endregion #endregion }