using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using GameCult.Caching;
namespace GameCult.Networking
{
///
/// Replicates selected remote database records into a local typed CultCache over one retained session.
///
public sealed class CultNetDatabaseSubscriptionClient : IDisposable
{
private readonly ICultNetSchemaClient _client;
private readonly CultCache _cache;
private readonly CultNetDocumentRegistry _documents;
private readonly ConcurrentDictionary>> _initialSnapshots = new();
private readonly object _queueLock = new();
private Task _queue = Task.CompletedTask;
private bool _disposed;
public CultNetDatabaseSubscriptionClient(
ICultNetSchemaClient client,
CultCache cache,
CultNetDocumentRegistry documents)
{
_client = client ?? throw new ArgumentNullException(nameof(client));
_cache = cache ?? throw new ArgumentNullException(nameof(cache));
_documents = documents ?? throw new ArgumentNullException(nameof(documents));
_client.OnCultNet(HandleSnapshot);
_client.OnCultNet(HandleChange);
}
/// Raised after a live change has been applied to the local typed cache.
public event Action? Changed;
/// Gets the local reactive cache receiving replicated documents.
public CultCache Cache => _cache;
///
/// Starts a remote subscription and waits until its initial matching snapshot is in the local cache.
///
public Task> SubscribeAsync(
string subscriptionId,
IEnumerable? recordKeys = null,
IEnumerable? schemaIds = null,
bool includeSnapshot = true)
{
if (_disposed) throw new ObjectDisposedException(nameof(CultNetDatabaseSubscriptionClient));
if (!_client.Connected) throw new InvalidOperationException("CultNet schema client must be connected before subscribing.");
if (string.IsNullOrWhiteSpace(subscriptionId)) throw new ArgumentException("Subscription id is required.", nameof(subscriptionId));
var messageId = Guid.NewGuid().ToString("N");
TaskCompletionSource>? completion = null;
if (includeSnapshot)
{
completion = new TaskCompletionSource>(TaskCreationOptions.RunContinuationsAsynchronously);
if (!_initialSnapshots.TryAdd(messageId, completion))
throw new InvalidOperationException($"Duplicate database subscription message id '{messageId}'.");
}
_client.SendCultNet(new CultNetDatabaseSubscribeMessage
{
MessageId = messageId,
SubscriptionId = subscriptionId,
RecordKeys = recordKeys?.Where(value => !string.IsNullOrWhiteSpace(value)).Distinct(StringComparer.Ordinal).ToArray(),
SchemaIds = schemaIds?.Where(value => !string.IsNullOrWhiteSpace(value)).Distinct(StringComparer.Ordinal).ToArray(),
IncludeSnapshot = includeSnapshot
});
return completion?.Task ?? Task.FromResult>(Array.Empty