static FunctionalityInjectionModuleEditor()
        {
#if UNITY_2019_2_OR_NEWER
            foreach (var type in TypeCache.GetTypesDerivedFrom <IFunctionalityProvider>())
            {
                if (type.IsAbstract || type.IsInterface)
                {
                    continue;
                }

                k_AllProviderTypes.Add(type);
            }

            foreach (var type in TypeCache.GetTypesDerivedFrom <IFunctionalitySubscriber>())
            {
                if (type.IsAbstract || type.IsInterface)
                {
                    continue;
                }

                k_AllSubscriberTypes.Add(type);
            }
#else
            typeof(IFunctionalityProvider).GetImplementationsOfInterface(k_AllProviderTypes);
            typeof(IFunctionalitySubscriber).GetImplementationsOfInterface(k_AllSubscriberTypes);
#endif

            k_AllProviderTypes.Sort((a, b) =>
            {
                var optionsA = FunctionalityIsland.GetProviderSelectionOptions(a);
                var optionsB = FunctionalityIsland.GetProviderSelectionOptions(b);
                return(optionsB.Priority.CompareTo(optionsA.Priority));
            });
        }
        /// <summary>
        /// Set up functionality providers from the list of default providers
        /// This allows custom serialized data to be set up on prefabs for providers
        /// </summary>
        /// <param name="island">The functionality island on which to set up default providers</param>
        /// <param name="requiredTraits">The required traits that must be satisfied by providers</param>
        /// <param name="newProviders">(Optional) A list to which new providers will be added</param>
        public static void RequireProvidersWithDefaultProviders(this FunctionalityIsland island, HashSet <TraitDefinition> requiredTraits, List <IFunctionalityProvider> newProviders = null)
        {
            if (requiredTraits.Count == 0)
            {
                return;
            }

            Profiler.BeginSample(FunctionalityIsland.SetupDefaultProvidersProfilerLabel);

            island.CheckSetup();

            var functionalityInjectionModuleLogging = ModuleLoaderDebugSettings.instance.functionalityInjectionModuleLogging;

            if (functionalityInjectionModuleLogging)
            {
                var list = new StringBuilder();

                if (requiredTraits.Count > 0)
                {
                    foreach (var trait in requiredTraits)
                    {
                        list.Append(trait);
                        list.Append(", ");
                    }

                    list.Length -= 2;
                }

                Debug.LogFormat("Requiring providers with defaults on: {0}", string.Join(", ", list));
            }

            // Copy the collection so that we don't modify the original
            requiredTraits = new HashSet <TraitDefinition>(requiredTraits);

            // Clear out traits that are already being provided
            foreach (var provider in island.uniqueProviders)
            {
                var traitsProvider = provider as IProvidesTraits;
                if (traitsProvider != null)
                {
                    var providedTraits = traitsProvider.GetProvidedTraits();
                    foreach (var trait in providedTraits)
                    {
                        requiredTraits.Remove(trait);
                    }
                }
            }

            k_DefaultProviderMap.Clear();
            foreach (var row in island.defaultProviders)
            {
                var providerTypeName = row.providerTypeName;

                var prefab = row.defaultProviderPrefab;
                if (prefab != null)
                {
                    var providersInPrefab = prefab.GetComponentsInChildren <IFunctionalityProvider>();
                    foreach (var provider in providersInPrefab)
                    {
                        k_DefaultProviderMap[provider.GetType()] = row;
                    }

                    continue;
                }

                var defaultProviderType = row.defaultProviderType;
                if (defaultProviderType == null)
                {
                    var defaultProviderTypeName = row.defaultProviderTypeName;
                    Debug.LogWarningFormat("Cannot set up default provider for {0} in {1}. Type {2} cannot be found.", providerTypeName, island.name, defaultProviderTypeName);
                    continue;
                }

                k_DefaultProviderMap[defaultProviderType] = row;
            }

            // Determine which provider interfaces are needed for the given subscribers
            k_NewProviderPrefabInstances.Clear();
            var currentPlatform = ModuleLoaderCore.GetCurrentPlatform();

            while (CheckMissingTraits(island, requiredTraits) > 0)
            {
                var providerAdded = false;
                k_RequiredTraits.Clear();
                k_RequiredTraits.UnionWith(requiredTraits); // Copy the collection in order to modify requiredTraits
                foreach (var trait in k_RequiredTraits)
                {
                    List <Type> providerTypes;
                    if (!k_TraitProviderMap.TryGetValue(trait, out providerTypes))
                    {
                        continue;
                    }

                    // Make sure potential provider types can be added
                    k_ProviderTypes.Clear();
                    foreach (var providerType in providerTypes)
                    {
                        var options = FunctionalityIsland.GetProviderSelectionOptions(providerType);
                        if (options != null)
                        {
                            if (options.DisallowAutoCreation)
                            {
                                if (!k_DefaultProviderMap.TryGetValue(providerType, out var defaultProvider) || defaultProvider.defaultProviderPrefab == null)
                                {
                                    continue;
                                }
                            }

                            var excludedPlatforms = options.ExcludedPlatforms;
                            if (excludedPlatforms != null && options.ExcludedPlatforms.Contains(currentPlatform))
                            {
                                continue;
                            }
                        }

                        if (island.CanAddProviderType(providerType))
                        {
                            k_ProviderTypes.Add(providerType);
                        }
                    }

                    if (k_ProviderTypes.Count == 0)
                    {
                        continue;
                    }

                    k_ProviderTypes.Sort((a, b) =>
                    {
                        var compare = GetProviderScore(b, requiredTraits).CompareTo(GetProviderScore(a, requiredTraits));
                        if (compare != 0)
                        {
                            return(compare);
                        }

                        compare = k_DefaultProviderMap.ContainsKey(b).CompareTo(k_DefaultProviderMap.ContainsKey(a));
                        if (compare != 0)
                        {
                            return(compare);
                        }

                        var optionsA = FunctionalityIsland.GetProviderSelectionOptions(a);
                        var optionsB = FunctionalityIsland.GetProviderSelectionOptions(b);
                        return(optionsB.Priority.CompareTo(optionsA.Priority));
                    });

                    var firstProvider = k_ProviderTypes[0];
                    if (k_ProviderTypes.Count > 1)
                    {
                        var secondProvider  = k_ProviderTypes[1];
                        var firstScore      = GetProviderScore(firstProvider, requiredTraits);
                        var secondScore     = GetProviderScore(secondProvider, requiredTraits);
                        var firstIsDefault  = k_DefaultProviderMap.ContainsKey(firstProvider);
                        var secondIsDefault = k_DefaultProviderMap.ContainsKey(secondProvider);
                        var firstPriority   = FunctionalityIsland.GetProviderSelectionOptions(firstProvider).Priority;
                        var secondPriority  = FunctionalityIsland.GetProviderSelectionOptions(secondProvider).Priority;
                        if (firstScore == secondScore && firstIsDefault == secondIsDefault && firstPriority == secondPriority)
                        {
                            Debug.LogWarning(string.Format(k_EqualScoreMessage, trait, firstProvider, secondProvider, island.name), island);
                        }
                    }

                    if (k_DefaultProviderMap.TryGetValue(firstProvider, out var row))
                    {
                        var prefab = row.defaultProviderPrefab;
                        var providesRequiredTrait = false;
                        if (prefab != null)
                        {
                            var prefabComponents = prefab.gameObject.GetComponentsInChildren <IProvidesTraits>();
                            foreach (var prefabComponent in prefabComponents)
                            {
                                var providedTraits = prefabComponent.GetProvidedTraits();
                                foreach (var providedTrait in providedTraits)
                                {
                                    providesRequiredTrait |= requiredTraits.Remove(providedTrait);
                                }
                            }

                            if (!providesRequiredTrait)
                            {
                                continue;
                            }

                            GameObject instance;
                            if (!k_NewProviderPrefabInstances.TryGetValue(prefab, out instance))
                            {
                                if (functionalityInjectionModuleLogging)
                                {
                                    Debug.LogFormat("Functionality Injection Module creating default provider: {0}", prefab);
                                }

                                instance = GameObjectUtils.Instantiate(prefab);
                                k_NewProviderPrefabInstances[prefab] = instance;
                            }

                            var providersInPrefab = instance.GetComponentsInChildren <IFunctionalityProvider>();
                            foreach (var provider in providersInPrefab)
                            {
                                AddRequirementsAndRemoveProvidedTraits(requiredTraits, provider);
                                island.AddProvider(provider.GetType(), provider);
                            }

                            continue;
                        }

                        var defaultProviderTypeName = row.defaultProviderTypeName;
                        var defaultProviderType     = row.defaultProviderType;
                        var providerType            = row.providerType;

                        TraitDefinition[] staticProvided;
                        if (!k_ProvidedTraits.TryGetValue(defaultProviderType, out staticProvided))
                        {
                            continue;
                        }

                        foreach (var providedTrait in staticProvided)
                        {
                            providesRequiredTrait |= requiredTraits.Remove(providedTrait);
                        }

                        if (!providesRequiredTrait)
                        {
                            continue;
                        }

                        var vanillaProvider = FunctionalityIsland.GetOrCreateProviderInstance(defaultProviderType, providerType);
                        if (vanillaProvider == null)
                        {
                            Debug.LogWarningFormat("Cannot instantiate {0} as an IFunctionalityProvider.", defaultProviderTypeName);
                            continue;
                        }

                        if (newProviders != null)
                        {
                            newProviders.Add(vanillaProvider);
                        }

                        providerAdded = true;
                        AddRequirementsAndRemoveProvidedTraits(requiredTraits, vanillaProvider);
                        island.AddProvider(defaultProviderType, vanillaProvider);
                    }
                    else
                    {
                        // Spawn or gain access to the class needed to support the functionality
                        var provider = FunctionalityIsland.GetOrCreateProviderInstance(firstProvider, firstProvider);
                        if (provider == null)
                        {
                            Debug.LogWarningFormat("Cannot instantiate {0} as an IFunctionalityProvider.", firstProvider.Name);
                            continue;
                        }

                        if (newProviders != null)
                        {
                            newProviders.Add(provider);
                        }

                        providerAdded = true;
                        AddRequirementsAndRemoveProvidedTraits(requiredTraits, provider);
                        island.AddProvider(firstProvider, provider);
                    }

                    break;
                }

                if (!providerAdded)
                {
                    break;
                }
            }

            island.InjectFunctionalityInDefaultProviders(k_NewProviderPrefabInstances, newProviders);
            island.ActivateProviderGameObjects();

            Profiler.EndSample();
        }
        public override void OnInspectorGUI()
        {
            using (var check = new EditorGUI.ChangeCheckScope())
            {
                EditorGUILayout.PropertyField(m_DefaultIslandProperty);
                m_ShowIslands = EditorGUILayout.Foldout(m_ShowIslands, "Current Islands", true);
                if (m_ShowIslands)
                {
                    DrawCurrentIslands();
                }

                if (check.changed)
                {
                    serializedObject.ApplyModifiedProperties();
                }
            }

            const float providerColumnWidthRatio = 0.5f;
            var         providerWidth            = EditorGUIUtility.currentViewWidth * providerColumnWidthRatio;
            var         foldoutStyle             = styles.ProviderTypesFoldoutStyle;

            styles.ProviderTypesFoldoutStyle.fixedWidth = providerWidth;
            var providerColumnWidth = GUILayout.Width(providerWidth);
            var priorityColumnWidth = GUILayout.Width(k_PriorityColumnWidth);

            using (new EditorGUILayout.HorizontalScope())
            {
                m_ShowProviderTypeList = EditorGUILayout.Foldout(m_ShowProviderTypeList, "Provider Types", true, foldoutStyle);
                if (m_ShowProviderTypeList)
                {
                    GUILayout.Space(providerWidth - k_PriorityHeaderOffset);
                    GUILayout.Label("Priorities", priorityColumnWidth);
                    GUILayout.Label("Excluded Platforms");
                }
            }

            if (m_ShowProviderTypeList)
            {
                using (new EditorGUI.IndentLevelScope())
                {
                    foreach (var type in k_AllProviderTypes)
                    {
                        using (new EditorGUILayout.HorizontalScope())
                        {
                            var priority          = 0;
                            var excludedPlatforms = string.Empty;
                            var priorityAttribute = FunctionalityIsland.GetProviderSelectionOptions(type);
                            if (priorityAttribute != null)
                            {
                                priority = priorityAttribute.Priority;
                                var platforms = priorityAttribute.ExcludedPlatforms;
                                if (platforms != null)
                                {
                                    excludedPlatforms = String.Join(", ", platforms);
                                }
                            }

                            MonoScript script;
                            if (k_MonoScripts.TryGetValue(type, out script))
                            {
                                EditorGUILayout.ObjectField(script, typeof(MonoScript), false, providerColumnWidth);
                            }
                            else
                            {
                                EditorGUILayout.LabelField(type.GetFullNameWithGenericArguments(), providerColumnWidth);
                            }

                            EditorGUILayout.LabelField(priority.ToString(), priorityColumnWidth);
                            EditorGUILayout.LabelField(string.Join(", ", excludedPlatforms));
                        }
                    }
                }
            }

            m_ShowSubscriberTypeList = EditorGUILayout.Foldout(m_ShowSubscriberTypeList, "Subscriber Types", true);
            if (m_ShowSubscriberTypeList)
            {
                using (new EditorGUI.IndentLevelScope())
                {
                    foreach (var type in k_AllSubscriberTypes)
                    {
                        MonoScript script;
                        if (k_MonoScripts.TryGetValue(type, out script))
                        {
                            EditorGUILayout.ObjectField(script, typeof(MonoScript), false);
                        }
                        else
                        {
                            EditorGUILayout.LabelField(type.GetFullNameWithGenericArguments());
                        }
                    }
                }
            }
        }