using Discord.WebSocket;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Kerobot.Common
{
///
/// Represents a commonly-used configuration structure: an array of strings consisting of values.
///
public class EntityList : IEnumerable
{
private readonly IReadOnlyCollection _innerList;
/// Gets an enumerable collection of all role names defined in this list.
public IEnumerable Roles
=> _innerList.Where(n => n.Type == EntityType.Role);
/// Gets an enumerable collection of all channel names defined in this list.
public IEnumerable Channels
=> _innerList.Where(n => n.Type == EntityType.Channel);
/// Gets an enumerable collection of all user names defined in this list.
public IEnumerable Users
=> _innerList.Where(n => n.Type == EntityType.User);
///
/// Creates a new EntityList instance with no data.
///
public EntityList() : this(null, false) { }
///
/// Creates a new EntityList instance using the given JSON token as input.
///
/// JSON array to be used for input. For ease of use, null values are also accepted.
/// Specifies if all entities defined in configuration must have their type specified.
/// The input is not a JSON array.
///
/// Unintiutively, this exception is thrown if a user-provided configuration value is blank.
///
///
/// When enforceTypes is set, this is thrown if an EntityName results in having its Type be Unspecified.
///
public EntityList(JToken input, bool enforceTypes)
{
if (input == null)
{
_innerList = new List().AsReadOnly();
return;
}
if (input.Type != JTokenType.Array)
throw new ArgumentException("JToken input must be a JSON array.");
var inputArray = (JArray)input;
var list = new List();
foreach (var item in inputArray.Values())
{
var itemName = new EntityName(item);
if (enforceTypes && itemName.Type == EntityType.Unspecified)
throw new FormatException($"The following value is not prefixed: {item}");
list.Add(itemName);
}
_innerList = list.AsReadOnly();
}
#region Entity matching
///
/// Checks if the parameters of the given matches with
/// any entity specified in this list.
///
///
/// Specifies if EntityName instances within this list should have their internal ID value
/// updated if found during the matching process.
///
///
/// True if the message author exists in this list, or if the message's channel exists in this list,
/// or if the message author contains a role that exists in this list.
///
public bool IsListMatch(SocketMessage msg, bool keepId)
{
var author = msg.Author as SocketGuildUser;
var authorRoles = author.Roles;
var channel = msg.Channel;
foreach (var entry in this)
{
if (entry.Type == EntityType.Role)
{
if (entry.Id.HasValue)
{
return authorRoles.Any(r => r.Id == entry.Id.Value);
}
else
{
foreach (var r in authorRoles)
{
if (!string.Equals(r.Name, entry.Name, StringComparison.OrdinalIgnoreCase)) break;
if (keepId) entry.SetId(r.Id);
return true;
}
}
}
else if (entry.Type == EntityType.Channel)
{
if (entry.Id.HasValue)
{
return entry.Id.Value == channel.Id;
}
else
{
if (!string.Equals(channel.Name, entry.Name, StringComparison.OrdinalIgnoreCase)) break;
if (keepId) entry.SetId(channel.Id);
return true;
}
}
else // User
{
if (entry.Id.HasValue)
{
return entry.Id.Value == author.Id;
}
else
{
if (!string.Equals(author.Username, entry.Name, StringComparison.OrdinalIgnoreCase)) break;
if (keepId) entry.SetId(author.Id);
return true;
}
}
}
return false;
}
#endregion
public override string ToString() => $"Entity list contains {_innerList.Count} item(s).";
public IEnumerator GetEnumerator() => _innerList.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}