public static RuntimePrefabPropertyOverride Create <TValue>(string propertyPath, string transformPath,
                                                                    int componentIndex, TValue value, SerializationMetadata metadata = null)
        {
            switch (value)
            {
            case Vector2 vector2:
            {
                var @override = new RuntimePrefabPropertyOverrideList(propertyPath, transformPath, componentIndex);
                var list      = @override.List;
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.x", transformPath, componentIndex, vector2.x));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.y", transformPath, componentIndex, vector2.y));
                return(@override);
            }

            case Vector3 vector3:
            {
                var @override = new RuntimePrefabPropertyOverrideList(propertyPath, transformPath, componentIndex);
                var list      = @override.List;
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.x", transformPath, componentIndex, vector3.x));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.y", transformPath, componentIndex, vector3.y));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.z", transformPath, componentIndex, vector3.z));
                return(@override);
            }

            case Vector4 vector4:
            {
                var @override = new RuntimePrefabPropertyOverrideList(propertyPath, transformPath, componentIndex);
                var list      = @override.List;
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.x", transformPath, componentIndex, vector4.x));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.y", transformPath, componentIndex, vector4.y));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.z", transformPath, componentIndex, vector4.z));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.w", transformPath, componentIndex, vector4.w));
                return(@override);
            }

            case Quaternion quaternion:
            {
                var @override = new RuntimePrefabPropertyOverrideList(propertyPath, transformPath, componentIndex);
                var list      = @override.List;
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.x", transformPath, componentIndex, quaternion.x));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.y", transformPath, componentIndex, quaternion.y));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.z", transformPath, componentIndex, quaternion.z));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.w", transformPath, componentIndex, quaternion.w));
                return(@override);
            }

            case Color color:
            {
                var @override = new RuntimePrefabPropertyOverrideList(propertyPath, transformPath, componentIndex);
                var list      = @override.List;
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.r", transformPath, componentIndex, color.r));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.g", transformPath, componentIndex, color.g));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.b", transformPath, componentIndex, color.b));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.a", transformPath, componentIndex, color.a));
                return(@override);
            }

            case Color32 color:
            {
                var @override = new RuntimePrefabPropertyOverrideList(propertyPath, transformPath, componentIndex);
                var list      = @override.List;
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.r", transformPath, componentIndex, color.r));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.g", transformPath, componentIndex, color.g));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.b", transformPath, componentIndex, color.b));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.a", transformPath, componentIndex, color.a));
                return(@override);
            }

            case string @string:
            {
                return(new RuntimePrefabOverrideString(propertyPath, transformPath, componentIndex, @string));
            }

            case char @char:
            {
                return(new RuntimePrefabOverrideChar(propertyPath, transformPath, componentIndex, @char));
            }

            case bool @bool:
            {
                return(new RuntimePrefabOverrideBool(propertyPath, transformPath, componentIndex, @bool));
            }

            case int @int:
            {
                return(new RuntimePrefabOverrideInt(propertyPath, transformPath, componentIndex, @int));
            }

            case long @long:
            {
                return(new RuntimePrefabOverrideLong(propertyPath, transformPath, componentIndex, @long));
            }

            case float @float:
            {
                return(new RuntimePrefabOverrideFloat(propertyPath, transformPath, componentIndex, @float));
            }

            case UnityObject unityObject:
            {
                return(new RuntimePrefabOverrideUnityObjectReference(propertyPath, transformPath, componentIndex,
                                                                     UnityObjectReference.GetReferenceForObject(unityObject, metadata)));
            }

            default:
            {
                // TODO: Handle null values better
                if (typeof(TValue) == typeof(UnityObject) && value == null)
                {
                    return(new RuntimePrefabOverrideUnityObjectReference(propertyPath, transformPath, componentIndex,
                                                                         UnityObjectReference.NullObjectReference));
                }

                throw new NotImplementedException();
            }
            }
        }
        public static void Update <TValue>(RuntimePrefabPropertyOverride @override, TValue value, SerializationMetadata metadata = null)
        {
            var propertyPath   = @override.m_PropertyPath;
            var transformPath  = @override.m_TransformPath;
            var componentIndex = @override.m_ComponentIndex;

            switch (value)
            {
            case Vector2 vector2:
            {
                var list = ((RuntimePrefabPropertyOverrideList)@override).List;
                list.Clear();
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.x", transformPath, componentIndex, vector2.x));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.y", transformPath, componentIndex, vector2.y));
                break;
            }

            case Vector3 vector3:
            {
                var list = ((RuntimePrefabPropertyOverrideList)@override).List;
                list.Clear();
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.x", transformPath, componentIndex, vector3.x));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.y", transformPath, componentIndex, vector3.y));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.z", transformPath, componentIndex, vector3.z));
                break;
            }

            case Vector4 vector4:
            {
                var list = ((RuntimePrefabPropertyOverrideList)@override).List;
                list.Clear();
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.x", transformPath, componentIndex, vector4.x));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.y", transformPath, componentIndex, vector4.y));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.z", transformPath, componentIndex, vector4.z));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.w", transformPath, componentIndex, vector4.w));
                break;
            }

            case Quaternion quaternion:
            {
                var list = ((RuntimePrefabPropertyOverrideList)@override).List;
                list.Clear();
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.x", transformPath, componentIndex, quaternion.x));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.y", transformPath, componentIndex, quaternion.y));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.z", transformPath, componentIndex, quaternion.z));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.w", transformPath, componentIndex, quaternion.w));
                break;
            }

            case Color color:
            {
                var list = ((RuntimePrefabPropertyOverrideList)@override).List;
                list.Clear();
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.r", transformPath, componentIndex, color.r));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.g", transformPath, componentIndex, color.g));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.b", transformPath, componentIndex, color.b));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.a", transformPath, componentIndex, color.a));
                break;
            }

            case Color32 color:
            {
                var list = ((RuntimePrefabPropertyOverrideList)@override).List;
                list.Clear();
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.r", transformPath, componentIndex, color.r));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.g", transformPath, componentIndex, color.g));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.b", transformPath, componentIndex, color.b));
                list.Add(new RuntimePrefabOverrideFloat($"{propertyPath}.a", transformPath, componentIndex, color.a));
                break;
            }

            case int @int:
            {
                ((RuntimePrefabOverrideInt)@override).Value = @int;
                break;
            }

            case long @long:
            {
                ((RuntimePrefabOverrideLong)@override).Value = @long;
                break;
            }

            case float @float:
            {
                ((RuntimePrefabOverrideFloat)@override).Value = @float;
                break;
            }

            case UnityObject unityObject:
            {
                ((RuntimePrefabOverrideUnityObjectReference)@override).Value = UnityObjectReference.GetReferenceForObject(unityObject, metadata);
                break;
            }

            default:
            {
                // TODO: Handle null values better
                if (typeof(TValue) == typeof(UnityObject) && value == null)
                {
                    ((RuntimePrefabOverrideUnityObjectReference)@override).Value = UnityObjectReference.NullObjectReference;
                    break;
                }

                ((RuntimePrefabPropertyOverride <TValue>)@override).Value = value;
                break;
            }
            }
        }
        static RuntimePrefabPropertyOverride Create <TContainer>(SerializedProperty property, string propertyPath,
                                                                 string transformPath, int componentIndex, ref bool hasNext, out bool shouldIterate, SerializationMetadata metadata)
            where TContainer : UnityObject
        {
            shouldIterate = true;
            switch (property.propertyType)
            {
            case SerializedPropertyType.Bounds:
            case SerializedPropertyType.Quaternion:
            case SerializedPropertyType.Vector2:
            case SerializedPropertyType.Vector3:
            case SerializedPropertyType.Vector4:
            case SerializedPropertyType.Rect:
            case SerializedPropertyType.Generic:
            case SerializedPropertyType.Gradient:
            case SerializedPropertyType.Vector2Int:
            case SerializedPropertyType.Vector3Int:
            case SerializedPropertyType.RectInt:
            case SerializedPropertyType.BoundsInt:
            case SerializedPropertyType.Color:
                var listOverride = CreateMultiple <TContainer>(property, propertyPath, transformPath,
                                                               componentIndex, ref hasNext, ref shouldIterate, metadata);

                shouldIterate = false;
                return(listOverride);

            case SerializedPropertyType.Integer:
                return(new RuntimePrefabOverrideLong(propertyPath, transformPath, componentIndex, property.longValue));

            case SerializedPropertyType.Boolean:
                return(new RuntimePrefabOverrideBool(propertyPath, transformPath, componentIndex, property.boolValue));

            case SerializedPropertyType.Float:
                // TODO: detect double precision
                return(new RuntimePrefabOverrideFloat(propertyPath, transformPath, componentIndex, property.floatValue));

            case SerializedPropertyType.String:
                return(new RuntimePrefabOverrideString(propertyPath, transformPath, componentIndex, property.stringValue));

            case SerializedPropertyType.ObjectReference:
                var objectReference = UnityObjectReference.GetReferenceForObject(property.objectReferenceValue, metadata);
                return(new RuntimePrefabOverrideUnityObjectReference(propertyPath, transformPath, componentIndex, objectReference));

            case SerializedPropertyType.LayerMask:
                return(new RuntimePrefabOverrideInt(propertyPath, transformPath, componentIndex, property.intValue));

            case SerializedPropertyType.Enum:
                return(new RuntimePrefabOverrideLong(propertyPath, transformPath, componentIndex, property.enumValueIndex));

            case SerializedPropertyType.ArraySize:
                return(new RuntimePrefabOverrideInt(propertyPath, transformPath, componentIndex, property.intValue));

            case SerializedPropertyType.Character:
                return(new RuntimePrefabOverrideChar(propertyPath, transformPath, componentIndex, (char)property.intValue));

            case SerializedPropertyType.AnimationCurve:
                return(new RuntimePrefabOverrideAnimationCurve(propertyPath, transformPath, componentIndex, property.animationCurveValue));

            case SerializedPropertyType.ExposedReference:
                var exposedReference = UnityObjectReference.GetReferenceForObject(property.exposedReferenceValue, metadata);
                return(new RuntimePrefabOverrideUnityObjectReference(propertyPath, transformPath, componentIndex, exposedReference));

            case SerializedPropertyType.FixedBufferSize:
                return(new RuntimePrefabOverrideInt(propertyPath, transformPath, componentIndex, property.fixedBufferSize));

            case SerializedPropertyType.ManagedReference:
                Debug.LogWarning($"Encountered managed reference property override override for {propertyPath}");
                return(null);

            default:
                Debug.LogWarning($"Unknown property type in prefab override for {propertyPath}");
                return(null);
            }
        }