using System; using System.Collections.Generic; using System.IO; using System.Linq; using Pf2e.Database.Model.Base.Source; namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers; /// /// Класс коллекции книг, содержащих правила. /// Реализует шаблон Singleton. /// internal class RuleTocCollection { /// /// Список книг, содержащих правила. /// public List Books { get; } = []; /// /// Поиск книги по числовому идентификатору. /// public RuleTocBook this[int id] => Books.FirstOrDefault(i => i.SourceId == id) ?? throw new($"Book with id {id} not found"); /// /// Поиск книги по CLR-представлению источника. /// public RuleTocBook this[Source source] { get { RuleTocBook? result = Books.FirstOrDefault(i => i.SourceId == source.Id); if (result is not null) { return result; } result = new(source); Books.Add(result); return result; } } /// /// Поиск книги по имени. /// public RuleTocBook? FindName(string name) { foreach (RuleTocBook book in Books) { if (book.IsNameExists(name)) { return book; } } return null; } /// /// Поиск правила по идентификатору. /// public RuleTocItem? FindEntityId(int id) { foreach (RuleTocBook book in Books) { RuleTocItem? result = book.GetByEntityId(id); if (result is not null) { return result; } } return null; } /// /// Возвращает строковое представление коллекции в файле YAML. /// public override string ToString() { return string.Join("\n", Books.Select(book => book.ToString()).ToList()); } /// /// Сохраняет коллекцию в файле YAML. /// public void SaveFile() { string fileName = $"rules-toc.{new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds()}.debug.yaml"; using FileStream fileStream = File.Create(fileName); using StreamWriter writer = new(fileStream); writer.Write(ToString()); } /// /// Экземпляр класса . /// public static RuleTocCollection Instance { get; } = new(); } /// /// Класс представляет собой книгу, содержащую правила. /// internal class RuleTocBook(Source source) { /// /// Идентификатор источника. /// public int SourceId { get; } = source.Id; /// /// Названия источника. /// public string SourceName { get; } = source.Name; /// /// Список дочерних правил. /// /// /// Список дочерних элементов. /// public List Children { get; } = []; /// /// Находит элемент по хлебным крошкам. /// /// Массив хлебных крошек, указывающих на путь в дереве. /// или null если элемент не найден. public RuleTocItem Get(string[] breadcrumbs) { RuleTocItem? node = null; while (true) { List children = node?.Children ?? Children; if (breadcrumbs.Length == 0) { return node!; } string root = breadcrumbs[0]; RuleTocItem? item = children.FirstOrDefault(i => i.Name == root); if (item is null) { item = new(root, node); children.Add(item); } breadcrumbs = breadcrumbs[1..]; node = item; } } /// /// Проверяет, существует ли имя в текущей книге или дочерних элементах. /// /// Имя для проверки. /// Начальный элемент для поиска. /// true если имя. public bool IsNameExists(string name, RuleTocItem? node = null) { foreach (RuleTocItem item in node?.Children ?? Children) { if (item.Name == name || IsNameExists(name, item)) { return true; } } return false; } /// /// Находит элемент по идентификатору сущности. /// /// Идентификатор сущности. /// Начальный узел для поиска. /// или null если элемент не найден. public RuleTocItem? GetByEntityId(int id, RuleTocItem? node = null) { if (node?.RuleId == id) { return node; } foreach (RuleTocItem item in node?.Children ?? Children) { RuleTocItem? result = GetByEntityId(id, item); if (result is not null) { return result; } } return null; } /// /// Возвращает строковое представление книги в файле YAML. /// /// Строковое представление книги. public override string ToString() { return $"\"{SourceName}\":\n" + string.Join( "\n", Children.Select(child => RuleTocItem.Indent(child.ToString())).ToList() ); } } /// /// Класс представляет собой элемент дерева правил. /// internal class RuleTocItem(string name, RuleTocItem? parent) { public string? Id { get; set; } = null; /// /// Имя элемента. /// public string Name { get; } = name; /// /// Идентификатор правила. /// public int? RuleId { get; set; } = null; /// /// Указывает, что элемент обрабатывается. /// public bool IsHandled { get; set; } = false; /// /// Родительский элемент дерева. /// public RuleTocItem? Parent { get; } = parent; /// /// Порядок дочерних элементов. /// public List? ChildrenOrder { get; set; } = null; /// /// Коллекция дочерних элементов. /// public List Children { get; } = []; /// /// Предыдущий идентификатор. /// public string? PreviousId { get; set; } /// /// Следующий идентификатор. /// public string? NextId { get; set; } /// /// Идентификатор первого дочернего элемента. /// public string? FirstChildId { get; set; } /// /// Идентификатор последнего дочернего элемента. /// public string? LastChildId { get; set; } /// /// Возвращает строковое представление элемента содержания в файле YAML. /// /// Строковое представление элемента содержания. public override string ToString() { List rows = [ $"{Name}:", $"{Tab}aonprd: {Id ?? "null"}", $"{Tab}entity: {RuleId?.ToString() ?? "null"}", ]; if (Children.Count == 0) { // rows.Add($"{TAB}children: None"); return string.Join("\n", rows); } rows.Add($"{Tab}children:"); foreach (RuleTocItem child in Children) { rows.Add(child.Indent(2)); } return string.Join("\n", rows); } /// /// Увеличение отступа строкового представления элемента содержания в файле YAML. /// /// Количество отступов. /// Строковое представление элемента содержания с отступом. /// public string Indent(int amount = 1) { string result = ToString(); for (int i = 0; i < amount; i++) { result = Indent(result); } return result; } /// /// Увеличение отступа строки. /// /// Входная строка. /// Строка с отступом. /// public static string Indent(string str) { return Tab + str.Replace("\n", $"\n{Tab}"); } /// /// Строка отступа строки. /// /// /// public const string Tab = " "; }