[CustomEditor(typeof(Initializer))]
    public class InitializerEditor : Editor
    {
        
        public override VisualElement CreateInspectorGUI()
        {
            VisualElement inspectorElement = new VisualElement();
            // Draw the default UI of the base class.
            inspectorElement.Add(new IMGUIContainer(() => DrawDefaultInspector()));

            // Store the reference to the object this editor is modifying
            Initializer initializer = (Initializer)target;
            
            Dictionary<string, Type> ghostParts = GetSubtypesOf<AbstractGhostPart>();
            List<string> ghostPartNames = ghostParts.Keys.ToList();
            if (ghostPartNames.Count == 0)
            {
                inspectorElement.Add(new HelpBox("Could not detect any implementations of AbstractGhostPart in the project. Check your Assembly Definition references, and ensure you have a class that inherits from AbstractGhostPart.", HelpBoxMessageType.Error));
                initializer.ghostPartType = null;
            } else
            {
                int selectedIndex = 0;

                //If there is no GhostPart set, set it to the first element in the dropdown, which is what will be selected by default.
                if (initializer.ghostPartType == null || !initializer.ghostPartType.IsSubclassOf(typeof(AbstractGhostPart)))
                    initializer.ghostPartType = ghostParts[ghostPartNames[0]];
                else
                    selectedIndex = ghostPartNames.IndexOf(initializer.ghostPartType.Name);

                DropdownField ghostPartDropdown = new DropdownField("GhostPart Type", ghostPartNames, selectedIndex);

                ghostPartDropdown.RegisterValueChangedCallback(evt => initializer.ghostPartType = ghostParts[evt.newValue]);
                inspectorElement.Add(ghostPartDropdown);
            }

            return inspectorElement;
        }

        /// <summary>
        /// Gets a list of all types in the project that are subtypes of a given type.
        /// </summary>
        /// <typeparam name="TypeToCheck">The type to look for subtypes of. Note that a Type is not a subtype of itself. This Type will not be returned.</typeparam>
        /// <returns>A Dictionary from name to Type of the Types that are subtypes of the given type</returns>
        private Dictionary<string, Type> GetSubtypesOf<TypeToCheck>()
        {
            Dictionary<string, Type> availableGhostPartTypes = new();
            Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly assembly in assemblies)
            {
                Type[] types = assembly.GetTypes();
                foreach (Type t in types)
                {
                    if (isOfType(t, typeof(TypeToCheck)))
                    {
                        availableGhostPartTypes[t.Name] = t;
                    }
                }
            }
            return availableGhostPartTypes;
        }

        /// <summary>
        /// Checks if the given Type is a sub-type of the given Parent Type.
        /// Checks the given Type's immediate base type, and if it doesn't match, recurses up the Type tree until we run out or find a match.
        /// </summary>
        /// <param name="originalType">The Type we want to check</param>
        /// <param name="parentTypeToCheck">The Original Parent Type we want to see if the given Type is a sub-type of</param>
        /// <returns>True if originalType is a sub-type of parentTypeToCheck. False if otherwise. Not that if originalType _is_ the parent type, this will intentionally return false.</returns>
        private static bool isOfType(Type originalType, Type parentTypeToCheck)
        {
            Type t = originalType.BaseType;
            while (t != null)
            {
                if (t.Equals(parentTypeToCheck)) return true;
                t = t.BaseType;
            }
            return false;
        }
    }
}