using SharpChat.Snowflake; namespace SharpChat.Channels; public class ChannelsContext(RandomSnowflake snowflake) { private readonly Dictionary<long, Channel> Channels = []; private Channel? DefaultChannel = null; private readonly Lock @lock = new(); public bool ChannelExists(Func<Channel, bool> predicate) { lock(@lock) return Channels.Values.Any(predicate); } public bool ChannelExists(string name) { return ChannelExists(c => c.NameEquals(name)); } public Channel GetDefaultChannel() { lock(@lock) { DefaultChannel ??= GetChannel(c => c.IsPublic && !c.IsTemporary) ?? throw new NoDefaultChannelException(); return DefaultChannel; } } public void SetDefaultChannel(Channel channel) => SetDefaultChannel(channel.Name); public void SetDefaultChannel(string channelName) { lock(@lock) DefaultChannel = GetChannel(channelName) ?? throw new ChannelNotFoundException(nameof(channelName)); } public Channel? GetChannel(Func<Channel, bool> predicate) { lock(@lock) return Channels.Values.FirstOrDefault(predicate); } public Channel? GetChannel(string name) { return GetChannel(c => c.NameEquals(name)); } public Channel? GetChannel(long id) { lock(@lock) return Channels.TryGetValue(id, out Channel? channel) ? channel : null; } public IEnumerable<Channel> GetChannels() { lock(@lock) return [.. Channels.Values]; } public IEnumerable<Channel> GetChannels(IEnumerable<long> ids) { return [.. ids.Select(GetChannel).Where(c => c is not null).Cast<Channel>()]; } public IEnumerable<Channel> GetChannels(Func<Channel, bool> predicate) { lock(@lock) return [.. Channels.Values.Where(predicate)]; } public IEnumerable<Channel> GetChannels(IEnumerable<string> names) { return GetChannels(c => names.Any(n => c.NameEquals(n))); } public IEnumerable<Channel> GetChannels(int maxRank) { return GetChannels(c => c.Rank <= maxRank); } public Channel CreateChannel( string name, string password = "", bool temporary = false, int rank = 0, string ownerId = "", long? id = null ) { if(!Channel.CheckName(name)) throw new ChannelNameFormatException(nameof(name)); lock(@lock) { if(ChannelExists(name)) throw new ChannelExistsException(nameof(name)); id ??= snowflake.Next(); if(Channels.ContainsKey(id.Value)) throw new ChannelExistsException(nameof(id)); Channel channel = new(id.Value, name, password, temporary, rank, ownerId); Channels.Add(id.Value, channel); return channel; } } public ChannelDiff UpdateChannel( long id, string? name = null, string? password = null, bool? temporary = null, int? rank = null, string? ownerId = null ) => UpdateChannelInternal( GetChannel(id) ?? throw new ChannelNotFoundException(nameof(id)), name, password, temporary, rank, ownerId ); public ChannelDiff UpdateChannel( string currentName, string? name = null, string? password = null, bool? temporary = null, int? rank = null, string? ownerId = null ) => UpdateChannelInternal( GetChannel(currentName) ?? throw new ChannelNotFoundException(nameof(currentName)), name, password, temporary, rank, ownerId ); public ChannelDiff UpdateChannel( Channel channel, string? name = null, string? password = null, bool? temporary = null, int? rank = null, string? ownerId = null ) => UpdateChannel(channel.Id, name, password, temporary, rank, ownerId); private ChannelDiff UpdateChannelInternal( Channel channel, string? name = null, string? password = null, bool? temporary = null, int? rank = null, string? ownerId = null ) { lock(@lock) { StringDiff nameDiff = new(channel.Name, name); if(nameDiff.Changed) { if(!Channel.CheckName(nameDiff.After)) throw new ChannelNameFormatException(nameof(name)); if(ChannelExists(nameDiff.After)) throw new ChannelExistsException(nameof(name)); channel.Name = nameDiff.After; } StringDiff passwordDiff = new(channel.Password, password); if(passwordDiff.Changed) channel.Password = passwordDiff.After; ValueDiff<bool> temporaryDiff = new(channel.IsTemporary, temporary); if(temporaryDiff.Changed) channel.IsTemporary = temporaryDiff.After; ValueDiff<int> rankDiff = new(channel.Rank, rank); if(rankDiff.Changed) channel.Rank = rankDiff.After; StringDiff ownerIdDiff = new(channel.OwnerId, ownerId); if(ownerIdDiff.Changed) channel.OwnerId = ownerIdDiff.After; return new( channel, nameDiff, passwordDiff, temporaryDiff, rankDiff, ownerIdDiff ); } } public void RemoveChannel(long id) => RemoveChannelInternal(GetChannel(id) ?? throw new ChannelNotFoundException(nameof(id)), nameof(id)); public void RemoveChannel(Channel channel) => RemoveChannel(channel.Name); public void RemoveChannel(string name) => RemoveChannelInternal(GetChannel(name) ?? throw new ChannelNotFoundException(nameof(name)), nameof(name)); private void RemoveChannelInternal(Channel channel, string argName) { lock(@lock) { Channel defaultChannel = GetDefaultChannel(); if(channel == defaultChannel || defaultChannel.NameEquals(channel.Name)) throw new ChannelIsDefaultException(argName); Channels.Remove(channel.Id); } } }