using System.Collections;
using System.Reflection;
using LinqToDB.Extensions;
using Pf2eModel.Model.Common.I18n;
namespace Pf2eViewer.EntityTreeViewer;
///
/// Класс для построения структуры данных класса.
///
internal class StructureBuilder
{
///
/// Список обработанных объектов.
///
private readonly List _processedObjects = [];
///
/// Создание поля структуры.
///
/// Название поля.
/// Значение поля.
/// Глубина, на котором поле отображается.
/// Поле структуры.
public StructureFieldComplex Create(string name, object value, int depth = 0)
{
_ = RegisterProcessed(value);
StructureFieldComplex field = new(this, value, name, depth);
CompleteProcessed(value, field);
return field;
}
///
/// Регистрация обработанного объекта.
///
/// Регистрируемый объект.
/// `true`, если объект был добавлен; `false`, если объект был добавлен ранее.
public bool RegisterProcessed(object value)
{
if (_processedObjects.Any(p => p.Value == value))
{
return false;
}
StructureProcessedObject processed = new(value);
_processedObjects.Add(processed);
return true;
}
///
/// Дополнить обработанный объект.
///
/// Значение.
/// Поле объекта.
public void CompleteProcessed(object value, StructureFieldComplex field)
{
StructureProcessedObject? q = _processedObjects.FirstOrDefault(p => p.Value == value);
if (q == null)
{
return;
}
q.Field = field;
}
///
/// Выбор поля структуры по значению.
///
/// Название поля.
/// Значение поля.
/// Глубина, на котором поле отображается.
///
public StructureField Resolve(string name, object? value, int depth)
{
switch (GetFieldType(value))
{
case StructureFieldType.Null:
return new StructureFieldNull(this, name, depth);
case StructureFieldType.Primitive:
return new StructureFieldPrimitive(this, value!, name, depth);
case StructureFieldType.Enum:
return new StructureFieldEnum(this, value!, name, depth);
case StructureFieldType.Reference:
break;
case StructureFieldType.Complex:
default:
StructureProcessedObject? processed = _processedObjects.FirstOrDefault(p =>
p.Value == value
);
if (processed is null)
{
return Create(name, value!, depth);
}
StructureFieldReference reference = new StructureFieldReference(
this,
name,
null,
depth
);
processed.AddReference(reference);
return reference;
}
throw new("Impossible");
}
///
/// Получить тип поля структуры по значению.
///
/// Значение поля.
/// Тип поля структуры.
public static StructureFieldType GetFieldType(object? value)
{
if (value is null)
{
return StructureFieldType.Null;
}
Type type = value.GetType();
if (type.IsEnum)
{
return StructureFieldType.Enum;
}
if (type.IsPrimitive || type == typeof(string))
{
return StructureFieldType.Primitive;
}
return StructureFieldType.Complex;
}
}
///
/// Класс для хранения обработанного объекта.
///
/// Объект.
internal class StructureProcessedObject(object value)
{
public readonly object Value = value;
private StructureFieldComplex? _field = null;
public StructureFieldComplex? Field
{
get => _field;
set
{
_field = value;
foreach (StructureFieldReference reference in _references)
{
reference.Reference = Field;
}
}
}
private readonly List _references = [];
public void AddReference(StructureFieldReference reference)
{
_references.Add(reference);
if (_field is not null)
{
reference.Reference = Field;
}
}
}
///
/// Тип поля структуры.
///
internal enum StructureFieldType
{
///
/// Пустое поле.
///
Null,
///
/// Простое поле.
///
Primitive,
///
/// Поле с перечислением.
///
Enum,
///
/// Поле с объектом.
///
Complex,
///
/// Поле с ссылкой на объект.
///
Reference,
}
///
/// Тип лестницы глубины.
///
internal enum LadderType
{
///
/// Пустой элемент: [ ].
///
Empty,
///
/// Последний элемент: [└].
///
Last,
///
/// Проходной элемент: [├] или [│].
///
Through,
}
///
/// Базовый класс для полей структуры.
///
/// Класс построения структуры данных.
/// Название поля.
/// Глубина поля.
internal abstract class StructureField(StructureBuilder builder, string name, int depth)
{
///
/// Название поля.
///
public readonly string Name = name;
///
/// Тип поля структуры.
///
public abstract StructureFieldType FieldType { get; }
///
/// Класс построения структуры данных.
///
public readonly StructureBuilder Builder = builder;
///
/// Флаг, указывающий, отображается ли поле.
///
public virtual bool Visible { get; set; } = true;
///
/// Глубина поля структуры.
///
public readonly int Depth = depth;
///
/// Получить строку, представляющую тип поля.
///
/// Строка, представляющая тип поля.
public abstract string GetTypeString();
///
/// Получить строку, представляющую значение поля.
///
/// Строка, представляющая значение поля.
public abstract string GetValueString();
///
/// Получить набор строк, используя результат функция.
///
/// Тип результирующей строки.
/// Функция, оборажения поля в результирующую строку.
/// Коллекция, содержащая набор строк.
public virtual ICollection GetRows(Func func)
{
StructureField[] q = [this];
return q.Select(func).ToList();
}
}
///
/// Пустое поле структуры.
///
/// Класс построения структуры данных.
/// Название поля.
/// Глубина поля.
internal class StructureFieldNull(StructureBuilder builder, string name, int depth)
: StructureField(builder, name, depth)
{
///
public override StructureFieldType FieldType => StructureFieldType.Null;
///
public override string GetTypeString() => "null";
///
public override string GetValueString() => "";
}
///
/// Простое поле структуры.
///
/// Класс построения структуры данных.
/// Значение поля.
/// Название поля.
/// Глубина поля.
internal class StructureFieldPrimitive(
StructureBuilder builder,
object value,
string name,
int depth
) : StructureField(builder, name, depth)
{
///
public override StructureFieldType FieldType => StructureFieldType.Primitive;
///
/// Значение поля.
///
public readonly object Value = value;
///
/// Тип значения поля.
///
public readonly Type Type = value.GetType();
///
public override string GetTypeString()
{
return Type.GetTypeCode(Type) switch
{
TypeCode.Boolean => "bool",
TypeCode.Int32 => "int",
TypeCode.String => "string",
TypeCode.Double => "double",
_ => Type.Name,
};
}
///
public override string GetValueString()
{
if (Type == typeof(string))
{
return "\"" + ((string)Value).Replace("\"", "\\\"") + "\"";
}
return Value.ToString() ?? string.Empty;
}
}
///
/// Поле структуры с перечислением.
///
/// Класс построения структуры данных.
/// Значение поля.
/// Название поля.
/// Глубина поля.
internal class StructureFieldEnum(StructureBuilder builder, object value, string name, int depth)
: StructureField(builder, name, depth)
{
///
public override StructureFieldType FieldType => StructureFieldType.Enum;
///
/// Значение поля.
///
public readonly object Value = value;
///
/// Тип значения поля.
///
public readonly Type Type = value.GetType();
///
public override string GetTypeString() => "enum";
///
public override string GetValueString() => $"{Type.Name}.{Value}";
}
///
/// Поле стркутуры с объектом.
///
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();
}
}
private bool _collapsed = false;
///
public override bool Visible
{
get => _visible;
set
{
_visible = value;
UpdateFieldVisibility();
}
}
private bool _visible = true;
///
/// Конструктор класса поля структуры с объектом.
///
/// Класс построения структуры данных.
/// Значение поля.
/// Название поля.
/// Глубина поля.
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);
SetFieldsFromRecords(
properties
.Where(property =>
property.GetAttribute() is null
)
.Select(property => new FieldRecord(property.Name, property.GetValue(value, null)))
.ToList()
);
}
///
/// Установить поля структуры из списка записей.
///
///
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");
}
return "";
}
///
public override ICollection GetRows(Func func)
{
return [.. base.GetRows(func), .. Fields.SelectMany(f => f.GetRows(func)).ToList()];
}
}
///
/// Поле структуры с ссылкой на объект.
///
/// Класс построения структуры данных.
/// Название поля.
/// Ссылка на объект.
/// Глубина поля.
internal class StructureFieldReference(
StructureBuilder builder,
string name,
StructureFieldComplex? reference,
int depth
) : StructureField(builder, name, depth)
{
///
/// Ссылка на объект.
///
public StructureFieldComplex? Reference = reference;
///
public override StructureFieldType FieldType => StructureFieldType.Reference;
///
public override string GetTypeString() => Reference?.GetTypeString() ?? "???";
///
public override string GetValueString() => "";
}