diff --git a/HorseRacingPredictor/HorseRacingPredictor/HorseRacingPredictor/UserSettings.cs b/HorseRacingPredictor/HorseRacingPredictor/HorseRacingPredictor/UserSettings.cs new file mode 100644 index 0000000..910c10a --- /dev/null +++ b/HorseRacingPredictor/HorseRacingPredictor/HorseRacingPredictor/UserSettings.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace HorseRacingPredictor +{ + /// + /// User-editable preferences persisted as JSON. + /// Replaces the legacy settings.ini key=value format. + /// + internal sealed class UserSettings + { + private static readonly string FilePath = + Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "usersettings.json"); + + private static readonly JsonSerializerOptions JsonOptions = new() + { + WriteIndented = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + + // ?? Football ???????????????????????????????????????????? + public string ApiKey { get; set; } = string.Empty; + public string FbExportPath { get; set; } = string.Empty; + public string FbPrefix { get; set; } = string.Empty; + public string FbSuffix { get; set; } = string.Empty; + public bool FbIncludeDate { get; set; } = true; + public string FbDateFormat { get; set; } = "yyyy-MM-dd"; + public string FbFormat { get; set; } = "CSV"; + + // ?? Racing ?????????????????????????????????????????????? + public string RacingApiKey { get; set; } = string.Empty; + public string RcExportPath { get; set; } = string.Empty; + public string RcPrefix { get; set; } = string.Empty; + public string RcSuffix { get; set; } = string.Empty; + public bool RcIncludeDate { get; set; } = true; + public string RcDateFormat { get; set; } = "yyyy-MM-dd"; + public string RcFormat { get; set; } = "CSV"; + public string RcTimezone { get; set; } = "Australia/Sydney"; + public List RcCountries { get; set; } = new() { "au", "nz" }; + + // ?? Persistence ????????????????????????????????????????? + + public static UserSettings Load() + { + try + { + if (!File.Exists(FilePath)) + return MigrateFromIniOrDefault(); + + var json = File.ReadAllText(FilePath); + return JsonSerializer.Deserialize(json, JsonOptions) ?? new UserSettings(); + } + catch + { + return new UserSettings(); + } + } + + public void Save() + { + var json = JsonSerializer.Serialize(this, JsonOptions); + File.WriteAllText(FilePath, json); + } + + /// + /// One-time migration: reads old settings.ini if present, converts to UserSettings, + /// saves the new usersettings.json, then deletes the ini file. + /// + private static UserSettings MigrateFromIniOrDefault() + { + var iniPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "settings.ini"); + if (!File.Exists(iniPath)) + return new UserSettings(); + + var settings = new UserSettings(); + try + { + foreach (var line in File.ReadAllLines(iniPath)) + { + var idx = line.IndexOf('='); + if (idx < 0) continue; + var key = line[..idx].Trim(); + var val = line[(idx + 1)..].Trim(); + + switch (key) + { + case "ApiKey": settings.ApiKey = val; break; + case "FbExportPath": settings.FbExportPath = val; break; + case "FbPrefix": settings.FbPrefix = val; break; + case "FbSuffix": settings.FbSuffix = val; break; + case "FbIncludeDate": settings.FbIncludeDate = val is "1" or "true" or "True"; break; + case "FbDateFormat": settings.FbDateFormat = val; break; + case "FbFormat": settings.FbFormat = val; break; + case "RcExportPath": settings.RcExportPath = val; break; + case "RcPrefix": settings.RcPrefix = val; break; + case "RcSuffix": settings.RcSuffix = val; break; + case "RcIncludeDate": settings.RcIncludeDate = val is "1" or "true" or "True"; break; + case "RcDateFormat": settings.RcDateFormat = val; break; + case "RcFormat": settings.RcFormat = val; break; + case "RacingApiKey": settings.RacingApiKey = val; break; + case "RcTimezone": settings.RcTimezone = val; break; + case "RcCountries": + settings.RcCountries = new List( + val.Split(new[] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries)); + break; + } + } + + // Persist as JSON and remove legacy file + settings.Save(); + File.Delete(iniPath); + } + catch + { + // If migration fails, return whatever we parsed + } + + return settings; + } + } +} diff --git a/HorseRacingPredictor/HorseRacingPredictor/MainWindow.xaml.cs b/HorseRacingPredictor/HorseRacingPredictor/MainWindow.xaml.cs index b473ff1..b925c73 100644 --- a/HorseRacingPredictor/HorseRacingPredictor/MainWindow.xaml.cs +++ b/HorseRacingPredictor/HorseRacingPredictor/MainWindow.xaml.cs @@ -831,54 +831,35 @@ namespace HorseRacingPredictor return null; } - // ???????????????????? SETTINGS ???????????????????? - - private string SettingsFilePath => - Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "settings.ini"); + // 覧覧覧覧覧覧覧覧覧 SETTINGS 覧覧覧覧覧覧覧覧覧 private void LoadSettings() { try { - txtRacingApiKey.Text = DefaultRacingApiKey; + var s = UserSettings.Load(); - // Default countries - SetSelectedCountries(new[] { "au", "nz" }); + txtApiKey.Text = s.ApiKey; + txtFbExportPath.Text = s.FbExportPath; + txtFbPrefix.Text = s.FbPrefix; + txtFbSuffix.Text = s.FbSuffix; + chkFbIncludeDate.IsChecked = s.FbIncludeDate; + SetComboBoxSelectionByContent(cmbFbDateFormat, s.FbDateFormat); + SetComboBoxSelectionByContent(cmbFbFormat, s.FbFormat); - if (!File.Exists(SettingsFilePath)) { ApplyRacingSettings(); return; } - foreach (var line in File.ReadAllLines(SettingsFilePath)) - { - var idx = line.IndexOf('='); - if (idx < 0) continue; - var key = line.Substring(0, idx).Trim(); - var val = line.Substring(idx + 1).Trim(); + txtRcExportPath.Text = s.RcExportPath; + txtRcPrefix.Text = s.RcPrefix; + txtRcSuffix.Text = s.RcSuffix; + chkRcIncludeDate.IsChecked = s.RcIncludeDate; + SetComboBoxSelectionByContent(cmbRcDateFormat, s.RcDateFormat); + SetComboBoxSelectionByContent(cmbRcFormat, s.RcFormat); - if (key == "ApiKey") txtApiKey.Text = val; - else if (key == "FbExportPath") txtFbExportPath.Text = val; - else if (key == "FbPrefix") txtFbPrefix.Text = val; - else if (key == "FbSuffix") txtFbSuffix.Text = val; - else if (key == "FbIncludeDate") chkFbIncludeDate.IsChecked = val == "1" || val.Equals("true", StringComparison.OrdinalIgnoreCase); - else if (key == "FbDateFormat") { try { SetComboBoxSelectionByContent(cmbFbDateFormat, val); } catch { } } - else if (key == "FbFormat") { try { SetComboBoxSelectionByContent(cmbFbFormat, val); } catch { } } - else if (key == "RcExportPath") txtRcExportPath.Text = val; - else if (key == "RcPrefix") txtRcPrefix.Text = val; - else if (key == "RcSuffix") txtRcSuffix.Text = val; - else if (key == "RcIncludeDate") chkRcIncludeDate.IsChecked = val == "1" || val.Equals("true", StringComparison.OrdinalIgnoreCase); - else if (key == "RcDateFormat") { try { SetComboBoxSelectionByContent(cmbRcDateFormat, val); } catch { } } - else if (key == "RcFormat") { try { SetComboBoxSelectionByContent(cmbRcFormat, val); } catch { } } - else if (key == "RacingApiKey") txtRacingApiKey.Text = val; - else if (key == "RcTimezone" && txtRcTimezone != null) txtRcTimezone.Text = val; - else if (key == "RcCountries") - { - var codes = val.Split(new[] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); - SetSelectedCountries(codes); - } - } + txtRacingApiKey.Text = string.IsNullOrEmpty(s.RacingApiKey) ? DefaultRacingApiKey : s.RacingApiKey; + if (txtRcTimezone != null) txtRcTimezone.Text = s.RcTimezone; + SetSelectedCountries(s.RcCountries.ToArray()); - // Update preview UI after loading values UpdateFbPreview(); UpdateRcPreview(); - ApplyRacingSettings(); } catch { } @@ -1110,29 +1091,29 @@ namespace HorseRacingPredictor { try { - var sb = new StringBuilder(); - sb.AppendLine($"ApiKey={txtApiKey.Text.Trim()}"); - sb.AppendLine($"FbExportPath={txtFbExportPath.Text.Trim()}"); - sb.AppendLine($"FbPrefix={txtFbPrefix.Text.Trim()}"); - sb.AppendLine($"FbSuffix={txtFbSuffix.Text.Trim()}"); - sb.AppendLine($"FbIncludeDate={(chkFbIncludeDate.IsChecked==true?"1":"0")}"); - sb.AppendLine($"FbDateFormat={(cmbFbDateFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "yyyy-MM-dd"}"); - sb.AppendLine($"FbFormat={(cmbFbFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "CSV"}"); - sb.AppendLine($"RcExportPath={txtRcExportPath.Text.Trim()}"); - sb.AppendLine($"RcPrefix={txtRcPrefix.Text.Trim()}"); - sb.AppendLine($"RcSuffix={txtRcSuffix.Text.Trim()}"); - sb.AppendLine($"RcIncludeDate={(chkRcIncludeDate.IsChecked==true?"1":"0")}"); - sb.AppendLine($"RcDateFormat={(cmbRcDateFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "yyyy-MM-dd"}"); - sb.AppendLine($"RcFormat={(cmbRcFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "CSV"}"); - sb.AppendLine($"RacingApiKey={txtRacingApiKey.Text.Trim()}"); - sb.AppendLine($"RcTimezone={txtRcTimezone?.Text?.Trim() ?? "Australia/Sydney"}"); - sb.AppendLine($"RcCountries={string.Join(",", GetSelectedCountries())}"); - File.WriteAllText(SettingsFilePath, sb.ToString(), Encoding.UTF8); + var s = new UserSettings + { + ApiKey = txtApiKey.Text.Trim(), + FbExportPath = txtFbExportPath.Text.Trim(), + FbPrefix = txtFbPrefix.Text.Trim(), + FbSuffix = txtFbSuffix.Text.Trim(), + FbIncludeDate = chkFbIncludeDate.IsChecked == true, + FbDateFormat = (cmbFbDateFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "yyyy-MM-dd", + FbFormat = (cmbFbFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "CSV", + RcExportPath = txtRcExportPath.Text.Trim(), + RcPrefix = txtRcPrefix.Text.Trim(), + RcSuffix = txtRcSuffix.Text.Trim(), + RcIncludeDate = chkRcIncludeDate.IsChecked == true, + RcDateFormat = (cmbRcDateFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "yyyy-MM-dd", + RcFormat = (cmbRcFormat?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "CSV", + RacingApiKey = txtRacingApiKey.Text.Trim(), + RcTimezone = txtRcTimezone?.Text?.Trim() ?? "Australia/Sydney", + RcCountries = new List(GetSelectedCountries()) + }; + s.Save(); - // update previews after save UpdateFbPreview(); UpdateRcPreview(); - ApplyRacingSettings(); MessageBox.Show("Impostazioni salvate con successo.",