using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using JetBrains.Annotations; using TriInspector.Utilities; using UnityEngine; namespace TriInspector { public class TriPropertyDefinition { private readonly Func _valueGetter; [CanBeNull] private readonly Action _valueSetter; private TriPropertyDefinition _arrayElementDefinitionBackingField; private IReadOnlyList _drawersBackingField; private IReadOnlyList _validatorsBackingField; private IReadOnlyList _hideProcessorsBackingField; private IReadOnlyList _disableProcessorsBackingField; internal TriPropertyDefinition(int order, FieldInfo fi) : this(fi, order, fi.Name, fi.FieldType, MakeGetter(fi), MakeSetter(fi), false) { } internal TriPropertyDefinition(int order, PropertyInfo pi) : this(pi, order, pi.Name, pi.PropertyType, MakeGetter(pi), MakeSetter(pi), false) { } internal TriPropertyDefinition(int order, MethodInfo mi) : this(mi, order, mi.Name, typeof(MethodInfo), MakeGetter(mi), MakeSetter(mi), false) { } private TriPropertyDefinition( MemberInfo memberInfo, int order, string fieldName, Type fieldType, Func valueGetter, Action valueSetter, bool isArrayElement) { MemberInfo = memberInfo; Name = fieldName; FieldType = fieldType; IsArrayElement = isArrayElement; _valueGetter = valueGetter; _valueSetter = valueSetter; Attributes = memberInfo.GetCustomAttributes().ToList(); Order = Attributes.TryGet(out PropertyOrderAttribute orderAttribute) ? orderAttribute.Order : order; IsReadOnly = _valueSetter == null || Attributes.TryGet(out ReadOnlyAttribute _); if (TriReflectionUtilities.IsArrayOrList(FieldType, out var elementType)) { IsArray = true; ArrayElementType = elementType; } } public MemberInfo MemberInfo { get; } public Type FieldType { get; } public string Name { get; } public int Order { get; } public IReadOnlyList Attributes { get; } public bool IsReadOnly { get; } public bool IsArrayElement { get; } public Type ArrayElementType { get; } public bool IsArray { get; } public IReadOnlyList HideProcessors { get { if (_hideProcessorsBackingField == null) { _hideProcessorsBackingField = TriDrawersUtilities.CreateHideProcessorsFor(Attributes).ToList(); } return _hideProcessorsBackingField; } } public IReadOnlyList DisableProcessors { get { if (_disableProcessorsBackingField == null) { _disableProcessorsBackingField = TriDrawersUtilities.CreateDisableProcessorsFor(Attributes).ToList(); } return _disableProcessorsBackingField; } } public IReadOnlyList Drawers { get { if (_drawersBackingField == null) { _drawersBackingField = Enumerable.Empty() .Concat(TriDrawersUtilities.CreateValueDrawersFor(FieldType)) .Concat(TriDrawersUtilities.CreateAttributeDrawersFor(Attributes)) .OrderBy(it => it.Order) .ToList(); } return _drawersBackingField; } } public IReadOnlyList Validators { get { if (_validatorsBackingField == null) { _validatorsBackingField = Enumerable.Empty() .Concat(TriDrawersUtilities.CreateValueValidatorsFor(FieldType)) .Concat(TriDrawersUtilities.CreateAttributeValidatorsFor(Attributes)) .ToList(); } return _validatorsBackingField; } } public object GetValue(TriProperty property, int targetIndex) { var parentValue = property.Parent.GetValue(targetIndex); return _valueGetter(property, parentValue); } public bool SetValue(TriProperty property, object value, int targetIndex, out object parentValue) { if (IsReadOnly) { Debug.LogError("Cannot set value for readonly property"); parentValue = default; return false; } parentValue = property.Parent.GetValue(targetIndex); _valueSetter?.Invoke(property, parentValue, value); return true; } public TriPropertyDefinition ArrayElementDefinition { get { if (_arrayElementDefinitionBackingField == null) { if (!IsArray) { throw new InvalidOperationException( $"Cannot get array element definition for non array property: {FieldType}"); } var elementMember = MemberInfo; var elementGetter = new Func((self, obj) => { var list = (IList) obj; return list[self.IndexInArray]; }); var elementSetter = new Action((self, obj, value) => { var list = (IList) obj; list[self.IndexInArray] = value; }); _arrayElementDefinitionBackingField = new TriPropertyDefinition(elementMember, 0, "Element", ArrayElementType, elementGetter, elementSetter, true); } return _arrayElementDefinitionBackingField; } } private static Func MakeGetter(FieldInfo fi) { return (self, obj) => fi.GetValue(obj); } private static Action MakeSetter(FieldInfo fi) { return (self, obj, value) => fi.SetValue(obj, value); } private static Func MakeGetter(PropertyInfo pi) { var method = pi.GetMethod; return (self, obj) => method.Invoke(obj, null); } private static Action MakeSetter(PropertyInfo pi) { var method = pi.SetMethod; if (method == null) { return null; } return (self, obj, value) => method.Invoke(obj, new[] {value,}); } private static Func MakeGetter(MethodInfo mi) { return (self, obj) => mi; } private static Action MakeSetter(MethodInfo mi) { return (self, obj, value) => { }; } } }