using Microsoft.EntityFrameworkCore;
using Pf2e.Database;
using Pf2e.Database.Model;
using Pf2e.Database.Model.Base;
using Pf2e.Database.Model.Enum;
using Pf2e.Database.Model.Structure;
namespace Pf2e.Common;
///
/// Класс, обрабатывающий запросы к сущностям и подключающий необходимые данные.
///
public class FullEntity
{
///
/// Корневая сущность.
///
public Entity Entity { get; }
///
/// Основная сущность.
///
public IEntity? SubEntity { get; }
///
/// Контекст базы данных, используемый для получения сущностей.
///
private Pf2eDbContext Context { get; }
///
/// Конструктор класса, который получает сущность из базы данных по типу и идентификатору и заполняет её данными.
///
/// Контекст базы данных.
/// Тип запрашиваемой сущности.
/// Идентификатор запрашиваемой сущности.
public FullEntity(Pf2eDbContext context, EntityType type, int id)
{
Context = context;
Entity = GetEntity(type, id);
SubEntity = GetSubEntity(type, id);
}
///
/// Конструктор класса, который получает сущность из базы данных по переданной сущности и заполняет её данными.
///
/// Контекст базы данных.
/// Сущность, которую нужно обработать.
public FullEntity(Pf2eDbContext context, Entity entity)
{
Context = context;
Entity = entity;
SubEntity = GetSubEntity(entity.Type, entity.Id);
}
///
/// Получает сущность из базы данных по типу и идентификатору.
///
/// Тип сущности.
/// Идентификатор сущности.
/// Сущность, соответствующая указанному типу и идентификатору.
private Entity GetEntity(EntityType type, int id) =>
Context
.Entities.Where(e => e.Id == id && e.Type == type)
.Include(e => e.Sources)
.ThenInclude(s => s.Source)
.Include(e => e.Keywords)
.Include(e => e.Externals)
.ThenInclude(x => x.Target)
.Include(e => e.Traits)
.ThenInclude(t => t.Entity)
.FirstOrDefault() ?? throw new("Entity is null");
///
/// Получает подчинённую сущность по типу и идентификатору.
///
/// Тип сущности.
/// Идентификатор сущности.
/// Подчинённая сущность, соответствующая указанному типу и идентификатору.
private IEntity? GetSubEntity(EntityType type, int id)
{
Getter? getter = _getters.FirstOrDefault(g => g.Type == type);
return (getter != null)
? getter.Get(id, this.Context)
: (IEntity?)Context.Find(EntityOfTypeAttribute.Resolve(Entity), id);
}
///
/// Словарь, содержащий методы получения сущностей из базы данных.
///
private readonly ICollection _getters =
[
new Getter(
EntityType.Ancestry,
context => context.Ancestries.Include(a => a.AncestryBoosts)
),
new Getter(
EntityType.Archetype,
context =>
context
.Archetypes.Include(a => a.DedicationFeat)
.ThenInclude(f => f.Entity)
.Include(a => a.Feats)
.ThenInclude(f => f.Entity)
),
new Getter(
EntityType.Background,
context =>
context
.Backgrounds.Include(b => b.BackgroundBoosts)
.Include(b => b.BackgroundSkills)
.ThenInclude(t => t.Training)
.Include(b => b.BackgroundItems)
),
new Getter(
EntityType.Class,
context =>
context
.Classes.Include(c => c.ClassKeyAbilities)
.Include(c => c.ClassTrainings)
.ThenInclude(t => t.Training)
.Include(c => c.ClassProgressions)
.Include(c => c.SubclassGroups)
),
new Getter(
EntityType.Feat,
context =>
context.Feats.Include(f => f.Archetypes).ThenInclude(a => a.Entity).AsQueryable()
),
new Getter(
EntityType.Spell,
context => context.Spells.Include(s => s.SpellAreas).Include(s => s.SpellDefenses)
),
new Getter(
EntityType.Ritual,
context =>
context
.Rituals.Include(r => r.Area)
.Include(r => r.Checks)
.ThenInclude(c => c.Training)
.AsQueryable()
),
new Getter(
EntityType.Subclass,
context =>
context
.Subclasses.Include(s => s.Trainings)
.ThenInclude(t => t.Training)
.Include(s => s.Entities)
),
];
///
/// Абстрактный класс, который определяет метод получения сущности из базы данных.
///
/// Связанный тип сущности.
private abstract class Getter(EntityType type)
{
public readonly EntityType Type = type;
public abstract IEntity? Get(int id, Pf2eDbContext context);
}
///
/// Класс, определяющий метод получения сущности из базы данных.
///
/// Тип данных основной сущности.
/// Связанный тип сущности.
/// Метод получения сущности из базы данных.
private class Getter(EntityType type, Func> method)
: Getter(type)
where T : IEntity
{
public override IEntity? Get(int id, Pf2eDbContext context)
{
return method.Invoke(context).FirstOrDefault(c => c.Id == id);
}
}
}