using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tango.Logging;
namespace Tango.Settings
{
///
/// Represents a settings manager capable of holding a collection of settings objects, saving and loading them using JSON.
///
public class SettingsManager
{
#region Singleton
private static object _syncLock = new object();
private static String MUTEX_NAME = Assembly.GetEntryAssembly().GetName().Name;
private static SettingsManager _default;
///
/// Gets the default settings manager instance.
///
public static SettingsManager Default
{
get
{
lock (_syncLock)
{
if (_default == null)
{
_default = new SettingsManager();
}
}
return _default;
}
}
#endregion
private List _settingsCollection;
private JsonSerializerSettings _jsonSettings;
private LogManager _logManager;
private bool _loaded;
///
/// Gets or sets the settings file path.
///
public String FilePath { get; protected set; }
///
/// Gets or sets the settings folder.
///
public String Folder { get; protected set; }
///
/// Prevents a default instance of the class from being created.
///
private SettingsManager()
{
_logManager = LogManager.Default;
FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Twine", "Tango", "Settings", Path.GetFileNameWithoutExtension(AppDomain.CurrentDomain.FriendlyName) + ".json");
Folder = Path.GetDirectoryName(FilePath);
Directory.CreateDirectory(Folder);
_settingsCollection = new List();
_jsonSettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
Formatting = Formatting.Indented,
Error = (sender, args) =>
{
args.ErrorContext.Handled = true;
LogManager.Default.Log(args.ErrorContext.Error.Message, LogCategory.Error);
}
};
_jsonSettings.Converters.Add(new StringEnumConverter(false));
}
private void EnsureLoaded()
{
if (!_loaded)
{
Load();
}
}
///
/// Gets or creates the specified settings object type.
///
///
///
public T GetOrCreate() where T : SettingsBase
{
try
{
EnsureLoaded();
}
catch (Exception ex)
{
_logManager.Log(ex, "Error deserializing settings for " + typeof(T).Name);
}
var settings = _settingsCollection.SingleOrDefault(x => x.GetType() == typeof(T)) as T;
if (settings == null)
{
_logManager.Log("Settings for " + typeof(T).Name + " were not found. Initializing default settings.");
settings = Activator.CreateInstance();
settings.SaveAction = Save;
_settingsCollection.Add(settings);
}
return settings;
}
///
/// Loads the settings from the .
///
protected virtual void Load()
{
if (File.Exists(FilePath))
{
_logManager.Log("Loading settings from " + FilePath + "...");
_settingsCollection = JsonConvert.DeserializeObject>(File.ReadAllText(FilePath), _jsonSettings);
foreach (var settings in _settingsCollection)
{
settings.SaveAction = Save;
}
}
_loaded = true;
}
///
/// Determines whether a settings file exists.
///
public virtual bool IsFileExists()
{
return File.Exists(FilePath);
}
///
/// Saves settings.
///
public virtual void Save()
{
EnsureLoaded();
using (var mutex = new Mutex(false, MUTEX_NAME))
{
var mutexAcquired = false;
try
{
mutexAcquired = mutex.WaitOne(5000);
}
catch (AbandonedMutexException)
{
mutexAcquired = true;
}
// if it wasn't acquired, it timed out, so can handle that how ever we want
if (!mutexAcquired)
{
_logManager.Log("Error saving settings due to Mutex not acquired");
return;
}
// otherwise, we've acquired the mutex and should do what we need to do,
// then ensure that we always release the mutex
try
{
_logManager.Log("Saving settings to " + FilePath + "...");
String json = JsonConvert.SerializeObject(_settingsCollection, _jsonSettings);
File.WriteAllText(FilePath, json);
}
finally
{
mutex.ReleaseMutex();
}
}
}
}
}