1using System; 2using System.Collections.Generic; 3using System.IO; 4using System.Runtime.Serialization; 5using System.Runtime.Serialization.Json; 6using System.Text; 7 8namespace WindowCapture.Models 9{ 10 /// <summary> 11 /// Stores title page template data and field values 12 /// Saved as JSON to %AppData%\WindowCapture\titlepage_data.json 13 /// </summary> 14 [DataContract] 15 public class TitlePageData 16 { 17 private static string dataPath; 18 private static TitlePageData instance; 19 20 static TitlePageData() 21 { 22 dataPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "titlepage_data.json"); 23 } 24 25 [DataMember] 26 public string TemplatePath { get; set; } 27 28 [DataMember] 29 public string LastWorkNumber { get; set; } 30 31 [DataMember] 32 public List<string> Disciplines { get; set; } 33 34 [DataMember] 35 public List<string> Topics { get; set; } 36 37 [DataMember] 38 public List<string> Students { get; set; } 39 40 [DataMember] 41 public List<string> Groups { get; set; } 42 43 [DataMember] 44 public List<string> Teachers { get; set; } 45 46 [DataMember] 47 public LastSelectedIndices LastSelected { get; set; } 48 49 [DataMember] 50 public string GeminiApiKey { get; set; } 51 52 [DataMember] 53 public GeminiSettings Gemini { get; set; } 54 55 [DataMember] 56 public AudioPlayerSettings AudioPlayer { get; set; } 57 58 public TitlePageData() 59 { 60 TemplatePath = ""; 61 LastWorkNumber = "1"; 62 Disciplines = new List<string>(); 63 Topics = new List<string>(); 64 Students = new List<string>(); 65 Groups = new List<string>(); 66 Teachers = new List<string>(); 67 LastSelected = new LastSelectedIndices(); 68 GeminiApiKey = ""; 69 Gemini = new GeminiSettings(); 70 AudioPlayer = new AudioPlayerSettings(); 71 } 72 73 /// <summary> 74 /// Get singleton instance (loads from file if not loaded) 75 /// </summary> 76 public static TitlePageData Instance 77 { 78 get 79 { 80 if (instance == null) 81 { 82 instance = Load(); 83 } 84 return instance; 85 } 86 } 87 88 /// <summary> 89 /// Load data from JSON file 90 /// </summary> 91 public static TitlePageData Load() 92 { 93 try 94 { 95 if (File.Exists(dataPath)) 96 { 97 var json = File.ReadAllText(dataPath, Encoding.UTF8); 98 var serializer = new DataContractJsonSerializer(typeof(TitlePageData)); 99 using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(json))) 100 { 101 var data = (TitlePageData)serializer.ReadObject(ms); 102 if (data != null) 103 { 104 // Ensure lists are not null 105 if (data.Disciplines == null) data.Disciplines = new List<string>(); 106 if (data.Topics == null) data.Topics = new List<string>(); 107 if (data.Students == null) data.Students = new List<string>(); 108 if (data.Groups == null) data.Groups = new List<string>(); 109 if (data.Teachers == null) data.Teachers = new List<string>(); 110 if (data.LastSelected == null) data.LastSelected = new LastSelectedIndices(); 111 if (data.Gemini == null) data.Gemini = new GeminiSettings(); 112 if (data.AudioPlayer == null) data.AudioPlayer = new AudioPlayerSettings(); 113 return data; 114 } 115 } 116 } 117 } 118 catch (Exception ex) 119 { 120 System.Diagnostics.Debug.WriteLine("Failed to load title page data: " + ex.Message); 121 } 122 return new TitlePageData(); 123 } 124 125 /// <summary> 126 /// Save data to JSON file 127 /// </summary> 128 public void Save() 129 { 130 try 131 { 132 var dir = Path.GetDirectoryName(dataPath); 133 if (!Directory.Exists(dir)) 134 Directory.CreateDirectory(dir); 135 136 var serializer = new DataContractJsonSerializer(typeof(TitlePageData)); 137 using (var ms = new MemoryStream()) 138 { 139 serializer.WriteObject(ms, this); 140 var json = Encoding.UTF8.GetString(ms.ToArray()); 141 File.WriteAllText(dataPath, json, Encoding.UTF8); 142 } 143 } 144 catch (Exception ex) 145 { 146 System.Diagnostics.Debug.WriteLine("Failed to save title page data: " + ex.Message); 147 } 148 } 149 150 /// <summary> 151 /// Export settings to file (without API keys) 152 /// </summary> 153 public bool ExportSettings(string filePath) 154 { 155 try 156 { 157 // Create a copy without API keys 158 var exportData = new TitlePageData 159 { 160 TemplatePath = this.TemplatePath, 161 LastWorkNumber = this.LastWorkNumber, 162 Disciplines = this.Disciplines != null ? new List<string>(this.Disciplines) : new List<string>(), 163 Topics = this.Topics != null ? new List<string>(this.Topics) : new List<string>(), 164 Students = this.Students != null ? new List<string>(this.Students) : new List<string>(), 165 Groups = this.Groups != null ? new List<string>(this.Groups) : new List<string>(), 166 Teachers = this.Teachers != null ? new List<string>(this.Teachers) : new List<string>(), 167 LastSelected = this.LastSelected, 168 GeminiApiKey = null, // Don't export API key 169 Gemini = new GeminiSettings 170 { 171 Model = this.Gemini.Model, 172 ThinkingBudget = this.Gemini.ThinkingBudget, 173 MediaResolution = this.Gemini.MediaResolution, 174 SystemInstruction = this.Gemini.SystemInstruction, 175 ModelStatuses = null, // Don't export statuses 176 ApiKeys = null, // Don't export API keys 177 CurrentApiKeyIndex = 0, 178 LastWorkingModel = null 179 }, 180 AudioPlayer = this.AudioPlayer 181 }; 182 183 var serializer = new DataContractJsonSerializer(typeof(TitlePageData)); 184 using (var ms = new MemoryStream()) 185 { 186 serializer.WriteObject(ms, exportData); 187 var json = Encoding.UTF8.GetString(ms.ToArray()); 188 File.WriteAllText(filePath, json, Encoding.UTF8); 189 } 190 return true; 191 } 192 catch (Exception ex) 193 { 194 System.Diagnostics.Debug.WriteLine("Failed to export settings: " + ex.Message); 195 return false; 196 } 197 } 198 199 /// <summary> 200 /// Import settings from file (preserves API keys) 201 /// </summary> 202 public bool ImportSettings(string filePath) 203 { 204 try 205 { 206 if (!File.Exists(filePath)) 207 return false; 208 209 var json = File.ReadAllText(filePath, Encoding.UTF8); 210 var serializer = new DataContractJsonSerializer(typeof(TitlePageData)); 211 using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(json))) 212 { 213 var importData = (TitlePageData)serializer.ReadObject(ms); 214 if (importData == null) 215 return false; 216 217 // Import all except API keys 218 this.TemplatePath = importData.TemplatePath; 219 this.LastWorkNumber = importData.LastWorkNumber; 220 221 // Merge lists (add new items, keep existing) 222 MergeLists(this.Disciplines, importData.Disciplines); 223 MergeLists(this.Topics, importData.Topics); 224 MergeLists(this.Students, importData.Students); 225 MergeLists(this.Groups, importData.Groups); 226 MergeLists(this.Teachers, importData.Teachers); 227 228 if (importData.LastSelected != null) 229 this.LastSelected = importData.LastSelected; 230 231 // Import Gemini settings (except API keys) 232 if (importData.Gemini != null) 233 { 234 this.Gemini.Model = importData.Gemini.Model; 235 this.Gemini.ThinkingBudget = importData.Gemini.ThinkingBudget; 236 this.Gemini.MediaResolution = importData.Gemini.MediaResolution; 237 this.Gemini.SystemInstruction = importData.Gemini.SystemInstruction; 238 // Keep existing API keys and statuses 239 } 240 241 if (importData.AudioPlayer != null) 242 { 243 this.AudioPlayer.Width = importData.AudioPlayer.Width; 244 this.AudioPlayer.Height = importData.AudioPlayer.Height; 245 } 246 247 Save(); 248 return true; 249 } 250 } 251 catch (Exception ex) 252 { 253 System.Diagnostics.Debug.WriteLine("Failed to import settings: " + ex.Message); 254 return false; 255 } 256 } 257 258 private void MergeLists(List<string> target, List<string> source) 259 { 260 if (source == null) return; 261 if (target == null) return; 262 263 foreach (var item in source) 264 { 265 if (!string.IsNullOrEmpty(item) && !target.Contains(item)) 266 target.Add(item); 267 } 268 } 269 270 /// <summary> 271 /// Add item to a list if not already present 272 /// </summary> 273 public bool AddToList(string listName, string value) 274 { 275 if (string.IsNullOrWhiteSpace(value)) return false; 276 277 List<string> list = GetList(listName); 278 if (list == null) return false; 279 280 if (!list.Contains(value)) 281 { 282 list.Add(value); 283 Save(); 284 return true; 285 } 286 return false; 287 } 288 289 /// <summary> 290 /// Get list by name 291 /// </summary> 292 public List<string> GetList(string listName) 293 { 294 switch (listName.ToLower()) 295 { 296 case "discipline": 297 case "disciplines": 298 return Disciplines; 299 case "topic": 300 case "topics": 301 return Topics; 302 case "student": 303 case "students": 304 return Students; 305 case "group": 306 case "groups": 307 return Groups; 308 case "teacher": 309 case "teachers": 310 return Teachers; 311 default: 312 return null; 313 } 314 } 315 316 /// <summary> 317 /// Get last selected index for a field 318 /// </summary> 319 public int GetLastSelectedIndex(string fieldName) 320 { 321 switch (fieldName.ToLower()) 322 { 323 case "discipline": return LastSelected.Discipline; 324 case "topic": return LastSelected.Topic; 325 case "student": return LastSelected.Student; 326 case "group": return LastSelected.Group; 327 case "teacher": return LastSelected.Teacher; 328 default: return 0; 329 } 330 } 331 332 /// <summary> 333 /// Set last selected index for a field 334 /// </summary> 335 public void SetLastSelectedIndex(string fieldName, int index) 336 { 337 switch (fieldName.ToLower()) 338 { 339 case "discipline": LastSelected.Discipline = index; break; 340 case "topic": LastSelected.Topic = index; break; 341 case "student": LastSelected.Student = index; break; 342 case "group": LastSelected.Group = index; break; 343 case "teacher": LastSelected.Teacher = index; break; 344 } 345 Save(); 346 } 347 } 348}