using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
namespace StardewModdingAPI.Framework.Reflection
{
/// Generates proxy classes to access mod APIs through an arbitrary interface.
internal class InterfaceProxyFactory
{
/*********
** Fields
*********/
/// The CLR module in which to create proxy classes.
private readonly ModuleBuilder ModuleBuilder;
/// The generated proxy types.
private readonly IDictionary Builders = new Dictionary();
/*********
** Public methods
*********/
/// Construct an instance.
public InterfaceProxyFactory()
{
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName($"StardewModdingAPI.Proxies, Version={this.GetType().Assembly.GetName().Version}, Culture=neutral"), AssemblyBuilderAccess.Run);
this.ModuleBuilder = assemblyBuilder.DefineDynamicModule("StardewModdingAPI.Proxies");
}
/// Create an API proxy.
/// The interface through which to access the API.
/// The API instance to access.
/// The unique ID of the mod consuming the API.
/// The unique ID of the mod providing the API.
public TInterface CreateProxy(object instance, string sourceModID, string targetModID)
where TInterface : class
{
lock (this.Builders)
{
// validate
if (instance == null)
throw new InvalidOperationException("Can't proxy access to a null API.");
if (!typeof(TInterface).IsInterface)
throw new InvalidOperationException("The proxy type must be an interface, not a class.");
// get proxy type
Type targetType = instance.GetType();
string proxyTypeName = $"StardewModdingAPI.Proxies.From<{sourceModID}_{typeof(TInterface).FullName}>_To<{targetModID}_{targetType.FullName}>";
if (!this.Builders.TryGetValue(proxyTypeName, out InterfaceProxyBuilder builder))
{
builder = new InterfaceProxyBuilder(proxyTypeName, this.ModuleBuilder, typeof(TInterface), targetType);
this.Builders[proxyTypeName] = builder;
}
// create instance
return (TInterface)builder.CreateInstance(instance);
}
}
}
}