Files
eveo/src/Eve-O-Preview/Configuration/Implementation/ConfigurationStorage.cs

204 lines
9.2 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
namespace EveOPreview.Configuration.Implementation {
class ConfigurationStorage : IConfigurationStorage {
private const string CONFIGURATION_FILE_NAME = "EVE-O-Preview.json";
private const string PROFILES_DIRECTORY = "Profiles";
private const string MASTER_CONFIG_FILE = "master.json";
private readonly IAppConfig _appConfig;
private readonly IThumbnailConfiguration _thumbnailConfiguration;
public ConfigurationStorage(IAppConfig appConfig, IThumbnailConfiguration thumbnailConfiguration) {
this._appConfig = appConfig;
this._thumbnailConfiguration = thumbnailConfiguration;
}
public void Load() {
string masterConfigPath = this.GetMasterConfigPath();
// First, load the master configuration which contains ProfileReadOnly and CurrentProfile
string currentProfile = "Default";
bool profileReadOnly = false;
if (File.Exists(masterConfigPath)) {
string masterRawData = File.ReadAllText(masterConfigPath);
var masterConfig = JsonConvert.DeserializeObject<dynamic>(masterRawData);
if (masterConfig.CurrentProfile != null) {
currentProfile = masterConfig.CurrentProfile.ToString();
}
if (masterConfig.ProfileReadOnly != null) {
profileReadOnly = (bool)masterConfig.ProfileReadOnly;
}
}
this._thumbnailConfiguration.CurrentProfile = currentProfile;
this._thumbnailConfiguration.ProfileReadOnly = profileReadOnly;
// Scan the profiles directory for available profiles AFTER loading master config
List<string> scannedProfiles = this.ScanAvailableProfiles();
// Replace AvailableProfiles with scanned profiles (they are the source of truth)
this._thumbnailConfiguration.AvailableProfiles.Clear();
foreach (string profile in scannedProfiles) {
this._thumbnailConfiguration.AvailableProfiles.Add(profile);
}
// Ensure CurrentProfile is in the list
if (!string.IsNullOrEmpty(this._thumbnailConfiguration.CurrentProfile) &&
!this._thumbnailConfiguration.AvailableProfiles.Contains(this._thumbnailConfiguration.CurrentProfile)) {
this._thumbnailConfiguration.AvailableProfiles.Add(this._thumbnailConfiguration.CurrentProfile);
}
// Then, load the profile-specific configuration
// IMPORTANT: Reset the configuration to clean state before loading
// to avoid merging with existing default values
string profileConfigPath = this.GetProfileConfigPath(currentProfile);
if (File.Exists(profileConfigPath)) {
string profileRawData = File.ReadAllText(profileConfigPath);
JsonSerializerSettings jsonSerializerSettings =
new JsonSerializerSettings() { ObjectCreationHandling = ObjectCreationHandling.Replace };
JsonConvert.PopulateObject(profileRawData, this._thumbnailConfiguration, jsonSerializerSettings);
} else {
// Try to load from legacy config file for backwards compatibility
string legacyConfigPath = this.GetConfigFileName();
if (File.Exists(legacyConfigPath)) {
string legacyRawData = File.ReadAllText(legacyConfigPath);
JsonSerializerSettings jsonSerializerSettings =
new JsonSerializerSettings() { ObjectCreationHandling = ObjectCreationHandling.Replace };
JsonConvert.PopulateObject(legacyRawData, this._thumbnailConfiguration, jsonSerializerSettings);
}
}
// Restore the values we just set (they might have been overwritten by profile load)
this._thumbnailConfiguration.CurrentProfile = currentProfile;
this._thumbnailConfiguration.ProfileReadOnly = profileReadOnly;
// Re-scan profiles after loading in case the profile overwrote AvailableProfiles
this._thumbnailConfiguration.AvailableProfiles.Clear();
foreach (string profile in scannedProfiles) {
this._thumbnailConfiguration.AvailableProfiles.Add(profile);
}
// Validate data after loading it
this._thumbnailConfiguration.ApplyRestrictions();
}
public void Save() {
// Save master configuration (contains ProfileReadOnly and CurrentProfile)
this.SaveMasterConfig();
// If profile is read-only, don't save to the profile file
if (!this._thumbnailConfiguration.ProfileReadOnly) {
this.SaveProfileConfig();
}
}
private void SaveMasterConfig() {
string masterConfigPath = this.GetMasterConfigPath();
string directory = Path.GetDirectoryName(masterConfigPath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) {
Directory.CreateDirectory(directory);
}
// Create a minimal master config with only ProfileReadOnly and CurrentProfile
// AvailableProfiles is determined by scanning the folder, not saved here
var masterConfig = new {
CurrentProfile = this._thumbnailConfiguration.CurrentProfile ?? "Default",
ProfileReadOnly = this._thumbnailConfiguration.ProfileReadOnly
};
string rawData = JsonConvert.SerializeObject(masterConfig, Formatting.Indented);
try {
File.WriteAllText(masterConfigPath, rawData);
} catch (IOException) {
// Ignore error if for some reason the updated config cannot be written down
}
}
private void SaveProfileConfig() {
string profileConfigPath = this.GetProfileConfigPath();
string directory = Path.GetDirectoryName(profileConfigPath);
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) {
Directory.CreateDirectory(directory);
}
// Serialize the full configuration to the profile file
string rawData = JsonConvert.SerializeObject(this._thumbnailConfiguration, Formatting.Indented);
try {
File.WriteAllText(profileConfigPath, rawData);
} catch (IOException) {
// Ignore error if for some reason the updated config cannot be written down
}
}
private string GetMasterConfigPath() {
return Path.Combine(PROFILES_DIRECTORY, MASTER_CONFIG_FILE);
}
private string GetProfileConfigPath() {
return this.GetProfileConfigPath(this._thumbnailConfiguration.CurrentProfile ?? "Default");
}
private string GetProfileConfigPath(string profileName) {
string sanitizedProfileName = this.SanitizeFileName(profileName);
return Path.Combine(PROFILES_DIRECTORY, $"{sanitizedProfileName}.json");
}
private string GetConfigFileName() {
return string.IsNullOrEmpty(this._appConfig.ConfigFileName) ? ConfigurationStorage.CONFIGURATION_FILE_NAME
: this._appConfig.ConfigFileName;
}
private string SanitizeFileName(string fileName) {
string invalidChars = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
foreach (char c in invalidChars) {
fileName = fileName.Replace(c, '_');
}
return fileName;
}
private List<string> ScanAvailableProfiles() {
List<string> profiles = new List<string>();
if (!Directory.Exists(PROFILES_DIRECTORY)) {
profiles.Add("Default");
return profiles;
}
try {
foreach (string filePath in Directory.GetFiles(PROFILES_DIRECTORY, "*.json")) {
string fileName = Path.GetFileNameWithoutExtension(filePath);
// Skip the master config file
if (fileName.Equals(MASTER_CONFIG_FILE.Replace(".json", ""), StringComparison.OrdinalIgnoreCase)) {
continue;
}
// Add the profile name
if (!string.IsNullOrEmpty(fileName) && !profiles.Contains(fileName)) {
profiles.Add(fileName);
}
}
} catch (IOException) {
// If there's an error scanning the directory, just return what we have
}
// Ensure Default is always in the list
if (!profiles.Contains("Default")) {
profiles.Add("Default");
}
return profiles;
}
}
}