using System;
using System.Linq.Expressions;
using UnityEditor;
namespace Unity.Cinemachine.Editor
{
    /// 
    /// Helpers for the editor relating to SerializedProperties
    /// 
    static class SerializedPropertyHelper
    {
        /// 
        /// This is a way to get a field name string in such a manner that the compiler will
        /// generate errors for invalid fields.  Much better than directly using strings.
        /// Usage: instead of
        /// 
        /// "m_MyField";
        /// 
        /// do this:
        /// 
        /// MyClass myclass = null;
        /// SerializedPropertyHelper.PropertyName( () => myClass.m_MyField);
        /// 
        /// 
        /// Magic expression that resolves to a field: () => myClass.m_MyField
        /// 
        public static string PropertyName(Expression> exp)
        {
            if (exp.Body is not MemberExpression body)
            {
                var ubody = (UnaryExpression)exp.Body;
                body = ubody.Operand as MemberExpression;
            }
            return body.Member.Name;
        }
        /// 
        /// A compiler-assisted (non-string-based) way to call SerializedProperty.FindProperty
        /// 
        /// The serialized object to search
        /// Magic expression that resolves to a field: () => myClass.m_MyField
        /// The resulting SerializedProperty, or null
        public static SerializedProperty FindProperty(this SerializedObject obj, Expression> exp)
        {
            return obj.FindProperty(PropertyName(exp));
        }
        /// 
        /// A compiler-assisted (non-string-based) way to call SerializedProperty.FindPropertyRelative
        /// 
        /// The serialized object to search
        /// Magic expression that resolves to a field: () => myClass.m_MyField
        /// The resulting SerializedProperty, or null
        public static SerializedProperty FindPropertyRelative(this SerializedProperty obj, Expression> exp)
        {
            return obj.FindPropertyRelative(PropertyName(exp));
        }
        /// Get the value of a property, as an object
        /// The property to query
        /// The object value of the property
        public static object GetPropertyValue(SerializedProperty property)
        {
            var targetObject = property.serializedObject.targetObject;
            var field = targetObject.GetType().GetField(property.propertyPath);
            if (field != null)
                return field.GetValue(targetObject);
            var paths = property.propertyPath.Split('.');
            if (paths.Length > 1)
            {
                var fieldOwner = ReflectionHelpers.GetParentObject(property.propertyPath, targetObject);
                field = fieldOwner?.GetType().GetField(paths[paths.Length-1]);
                if (field != null)
                    return field.GetValue(fieldOwner);
            }
            return null;
        }
    }
}