/// <summary>
        /// Renders properties of <see cref="IMixedRealityServiceConfiguration"/> instance at provided index in inspector.
        /// Also renders inspector view of data provider's profile object and its contents if applicable and foldout is expanded.
        /// </summary>
        private bool RenderDataProviderEntry(int index, GUIContent removeContent, SystemType serviceType, Type dataProviderProfileType = null)
        {
            bool changed = false;
            SerializedProperty             provider           = providerConfigurations.GetArrayElementAtIndex(index);
            ServiceConfigurationProperties providerProperties = GetDataProviderConfigurationProperties(provider);

            // Don't hide new data providers added via the UI, otherwise there's no easy way to change their type
            if (serviceType?.Type == null && !MixedRealityProjectPreferences.ShowNullDataProviders && !providerProperties.componentName.stringValue.StartsWith(NewDataProvider))
            {
                return(false);
            }

            using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
            {
                using (new EditorGUILayout.HorizontalScope())
                {
                    if (index < 0 || index >= providerFoldouts.Count)
                    {
                        index = 0;
                    }
                    providerFoldouts[index] = EditorGUILayout.Foldout(providerFoldouts[index], providerProperties.componentName.stringValue, true);

                    if (GUILayout.Button(removeContent, EditorStyles.miniButtonRight, GUILayout.Width(24f)))
                    {
                        RemoveDataProvider(index);
                        return(true);
                    }
                }

                if (providerFoldouts[index])
                {
                    using (var c = new EditorGUI.ChangeCheckScope())
                    {
                        EditorGUILayout.PropertyField(providerProperties.componentType, ComponentTypeLabel);
                        if (c.changed)
                        {
                            serializedObject.ApplyModifiedProperties();
                            ApplyProviderConfiguration(serviceType.Type, providerProperties);
                            return(true);
                        }

                        EditorGUILayout.PropertyField(providerProperties.runtimePlatform, SupportedPlatformsLabel);
                        changed = c.changed;
                    }

                    changed |= RenderProfile(providerProperties.providerProfile, dataProviderProfileType, true, false, serviceType);

                    serializedObject.ApplyModifiedProperties();
                }

                if (IsProfileRequired(serviceType) &&
                    (providerProperties.providerProfile.objectReferenceValue == null))
                {
                    EditorGUILayout.HelpBox($"{providerProperties.componentName.stringValue} requires a profile.", MessageType.Warning);
                }
            }

            return(changed);
        }
        /// <summary>
        /// Renders properties of <see cref="IMixedRealityServiceConfiguration"/> instance at provided index in inspector.
        /// Also renders inspector view of data provider's profile object and its contents if applicable and foldout is expanded.
        /// </summary>
        protected bool RenderDataProviderEntry(int index, GUIContent removeContent, System.Type dataProviderProfileType = null)
        {
            bool changed = false;
            SerializedProperty             provider           = providerConfigurations.GetArrayElementAtIndex(index);
            ServiceConfigurationProperties providerProperties = GetDataProviderConfigurationProperties(provider);

            var serviceType = GetDataProviderConfiguration(index).ComponentType;

            using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
            {
                using (new EditorGUILayout.HorizontalScope())
                {
                    if (index < 0 || index >= providerFoldouts.Count)
                    {
                        index = 0;
                    }
                    providerFoldouts[index] = EditorGUILayout.Foldout(providerFoldouts[index], providerProperties.componentName.stringValue, true);

                    if (GUILayout.Button(removeContent, EditorStyles.miniButtonRight, GUILayout.Width(24f)))
                    {
                        RemoveDataProvider(index);
                        return(true);
                    }
                }

                if (providerFoldouts[index])
                {
                    using (var c = new EditorGUI.ChangeCheckScope())
                    {
                        EditorGUILayout.PropertyField(providerProperties.componentType, ComponentTypeLabel);
                        if (c.changed)
                        {
                            serializedObject.ApplyModifiedProperties();
                            ApplyProviderConfiguration(serviceType.Type, providerProperties);
                            return(true);
                        }

                        EditorGUILayout.PropertyField(providerProperties.runtimePlatform, SupportedPlatformsLabel);
                        changed = c.changed;
                    }

                    changed |= RenderProfile(providerProperties.providerProfile, dataProviderProfileType, true, false, serviceType);

                    serializedObject.ApplyModifiedProperties();
                }

                if (IsProfileRequired(serviceType) &&
                    (providerProperties.providerProfile.objectReferenceValue == null))
                {
                    EditorGUILayout.HelpBox($"{providerProperties.componentName.stringValue} requires a profile.", MessageType.Warning);
                }
            }

            return(changed);
        }
        /// <summary>
        /// Applies the given concrete dataprovider type properties to the provided <see cref="IMixedRealityServiceConfiguration"/> instance (as represented by <see cref="ServiceConfigurationProperties"/>).
        /// Requires <see cref="MixedRealityDataProviderAttribute"/> on concrete type class to pull initial values
        /// that will be applied to the <see cref="ServiceConfigurationProperties"/> container SerializedProperties
        /// </summary>
        protected virtual void ApplyProviderConfiguration(Type dataProviderType, ServiceConfigurationProperties providerProperties)
        {
            if (dataProviderType != null)
            {
                if (MixedRealityDataProviderAttribute.Find(dataProviderType) is MixedRealityDataProviderAttribute providerAttribute)
                {
                    providerProperties.componentName.stringValue            = !string.IsNullOrWhiteSpace(providerAttribute.Name) ? providerAttribute.Name : dataProviderType.Name;
                    providerProperties.providerProfile.objectReferenceValue = providerAttribute.DefaultProfile;
                    providerProperties.runtimePlatform.intValue             = (int)providerAttribute.RuntimePlatforms;
                }
                else
                {
                    providerProperties.componentName.stringValue = dataProviderType.Name;
                }

                serializedObject.ApplyModifiedProperties();
            }
        }
        /// <summary>
        /// Adds a new data provider profile entry (i.e <see cref="IMixedRealityServiceConfiguration"/>) to array list of target object
        /// Utilizes GetDataProviderConfigurationList() to get SerializedProperty object that represents array to insert against
        /// </summary>
        protected virtual void AddDataProvider()
        {
            providerConfigurations.InsertArrayElementAtIndex(providerConfigurations.arraySize);
            SerializedProperty provider = providerConfigurations.GetArrayElementAtIndex(providerConfigurations.arraySize - 1);

            ServiceConfigurationProperties providerProperties = GetDataProviderConfigurationProperties(provider);

            providerProperties.componentName.stringValue            = $"{NewDataProvider} {providerConfigurations.arraySize - 1}";
            providerProperties.runtimePlatform.intValue             = -1;
            providerProperties.providerProfile.objectReferenceValue = null;

            serializedObject.ApplyModifiedProperties();

            var providerType = GetDataProviderConfiguration(providerConfigurations.arraySize - 1).ComponentType;

            providerType.Type = null;

            providerFoldouts.Add(false);
        }