private static bool ValidateThemeCollection(AssetSwapUtility utility, out string warning)
        {
            // Make sure all themes have valid assets and each row contains similar types.
            for (int i = 0; i < utility.Themes[0].Assets.Count; ++i)
            {
                System.Type type = null;

                for (int j = 0; j < utility.Themes.Count; ++j)
                {
                    if (utility.Themes[j].Assets[i] == null)
                    {
                        warning = $"Theme \"{GetThemeName(utility.Themes[j], j)}\" asset index {i} is null.";
                        return(false);
                    }

                    System.Type currentType = utility.Themes[j].Assets[i].GetType();

                    if (j == 0)
                    {
                        type = currentType;
                    }
                    else if (currentType != type)
                    {
                        warning = $"Theme \"{GetThemeName(utility.Themes[j], j)}\" asset index {i} is of mismatched type. Expected \"{type}\" and got \"{currentType}\".";
                        return(false);
                    }
                }
            }

            warning = string.Empty;
            return(true);
        }
        private static void SwapObjectReferencesRecurse(GameObject gameObject, AssetSwapUtility utility, AssetSwapUtility.Theme selectedTheme)
        {
            SwapObjectReferences(gameObject, utility, selectedTheme);

            foreach (Transform child in gameObject.transform)
            {
                SwapObjectReferencesRecurse(child.gameObject, utility, selectedTheme);
            }
        }
        public static void CreateAssetSwapCollection()
        {
            AssetSwapUtility utility = CreateInstance <AssetSwapUtility>();

            AssetDatabase.CreateAsset(utility, AssetDatabase.GenerateUniqueAssetPath("Assets/AssetSwapCollection.asset"));
            AssetDatabase.SaveAssets();

            Selection.activeObject = utility;
            EditorUtility.FocusProjectWindow();
        }
        private static void SwapObjectReferences(GameObject gameObject, AssetSwapUtility utility, AssetSwapUtility.Theme selectedTheme)
        {
            var components = gameObject.GetComponents <Component>();

            foreach (var component in components)
            {
                if (component == null)
                {
                    continue;
                }

                SerializedObject   serializedObject = new SerializedObject(component);
                SerializedProperty property         = serializedObject.GetIterator();
                bool modified = false;

                while (property.NextVisible(true))
                {
                    if (property.propertyType == SerializedPropertyType.ObjectReference &&
                        property.objectReferenceValue != null)
                    {
                        Object currentAsset = property.objectReferenceValue;

                        // Does the current asset match any non-selected theme(s) asset(s)?
                        if (currentAsset != null)
                        {
                            foreach (var theme in utility.Themes)
                            {
                                if (theme == selectedTheme)
                                {
                                    continue;
                                }

                                int assetIndex = 0;

                                foreach (var asset in theme.Assets)
                                {
                                    if (asset == currentAsset)
                                    {
                                        property.objectReferenceValue = selectedTheme.Assets[assetIndex];
                                        modified = true;
                                    }

                                    ++assetIndex;
                                }
                            }
                        }
                    }
                }

                if (modified == true)
                {
                    property.serializedObject.ApplyModifiedProperties();
                }
            }
        }
        public override void OnInspectorGUI()
        {
            DrawTable();

            AssetSwapUtility utility = serializedObject.targetObject as AssetSwapUtility;

            if (utility != null)
            {
                DrawControls(utility);
            }
        }
        private void DrawControls(AssetSwapUtility utility)
        {
            EditorGUILayout.BeginVertical("Box");
            {
                List <string> displayedOptions = new List <string>(utility.Themes.Count);

                for (int i = 0; i < utility.Themes.Count; ++i)
                {
                    var theme = utility.Themes[i];
                    displayedOptions.Add(GetThemeName(theme, i));
                }

                selectedThemeIndex = EditorGUILayout.Popup("Selected Theme", selectedThemeIndex, displayedOptions.ToArray());
                selectionMode      = (SelectionMode)EditorGUILayout.EnumPopup("Selection Mode", selectionMode);

                string warning;
                GUI.enabled = ValidateThemeCollection(utility, out warning);

                GameObject[] gameObjects = null;

                if (GUI.enabled == true)
                {
                    GUI.enabled = ValidateSelection(selectionMode, out gameObjects, out warning);
                }

                EditorGUILayout.Space();

                if (GUILayout.Button("Apply"))
                {
                    Apply(gameObjects,
                          utility,
                          utility.Themes[selectedThemeIndex],
                          selectionMode != SelectionMode.SelectionWithoutChildren);
                }

                if (GUI.enabled == false)
                {
                    EditorGUILayout.HelpBox(warning, MessageType.Warning);
                    GUI.enabled = true;
                }
            }
            EditorGUILayout.EndVertical();
        }
        private static void Apply(GameObject[] gameObjects, AssetSwapUtility utility, AssetSwapUtility.Theme selectedTheme, bool recurse)
        {
            int progress = 0;

            foreach (var gameObject in gameObjects)
            {
                EditorUtility.DisplayProgressBar("Applying Theme Change", "Please wait...", (float)progress / gameObjects.Length);

                if (recurse)
                {
                    SwapObjectReferencesRecurse(gameObject, utility, selectedTheme);
                }
                else
                {
                    SwapObjectReferences(gameObject, utility, selectedTheme);
                }

                ++progress;
            }

            EditorUtility.ClearProgressBar();
        }