sharp-chat/SharpChat.Common/Users/UserManager.cs

275 lines
10 KiB
C#

using SharpChat.Events;
using SharpChat.Users.Remote;
using System;
using System.Collections.Generic;
using System.Linq;
namespace SharpChat.Users {
public class UserManager : IEventHandler {
private HashSet<User> Users { get; } = new();
private IEventDispatcher Dispatcher { get; }
private readonly object Sync = new();
public UserManager(IEventDispatcher dispatcher) {
Dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
}
private void OnConnect(object sender, UserConnectEvent uce) {
if(sender == this)
return;
lock(Sync) {
if(Users.Any(u => u.UserId == uce.UserId))
throw new ArgumentException(@"User already registered?????", nameof(uce));
Users.Add(new User(
uce.UserId,
uce.Name,
uce.Colour,
uce.Rank,
uce.Permissions,
uce.Status,
uce.StatusMessage,
uce.NickName
));
}
}
public void Disconnect(IUser user, UserDisconnectReason reason = UserDisconnectReason.Unknown) {
if(user == null)
return;
lock(Sync)
if(Users.Contains(user))
Dispatcher.DispatchEvent(this, new UserDisconnectEvent(user, reason));
}
public void Disconnect(IRemoteUser remoteUser, UserDisconnectReason reason = UserDisconnectReason.Unknown) {
if(remoteUser == null)
return;
GetUser(remoteUser, user => Disconnect(user, reason));
}
private void OnDisconnect(object sender, UserDisconnectEvent ude) {
GetUser(ude.UserId, user => {
if(user == null)
return;
if(user is IEventHandler ueh)
ueh.HandleEvent(sender, ude);
});
}
private void OnUpdate(object sender, UserUpdateEvent uue) {
GetUser(uue.UserId, user => {
if(user == null)
return;
if(user is IEventHandler ueh)
ueh.HandleEvent(sender, uue);
});
}
public void GetUser(Func<ILocalUser, bool> predicate, Action<ILocalUser> callback) {
if(predicate == null)
throw new ArgumentNullException(nameof(predicate));
if(callback == null)
throw new ArgumentNullException(nameof(callback));
lock(Sync)
callback(Users.FirstOrDefault(predicate));
}
public void GetUser(long userId, Action<ILocalUser> callback) {
if(callback == null)
throw new ArgumentNullException(nameof(callback));
GetUser(u => u.UserId == userId, callback);
}
public void GetUser(IUser user, Action<ILocalUser> callback) {
if(user == null)
throw new ArgumentNullException(nameof(user));
if(callback == null)
throw new ArgumentNullException(nameof(callback));
GetUser(user.Equals, callback);
}
public void GetUser(IRemoteUser remoteUser, Action<ILocalUser> callback) {
if(remoteUser == null)
throw new ArgumentNullException(nameof(remoteUser));
if(callback == null)
throw new ArgumentNullException(nameof(callback));
GetUser(u => u.UserId == remoteUser.UserId, callback);
}
[Flags]
public enum NameLookUpMode {
UserName = 0x1,
NickName = 0x2,
UserNameAndNickName = UserName | NickName,
}
public void GetUser(string userName, Action<ILocalUser> callback, NameLookUpMode lookUpMode = NameLookUpMode.UserNameAndNickName) {
if(userName == null)
throw new ArgumentNullException(nameof(userName));
if(callback == null)
throw new ArgumentNullException(nameof(callback));
if(string.IsNullOrWhiteSpace(userName)) {
callback(null);
return;
}
Func<ILocalUser, bool> predicate = null;
if((lookUpMode & NameLookUpMode.UserNameAndNickName) == NameLookUpMode.UserNameAndNickName)
predicate = new Func<ILocalUser, bool>(u => userName.Equals(u.UserName, StringComparison.InvariantCultureIgnoreCase) || userName.Equals(u.NickName, StringComparison.InvariantCultureIgnoreCase));
else if((lookUpMode & NameLookUpMode.UserName) == NameLookUpMode.UserName)
predicate = new Func<ILocalUser, bool>(u => userName.Equals(u.UserName, StringComparison.InvariantCultureIgnoreCase));
else if((lookUpMode & NameLookUpMode.NickName) == NameLookUpMode.NickName)
predicate = new Func<ILocalUser, bool>(u => userName.Equals(u.NickName, StringComparison.InvariantCultureIgnoreCase));
if(predicate == null) {
callback(null);
return;
}
GetUser(predicate, callback);
}
public void GetUsers(Action<IEnumerable<ILocalUser>> callback) {
if(callback == null)
throw new ArgumentNullException(nameof(callback));
lock(Sync)
callback(Users);
}
public void GetUsers(Func<ILocalUser, bool> predicate, Action<IEnumerable<ILocalUser>> callback) {
if(predicate == null)
throw new ArgumentNullException(nameof(predicate));
if(callback == null)
throw new ArgumentNullException(nameof(callback));
lock(Sync)
callback(Users.Where(predicate));
}
public void GetUsers(int minRank, Action<IEnumerable<ILocalUser>> callback) {
if(callback == null)
throw new ArgumentNullException(nameof(callback));
GetUsers(u => u.Rank >= minRank, callback);
}
public void GetUsers(IEnumerable<long> ids, Action<IEnumerable<ILocalUser>> callback) {
if(ids == null)
throw new ArgumentNullException(nameof(ids));
if(callback == null)
throw new ArgumentNullException(nameof(callback));
GetUsers(u => ids.Contains(u.UserId), callback);
}
public void Connect(IUserAuthResponse uar, Action<ILocalUser> callback) {
if(uar == null)
throw new ArgumentNullException(nameof(uar));
if(callback == null)
throw new ArgumentNullException(nameof(callback));
lock(Sync) {
GetUser(uar.UserId, user => {
if(user == null)
Create(uar.UserId, uar.UserName, uar.Colour, uar.Rank, uar.Permissions, callback: callback);
else {
Update(user, uar.UserName, uar.Colour, uar.Rank, uar.Permissions);
callback(user);
}
});
}
}
public void Create(
long userId,
string userName,
Colour colour,
int rank,
UserPermissions perms,
UserStatus status = UserStatus.Online,
string statusMessage = null,
string nickName = null,
Action<ILocalUser> callback = null
) {
if(userName == null)
throw new ArgumentNullException(nameof(userName));
lock(Sync) {
User user = new(userId, userName, colour, rank, perms, status, statusMessage, nickName);
Users.Add(user);
Dispatcher.DispatchEvent(this, new UserConnectEvent(user));
callback?.Invoke(user);
}
}
public void Update(
ILocalUser user,
string userName = null,
Colour? colour = null,
int? rank = null,
UserPermissions? perms = null,
UserStatus? status = null,
string statusMessage = null,
string nickName = null
) {
if(user == null)
throw new ArgumentNullException(nameof(user));
lock(Sync) {
if(userName != null && user.UserName == userName)
userName = null;
if(colour.HasValue && user.Colour.Equals(colour))
colour = null;
if(rank.HasValue && user.Rank == rank.Value)
rank = null;
if(nickName != null) {
string prevNickName = user.NickName ?? string.Empty;
if(nickName == prevNickName) {
nickName = null;
} else {
string nextUserName = userName ?? user.UserName;
if(nickName == nextUserName) {
nickName = null;
} else {
// cleanup
}
}
}
if(perms.HasValue && user.Permissions == perms.Value)
perms = null;
if(status.HasValue && user.Status == status.Value)
status = null;
if(statusMessage != null && user.StatusMessage == statusMessage) {
statusMessage = null;
} else {
// cleanup
}
Dispatcher.DispatchEvent(this, new UserUpdateEvent(user, userName, colour, rank, nickName, perms, status, statusMessage));
}
}
public void HandleEvent(object sender, IEvent evt) {
switch(evt) {
case UserConnectEvent uce:
OnConnect(sender, uce);
break;
case UserUpdateEvent uue:
OnUpdate(sender, uue);
break;
case UserDisconnectEvent ude:
OnDisconnect(sender, ude);
break;
}
}
}
}