using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using IPA.Config.Providers;
using IPA.Utilities;
#if NET3
using Net3_Proxy;
using Path = Net3_Proxy.Path;
using Array = Net3_Proxy.Array;
#endif
namespace IPA.Config
{
///
/// A class to handle updating ConfigProviders automatically
///
public class Config
{
static Config()
{
JsonConfigProvider.RegisterConfig();
}
///
/// Specifies that a particular parameter is preferred to use a particular .
/// If it is not available, also specifies backups. If none are available, the default is used.
///
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class PreferAttribute : Attribute
{
///
/// The order of preference for the config type.
///
/// the list of config extensions in order of preference
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public string[] PreferenceOrder { get; private set; }
///
///
/// Constructs the attribute with a specific preference list. Each entry is the extension without a '.'
///
/// The preferences in order of preference.
public PreferAttribute(params string[] preference)
{
PreferenceOrder = preference;
}
}
///
/// Specifies a preferred config name, instead of using the plugin's name.
///
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class NameAttribute : Attribute
{
///
/// The name to use for the config.
///
/// the name to use for the config
// ReSharper disable once UnusedAutoPropertyAccessor.Global
public string Name { get; private set; }
///
///
/// Constructs the attribute with a specific name.
///
/// the name to use for the config.
public NameAttribute(string name)
{
Name = name;
}
}
private static readonly Dictionary registeredProviders = new Dictionary();
///
/// Registers a to use for configs.
///
/// the type to register
public static void Register() where T : IConfigProvider => Register(typeof(T));
///
/// Registers a to use for configs.
///
/// the type to register
public static void Register(Type type)
{
var inst = Activator.CreateInstance(type) as IConfigProvider;
if (inst == null)
throw new ArgumentException($"Type not an {nameof(IConfigProvider)}");
if (registeredProviders.ContainsKey(inst.Extension))
throw new InvalidOperationException($"Extension provider for {inst.Extension} already exists");
registeredProviders.Add(inst.Extension, inst);
}
///
/// Gets a object using the specified list of preferred config types.
///
/// the name of the mod for this config
/// the preferred config types to try to get
/// a using the requested format, or of type JSON.
public static Config GetConfigFor(string configName, params string[] extensions)
{
var chosenExt = extensions.FirstOrDefault(s => registeredProviders.ContainsKey(s)) ?? "json";
var provider = registeredProviders[chosenExt];
var filename = Path.Combine(BeatSaber.UserDataPath, configName + "." + provider.Extension);
var config = new Config(configName, provider, new FileInfo(filename));
ConfigRuntime.RegisterConfig(config);
return config;
}
internal static Config GetConfigFor(string modName, ParameterInfo info)
{
var prefs = Array.Empty();
if (info.GetCustomAttribute() is PreferAttribute prefer)
prefs = prefer.PreferenceOrder;
if (info.GetCustomAttribute() is NameAttribute name)
modName = name.Name;
return GetConfigFor(modName, prefs);
}
///
/// Gets the name associated with this object.
///
public string Name { get; private set; }
///
/// Gets the associated with this object.
///
public IConfigProvider Provider { get; private set; }
internal IConfigStore Store = null;
internal readonly FileInfo File;
///
/// Sets this object's . Can only be called once.
///
/// the to add to this instance
/// If this was called before.
public void SetStore(IConfigStore store)
{
if (Store != null)
throw new InvalidOperationException($"{nameof(SetStore)} can only be called once");
Store = store;
ConfigRuntime.ConfigChanged();
}
///
/// Forces a synchronous load from disk.
///
public void LoadSync() => LoadAsync().Wait();
///
/// Forces an asynchronous load from disk.
///
public Task LoadAsync() => ConfigRuntime.TriggerFileLoad(this);
private Config(string name, IConfigProvider provider, FileInfo file)
{
Name = name; Provider = provider; File = file;
}
}
}