private int SortFields(FieldInfo field1, FieldInfo field2, string[] groups)
    {
        UTInspectorHint hint1 = UTInspectorHint.GetFor(field1);
        UTInspectorHint hint2 = UTInspectorHint.GetFor(field2);

        var group1Idx = Array.IndexOf(groups, hint1.group);
        var group2Idx = Array.IndexOf(groups, hint2.group);

        if (group1Idx != -1 && group2Idx != -1)
        {
            if (group1Idx == group2Idx)
            {
                return(hint1.order - hint2.order);
            }
            else
            {
                return(group1Idx - group2Idx);
            }
        }


        if (group1Idx == -1 && group2Idx == -1)
        {
            int groupOrder = string.Compare(hint1.group, hint2.group, StringComparison.InvariantCultureIgnoreCase);
            if (groupOrder == 0)
            {
                // same group or no group at all
                return(hint1.order - hint2.order);
            }
            // different groups
            // change priority of empty group
            if (string.IsNullOrEmpty(hint1.group))
            {
                return(1);
            }
            else if (string.IsNullOrEmpty(hint2.group))
            {
                return(-1);
            }
            else
            {
                return(groupOrder);
            }
        }
        else if (group1Idx == -1)
        {
            return(1);
        }
        else
        {
            return(-1);
        }
    }
    protected virtual void DrawAll()
    {
        var type   = target.GetType();
        var fields = type.GetFields();
        var groups = UTInspectorGroups.GetFor(type).groups;

        Array.Sort(fields, delegate(FieldInfo field1, FieldInfo field2) {
            return(SortFields(field1, field2, groups));
        });

        bool   firstGroup   = true;
        string currentGroup = "";

        RenderActionHead(type);
        foreach (var field in fields)
        {
            if (field.IsPublic && !field.IsStatic)
            {
                if (!IsVisible(field))
                {
                    continue;                     // skip it.
                }

                var             utDoc  = UTDoc.GetFor(field);
                UTInspectorHint utHint = UTInspectorHint.GetFor(field);
                GUIContent      label  = new GUIContent(utDoc.title + (utHint.required || utHint.arrayNotEmpty ? " *" : ""), utDoc.description);


                if (utHint.group != currentGroup || firstGroup)
                {
                    firstGroup   = false;
                    currentGroup = utHint.group;
                    EditorGUILayout.Space();
                    if (!string.IsNullOrEmpty(currentGroup))
                    {
                        GUILayout.Label(currentGroup, UTEditorResources.GroupStyle);
                    }
                }

                string msg;
                if (!UTRequiresLicenseAttribute.HasRequiredLicense(field, out msg))
                {
                    // disable fields which require licenses that are not currently installed
                    GUI.enabled = false;
                }
                DrawProperty(label, field);
                GUI.enabled = true;
            }
        }
    }
    protected virtual void DrawProperty(GUIContent label, FieldInfo field)
    {
        var fieldValue = field.GetValue(target);

        if (field.FieldType.IsArray)
        {
            var objArray    = (object[])fieldValue;
            var elementType = field.FieldType.GetElementType();

            // initialize array fields with empty arrays.
            if (objArray == null)
            {
                objArray = (object[])Array.CreateInstance(elementType, 0);
                field.SetValue(target, objArray);
                EditorUtility.SetDirty(target);
            }

            EditorGUILayout.BeginHorizontal();
            GUIContent arrayInfo = new GUIContent(objArray.Length + (objArray.Length == 1 ? " item" : " items"));
            EditorGUILayout.LabelField(label, arrayInfo, UTEditorResources.ArrayLabelStyle);
            if (GUILayout.Button("Add", UTEditorResources.ExpressionButtonStyle))
            {
                var newArray = (object[])Array.CreateInstance(elementType, objArray.Length + 1);
                Array.Copy(objArray, newArray, objArray.Length);
                if (elementType.IsSubclassOf(typeof(UTPropertyBase)))
                {
                    newArray [newArray.Length - 1] = Activator.CreateInstance(elementType);
                }
                field.SetValue(target, newArray);
                GUI.changed = true;
                objArray    = newArray;
            }

            EditorGUILayout.EndHorizontal();

            EditorGUILayout.BeginVertical();

            // the index of the element that should be deleted.
            // since the user cannot click multiple buttons at once
            // a single int is enough here.
            int deleteIndex = -1;
            for (int i = 0; i < objArray.Length; i++)
            {
                if (elementType.IsSubclassOf(typeof(UTPropertyBase)))
                {
                    propertyFieldWrapper.SetUp(emptyLabel, field, (UTPropertyBase)objArray [i]);
                    if (DrawPropertyArrayMember(propertyFieldWrapper))
                    {
                        deleteIndex = i;
                    }
                }
                else
                {
                    arrayMemberWrapper.SetUp(emptyLabel, field, objArray, i);
                    if (DrawPropertyArrayMember(arrayMemberWrapper))
                    {
                        deleteIndex = i;
                    }
                }
            }

            if (deleteIndex != -1)
            {
                var newArray = (object[])Array.CreateInstance(elementType, objArray.Length - 1);
                // no copy before index if the index was first element
                if (deleteIndex > 0)
                {
                    Array.Copy(objArray, 0, newArray, 0, deleteIndex);
                }
                // no copy after index if the index was last element
                if (deleteIndex + 1 < objArray.Length)
                {
                    Array.Copy(objArray, deleteIndex + 1, newArray, deleteIndex, objArray.Length - deleteIndex - 1);
                }
                field.SetValue(target, newArray);
                GUI.changed = true;
            }

            // add space only if array isn't empty, looks weird otherwise.
            if (objArray.Length > 0)
            {
                EditorGUILayout.Space();
            }
            else
            {
                var hint = UTInspectorHint.GetFor(field);
                if (hint.arrayNotEmpty)
                {
                    EditorGUILayout.BeginHorizontal();
                    EditorGUILayout.PrefixLabel(" ");
                    EditorGUILayout.HelpBox("At least one element is required.", MessageType.Error);
                    EditorGUILayout.EndHorizontal();
                }
            }

            EditorGUILayout.EndVertical();
        }
        else
        {
            EditorGUILayout.BeginHorizontal();
            if (field.FieldType.IsSubclassOf(typeof(UTPropertyBase)))
            {
                if (fieldValue == null)
                {
                    fieldValue = Activator.CreateInstance(field.FieldType);                      // make an empty instance
                    field.SetValue(target, fieldValue);
                    EditorUtility.SetDirty(target);
                }
                propertyFieldWrapper.SetUp(label, field, (UTPropertyBase)fieldValue);
                DrawProperty(propertyFieldWrapper, false);
            }
            else
            {
                plainFieldWrapper.SetUp(label, field, target);
                DrawProperty(plainFieldWrapper, false);
            }
            EditorGUILayout.EndHorizontal();
        }
    }