using System;
using System.Collections.Concurrent;
using System.Reflection;
using ZeroDDS.Cdr;
using ZeroDDS.Domain;
using ZeroDDS.Pub;
using ZeroDDS.Sub;
using ZeroDDS.Topic;
namespace ZeroDDS;
public interface IKeyHashProvider
{
byte[] KeyHashOf(object sample);
}
public sealed class DdsTopicTypeTraits<T> : ITopicTraits<T>, IKeyHashProvider where T : notnull
{
private readonly IDdsTopicType<T> _ts;
public DdsTopicTypeTraits(IDdsTopicType<T> typeSupport) =>
_ts = typeSupport ?? throw new ArgumentNullException(nameof(typeSupport));
public IDdsTopicType<T> TypeSupport => _ts;
public string TypeName => _ts.TypeName;
public byte[] Encode(T value) => _ts.Encode(value);
public T Decode(ReadOnlySpan<byte> bytes) => _ts.Decode(bytes);
public T Decode(ReadOnlySpan<byte> bytes, EndianMode endian) => _ts.Decode(bytes, endian);
public T Decode(ReadOnlySpan<byte> bytes, EndianMode endian, int representation) =>
_ts.Decode(bytes, endian, representation);
public byte[] KeyHash(T value) => _ts.KeyHash(value);
byte[] IKeyHashProvider.KeyHashOf(object sample) => _ts.KeyHash((T)sample);
}
public static class TypeSupportRegistry
{
private static readonly ConcurrentDictionary<Type, object> _map = new();
public static void Register<T>(IDdsTopicType<T> typeSupport) where T : notnull =>
_map[typeof(T)] = typeSupport ?? throw new ArgumentNullException(nameof(typeSupport));
public static IDdsTopicType<T> Resolve<T>() where T : notnull
{
if (_map.TryGetValue(typeof(T), out var existing))
return (IDdsTopicType<T>)existing;
var ts = Discover<T>();
if (ts is null)
{
throw new InvalidOperationException(
$"No IDdsTopicType<{typeof(T).Name}> found. Register one via " +
$"TypeSupportRegistry.Register, or ensure a '{typeof(T).Name}TypeSupport' " +
"class with a static 'Instance' member is loaded.");
}
_map[typeof(T)] = ts;
return ts;
}
private static IDdsTopicType<T>? Discover<T>() where T : notnull
{
var t = typeof(T);
var byName = t.Assembly.GetType(t.FullName + "TypeSupport");
var fromName = InstanceOf<T>(byName);
if (fromName is not null) return fromName;
foreach (var candidate in t.Assembly.GetTypes())
{
if (candidate == t) continue;
if (typeof(IDdsTopicType<T>).IsAssignableFrom(candidate))
{
var inst = InstanceOf<T>(candidate);
if (inst is not null) return inst;
}
}
return null;
}
private static IDdsTopicType<T>? InstanceOf<T>(Type? candidate) where T : notnull
{
if (candidate is null) return null;
if (!typeof(IDdsTopicType<T>).IsAssignableFrom(candidate)) return null;
var field = candidate.GetField("Instance",
BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
if (field?.GetValue(null) is IDdsTopicType<T> fv) return fv;
var prop = candidate.GetProperty("Instance",
BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
if (prop?.GetValue(null) is IDdsTopicType<T> pv) return pv;
if (candidate.GetConstructor(Type.EmptyTypes) is not null &&
Activator.CreateInstance(candidate) is IDdsTopicType<T> created)
{
return created;
}
return null;
}
}
public static class TypedFluentExtensions
{
public static Topic<T> CreateTypedTopic<T>(this DomainParticipant dp, string name)
where T : notnull
{
var traits = new DdsTopicTypeTraits<T>(TypeSupportRegistry.Resolve<T>());
return new Topic<T>(dp, name, traits);
}
public static Topic<T> CreateTypedTopic<T>(this DomainParticipant dp, string name,
IDdsTopicType<T> typeSupport) where T : notnull =>
new Topic<T>(dp, name, new DdsTopicTypeTraits<T>(typeSupport));
public static DataWriter<T> CreateTypedWriter<T>(this Publisher pub, Topic<T> topic)
where T : notnull =>
new DataWriter<T>(pub, topic);
public static DataReader<T> CreateTypedReader<T>(this Subscriber sub, Topic<T> topic)
where T : notnull =>
new DataReader<T>(sub, topic);
}