using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using LinqToDB.Extensions;
using Pf2e.Database.Model.Common.I18n;
namespace Pf2e.WPF.EntityViewer.EntityViewStructure;
///
/// Поле стркутуры с объектом.
///
internal class StructureFieldComplex : StructureField
{
///
public override StructureFieldType FieldType => StructureFieldType.Complex;
///
/// Набор полей объекта.
///
public readonly List Fields = [];
///
/// Тип значения поля.
///
public readonly Type Type;
///
/// Флаг, указывающий, отображается ли поля объекта.
///
public bool Collapsed
{
get => _collapsed;
set
{
_collapsed = value;
UpdateFieldVisibility();
OnPropertyChanged();
}
}
private bool _collapsed;
///
/// Флаг, указывающий, что у этот элемент можно свернуть.
///
public bool Collapsable
{
get => Fields.Count > 0;
}
///
/// Значение поля идентификатора элемента.
///
public string? IdValue { get; private set; }
///
public override bool Visible
{
get => base.Visible;
set
{
base.Visible = value;
UpdateFieldVisibility();
}
}
///
/// Конструктор класса поля структуры с объектом.
///
/// Класс построения структуры данных.
/// Значение поля.
/// Название поля.
/// Глубина поля.
public StructureFieldComplex(StructureBuilder builder, object value, string name, int depth)
: base(builder, name, depth)
{
Type = value.GetType();
SetFields((dynamic)value);
}
///
/// Обновить видимость полей структуры.
///
private void UpdateFieldVisibility()
{
foreach (StructureField field in Fields)
{
field.Visible = Visible && !Collapsed;
}
}
///
/// Установить поля структуры `LocaleString`.
///
/// Объект строки локализации.
private void SetFields(LocaleString localeString)
{
SetFieldsFromRecords(
localeString
.Select(field => new FieldRecord(field.Locale.ToString(), field.Value))
.ToList()
);
}
///
/// Установить поля структуры `IEnumerable`.
///
/// Объект-перечисление.
private void SetFields(IEnumerable enumerable)
{
int i = 0;
List records = [];
foreach (object? item in enumerable)
{
records.Add(new($"[{i++}]", item));
}
SetFieldsFromRecords(records);
}
///
/// Установить поля структуры общего объекта.
///
/// Объект.
private void SetFields(object value)
{
Type type = value.GetType();
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var records = properties
.Where(property => property.GetAttribute() is null)
.Select(property => new FieldRecord(property.Name, property.GetValue(value, null)))
.ToList();
var idRecord = records.FirstOrDefault(record => record.Name == "Id");
if (idRecord is not null && idRecord.Value is not null)
{
records.Remove(idRecord);
IdValue = idRecord.Value.ToString();
}
SetFieldsFromRecords(records);
}
///
/// Установить поля структуры из списка записей.
///
///
private void SetFieldsFromRecords(List records)
{
foreach (
FieldRecord record in records.Where(record =>
record.FieldType == StructureFieldType.Complex
)
)
{
record.Allowed = Builder.RegisterProcessed(record.Value!);
}
foreach (FieldRecord record in records)
{
Fields.Add(
record is { FieldType: StructureFieldType.Complex, Allowed: true }
? Builder.Create(record.Name, record.Value!, Depth + 1)
: Builder.Resolve(record.Name, record.Value!, Depth + 1)
);
}
}
///
/// Запись поля структуры.
///
/// Название поля.
/// Значение поля.
private class FieldRecord(string name, object? value)
{
///
/// Название поля.
///
public readonly string Name = name;
///
/// Значение поля.
///
public readonly object? Value = value;
///
/// Тип поля структуры.
///
public StructureFieldType FieldType => StructureBuilder.GetFieldType(Value);
///
/// Флаг, указывающий, использовать ли поле.
///
public bool Allowed = true;
}
///
public override string GetTypeString()
{
if (Type.IsGenericEnumerableType())
{
Type elementType = Type.GetGenericArguments().Single();
return $"{elementType.Name}[]";
}
else
{
return Type.Name;
}
}
///
public override string GetValueString()
{
if (Type.IsGenericEnumerableType())
{
return Fields.Count.ToString() + (Fields.Count == 1 ? " item" : " items");
}
if (IdValue is not null)
{
return $"#{IdValue}";
}
return "";
}
///
public override ICollection GetRows(Func func)
{
return [.. base.GetRows(func), .. Fields.SelectMany(f => f.GetRows(func)).ToList()];
}
}