// Copyright (c) Rotorz Limited. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root.

using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace Unity.VisualScripting.ReorderableList
{
    /// <summary>
    /// Utility class for drawing reorderable lists.
    /// </summary>
    public static class ReorderableListGUI
    {
        static ReorderableListGUI()
        {
            DefaultListControl = new ReorderableListControl();

            // Duplicate default styles to prevent user scripts from interferring with
            // the default list control instance.
            DefaultListControl.ContainerStyle = new GUIStyle(ReorderableListStyles.Container);
            DefaultListControl.FooterButtonStyle = new GUIStyle(ReorderableListStyles.FooterButton);
            DefaultListControl.ItemButtonStyle = new GUIStyle(ReorderableListStyles.ItemButton);

            IndexOfChangedItem = -1;
        }

        /// <summary>
        /// Default list item height is 18 pixels.
        /// </summary>
        public const float DefaultItemHeight = 18;

        private static GUIContent s_Temp = new GUIContent();

        /// <summary>
        /// Gets or sets the zero-based index of the last item that was changed. A value of -1
        /// indicates that no item was changed by list.
        /// </summary>
        /// <remarks>
        ///     <para>This property should not be set when items are added or removed.</para>
        /// </remarks>
        public static int IndexOfChangedItem { get; internal set; }

        /// <summary>
        /// Gets the control ID of the list that is currently being drawn.
        /// </summary>
        public static int CurrentListControlID => ReorderableListControl.CurrentListControlID;

        /// <summary>
        /// Gets the position of the list control that is currently being drawn.
        /// </summary>
        /// <remarks>
        ///     <para>
        ///     The value of this property should be ignored for <see cref="EventType.Layout" />
        ///     type events when using reorderable list controls with automatic layout.
        ///     </para>
        /// </remarks>
        /// <see cref="CurrentItemTotalPosition" />
        public static Rect CurrentListPosition => ReorderableListControl.CurrentListPosition;

        /// <summary>
        /// Gets the zero-based index of the list item that is currently being drawn;
        /// or a value of -1 if no item is currently being drawn.
        /// </summary>
        public static int CurrentItemIndex => ReorderableListControl.CurrentItemIndex;

        /// <summary>
        /// Gets the total position of the list item that is currently being drawn.
        /// </summary>
        /// <remarks>
        ///     <para>
        ///     The value of this property should be ignored for <see cref="EventType.Layout" />
        ///     type events when using reorderable list controls with automatic layout.
        ///     </para>
        /// </remarks>
        /// <see cref="CurrentItemIndex" />
        /// <see cref="CurrentListPosition" />
        public static Rect CurrentItemTotalPosition => ReorderableListControl.CurrentItemTotalPosition;

        /// <summary>
        /// Gets the default list control implementation.
        /// </summary>
        private static ReorderableListControl DefaultListControl { get; set; }

        #region Basic Item Drawers

        /// <summary>
        /// Default list item drawer implementation.
        /// </summary>
        /// <remarks>
        ///     <para>Always presents the label "Item drawer not implemented.".</para>
        /// </remarks>
        /// <param name="position">Position to draw list item control(s).</param>
        /// <param name="item">Value of list item.</param>
        /// <returns>
        /// Unmodified value of list item.
        /// </returns>
        /// <typeparam name="T">Type of list item.</typeparam>
        public static T DefaultItemDrawer<T>(Rect position, T item)
        {
            GUI.Label(position, "Item drawer not implemented.");
            return item;
        }

        /// <summary>
        /// Draws text field allowing list items to be edited.
        /// </summary>
        /// <remarks>
        ///     <para>
        ///     Null values are automatically changed to empty strings since null
        ///     values cannot be edited using a text field.
        ///     </para>
        ///     <para>
        ///     Value of <c>GUI.changed</c> is set to <c>true</c> if value of item
        ///     is modified.
        ///     </para>
        /// </remarks>
        /// <param name="position">Position to draw list item control(s).</param>
        /// <param name="item">Value of list item.</param>
        /// <returns>
        /// Modified value of list item.
        /// </returns>
        public static string TextFieldItemDrawer(Rect position, string item)
        {
            if (item == null)
            {
                item = "";
                GUI.changed = true;
            }
            return EditorGUI.TextField(position, item);
        }

        #endregion

        #region Title Control

        /// <summary>
        /// Draw title control for list field.
        /// </summary>
        /// <remarks>
        ///     <para>When needed, should be shown immediately before list field.</para>
        /// </remarks>
        /// <example>
        ///     <code language="csharp"><![CDATA[
        /// ReorderableListGUI.Title(titleContent);
        /// ReorderableListGUI.ListField(list, DynamicListGU.TextFieldItemDrawer);
        /// ]]></code>
        ///     <code language="unityscript"><![CDATA[
        /// ReorderableListGUI.Title(titleContent);
        /// ReorderableListGUI.ListField(list, DynamicListGU.TextFieldItemDrawer);
        /// ]]></code>
        /// </example>
        /// <param name="title">Content for title control.</param>
        public static void Title(GUIContent title)
        {
            var position = GUILayoutUtility.GetRect(title, ReorderableListStyles.Title);
            Title(position, title);
            LudiqGUI.Space(-1);
        }

        /// <summary>
        /// Draw title control for list field.
        /// </summary>
        /// <remarks>
        ///     <para>When needed, should be shown immediately before list field.</para>
        /// </remarks>
        /// <example>
        ///     <code language="csharp"><![CDATA[
        /// ReorderableListGUI.Title("Your Title");
        /// ReorderableListGUI.ListField(list, DynamicListGU.TextFieldItemDrawer);
        /// ]]></code>
        ///     <code language="unityscript"><![CDATA[
        /// ReorderableListGUI.Title('Your Title');
        /// ReorderableListGUI.ListField(list, DynamicListGU.TextFieldItemDrawer);
        /// ]]></code>
        /// </example>
        /// <param name="title">Text for title control.</param>
        public static void Title(string title)
        {
            s_Temp.text = title;
            Title(s_Temp);
        }

        /// <summary>
        /// Draw title control for list field with absolute positioning.
        /// </summary>
        /// <param name="position">Position of control.</param>
        /// <param name="title">Content for title control.</param>
        public static void Title(Rect position, GUIContent title)
        {
            if (Event.current.type == EventType.Repaint)
            {
                ReorderableListStyles.Title.Draw(position, title, false, false, false, false);
            }
        }

        /// <summary>
        /// Draw title control for list field with absolute positioning.
        /// </summary>
        /// <param name="position">Position of control.</param>
        /// <param name="text">Text for title control.</param>
        public static void Title(Rect position, string text)
        {
            s_Temp.text = text;
            Title(position, s_Temp);
        }

        #endregion

        #region List<T> Control

        /// <summary>
        /// Draw list field control.
        /// </summary>
        /// <param name="list">The list which can be reordered.</param>
        /// <param name="drawItem">Callback to draw list item.</param>
        /// <param name="drawEmpty">Callback to draw custom content for empty list (optional).</param>
        /// <param name="itemHeight">Height of a single list item.</param>
        /// <param name="flags">Optional flags to pass into list field.</param>
        /// <typeparam name="T">Type of list item.</typeparam>
        private static void DoListField<T>(IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem, ReorderableListControl.DrawEmpty drawEmpty, float itemHeight, ReorderableListFlags flags)
        {
            var adaptor = new GenericListAdaptor<T>(list, drawItem, itemHeight);
            ReorderableListControl.DrawControlFromState(adaptor, drawEmpty, flags);
        }

        /// <summary>
        /// Draw list field control with absolute positioning.
        /// </summary>
        /// <param name="position">Position of control.</param>
        /// <param name="list">The list which can be reordered.</param>
        /// <param name="drawItem">Callback to draw list item.</param>
        /// <param name="drawEmpty">Callback to draw custom content for empty list (optional).</param>
        /// <param name="itemHeight">Height of a single list item.</param>
        /// <param name="flags">Optional flags to pass into list field.</param>
        /// <typeparam name="T">Type of list item.</typeparam>
        private static void DoListFieldAbsolute<T>(Rect position, IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem, ReorderableListControl.DrawEmptyAbsolute drawEmpty, float itemHeight, ReorderableListFlags flags)
        {
            var adaptor = new GenericListAdaptor<T>(list, drawItem, itemHeight);
            ReorderableListControl.DrawControlFromState(position, adaptor, drawEmpty, flags);
        }

        /// <inheritdoc
        ///     cref="DoListField{T}(IList{T}, ReorderableListControl.ItemDrawer{T}, ReorderableListControl.DrawEmpty, float, ReorderableListFlags)" />
        public static void ListField<T>(IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem, ReorderableListControl.DrawEmpty drawEmpty, float itemHeight, ReorderableListFlags flags)
        {
            DoListField(list, drawItem, drawEmpty, itemHeight, flags);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute{T}(Rect, IList{T}, ReorderableListControl.ItemDrawer{T}, ReorderableListControl.DrawEmptyAbsolute, float, ReorderableListFlags)" />
        public static void ListFieldAbsolute<T>(Rect position, IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem, ReorderableListControl.DrawEmptyAbsolute drawEmpty, float itemHeight, ReorderableListFlags flags)
        {
            DoListFieldAbsolute(position, list, drawItem, drawEmpty, itemHeight, flags);
        }

        /// <inheritdoc
        ///     cref="DoListField{T}(IList{T}, ReorderableListControl.ItemDrawer{T}, ReorderableListControl.DrawEmpty, float, ReorderableListFlags)" />
        public static void ListField<T>(IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem, ReorderableListControl.DrawEmpty drawEmpty, float itemHeight)
        {
            DoListField(list, drawItem, drawEmpty, itemHeight, 0);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute{T}(Rect, IList{T}, ReorderableListControl.ItemDrawer{T}, ReorderableListControl.DrawEmptyAbsolute, float, ReorderableListFlags)" />
        public static void ListFieldAbsolute<T>(Rect position, IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem, ReorderableListControl.DrawEmptyAbsolute drawEmpty, float itemHeight)
        {
            DoListFieldAbsolute(position, list, drawItem, drawEmpty, itemHeight, 0);
        }

        /// <inheritdoc
        ///     cref="DoListField{T}(IList{T}, ReorderableListControl.ItemDrawer{T}, ReorderableListControl.DrawEmpty, float, ReorderableListFlags)" />
        public static void ListField<T>(IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem, ReorderableListControl.DrawEmpty drawEmpty, ReorderableListFlags flags)
        {
            DoListField(list, drawItem, drawEmpty, DefaultItemHeight, flags);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute{T}(Rect, IList{T}, ReorderableListControl.ItemDrawer{T}, ReorderableListControl.DrawEmptyAbsolute, float, ReorderableListFlags)" />
        public static void ListFieldAbsolute<T>(Rect position, IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem, ReorderableListControl.DrawEmptyAbsolute drawEmpty, ReorderableListFlags flags)
        {
            DoListFieldAbsolute(position, list, drawItem, drawEmpty, DefaultItemHeight, flags);
        }

        /// <inheritdoc
        ///     cref="DoListField{T}(IList{T}, ReorderableListControl.ItemDrawer{T}, ReorderableListControl.DrawEmpty, float, ReorderableListFlags)" />
        public static void ListField<T>(IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem, ReorderableListControl.DrawEmpty drawEmpty)
        {
            DoListField(list, drawItem, drawEmpty, DefaultItemHeight, 0);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute{T}(Rect, IList{T}, ReorderableListControl.ItemDrawer{T}, ReorderableListControl.DrawEmptyAbsolute, float, ReorderableListFlags)" />
        public static void ListFieldAbsolute<T>(Rect position, IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem, ReorderableListControl.DrawEmptyAbsolute drawEmpty)
        {
            DoListFieldAbsolute(position, list, drawItem, drawEmpty, DefaultItemHeight, 0);
        }

        /// <inheritdoc
        ///     cref="DoListField{T}(IList{T}, ReorderableListControl.ItemDrawer{T}, ReorderableListControl.DrawEmpty, float, ReorderableListFlags)" />
        public static void ListField<T>(IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem, float itemHeight, ReorderableListFlags flags)
        {
            DoListField(list, drawItem, null, itemHeight, flags);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute{T}(Rect, IList{T}, ReorderableListControl.ItemDrawer{T}, ReorderableListControl.DrawEmptyAbsolute, float, ReorderableListFlags)" />
        public static void ListFieldAbsolute<T>(Rect position, IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem, float itemHeight, ReorderableListFlags flags)
        {
            DoListFieldAbsolute(position, list, drawItem, null, itemHeight, flags);
        }

        /// <inheritdoc
        ///     cref="DoListField{T}(IList{T}, ReorderableListControl.ItemDrawer{T}, ReorderableListControl.DrawEmpty, float, ReorderableListFlags)" />
        public static void ListField<T>(IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem, float itemHeight)
        {
            DoListField(list, drawItem, null, itemHeight, 0);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute{T}(Rect, IList{T}, ReorderableListControl.ItemDrawer{T}, ReorderableListControl.DrawEmptyAbsolute, float, ReorderableListFlags)" />
        public static void ListFieldAbsolute<T>(Rect position, IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem, float itemHeight)
        {
            DoListFieldAbsolute(position, list, drawItem, null, itemHeight, 0);
        }

        /// <inheritdoc
        ///     cref="DoListField{T}(IList{T}, ReorderableListControl.ItemDrawer{T}, ReorderableListControl.DrawEmpty, float, ReorderableListFlags)" />
        public static void ListField<T>(IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem, ReorderableListFlags flags)
        {
            DoListField(list, drawItem, null, DefaultItemHeight, flags);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute{T}(Rect, IList{T}, ReorderableListControl.ItemDrawer{T}, ReorderableListControl.DrawEmptyAbsolute, float, ReorderableListFlags)" />
        public static void ListFieldAbsolute<T>(Rect position, IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem, ReorderableListFlags flags)
        {
            DoListFieldAbsolute(position, list, drawItem, null, DefaultItemHeight, flags);
        }

        /// <inheritdoc
        ///     cref="DoListField{T}(IList{T}, ReorderableListControl.ItemDrawer{T}, ReorderableListControl.DrawEmpty, float, ReorderableListFlags)" />
        public static void ListField<T>(IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem)
        {
            DoListField(list, drawItem, null, DefaultItemHeight, 0);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute{T}(Rect, IList{T}, ReorderableListControl.ItemDrawer{T}, ReorderableListControl.DrawEmptyAbsolute, float, ReorderableListFlags)" />
        public static void ListFieldAbsolute<T>(Rect position, IList<T> list, ReorderableListControl.ItemDrawer<T> drawItem)
        {
            DoListFieldAbsolute(position, list, drawItem, null, DefaultItemHeight, 0);
        }

        /// <summary>
        /// Calculate height of list field for absolute positioning.
        /// </summary>
        /// <param name="itemCount">Count of items in list.</param>
        /// <param name="itemHeight">Fixed height of list item.</param>
        /// <param name="flags">Optional flags to pass into list field.</param>
        /// <returns>
        /// Required list height in pixels.
        /// </returns>
        public static float CalculateListFieldHeight(int itemCount, float itemHeight, ReorderableListFlags flags)
        {
            // We need to push/pop flags so that nested controls are properly calculated.
            var restoreFlags = DefaultListControl.Flags;
            try
            {
                DefaultListControl.Flags = flags;
                return DefaultListControl.CalculateListHeight(itemCount, itemHeight);
            }
            finally
            {
                DefaultListControl.Flags = restoreFlags;
            }
        }

        /// <inheritdoc cref="CalculateListFieldHeight(int, float, ReorderableListFlags)" />
        public static float CalculateListFieldHeight(int itemCount, ReorderableListFlags flags)
        {
            return CalculateListFieldHeight(itemCount, DefaultItemHeight, flags);
        }

        /// <inheritdoc cref="CalculateListFieldHeight(int, float, ReorderableListFlags)" />
        public static float CalculateListFieldHeight(int itemCount, float itemHeight)
        {
            return CalculateListFieldHeight(itemCount, itemHeight, 0);
        }

        /// <inheritdoc cref="CalculateListFieldHeight(int, float, ReorderableListFlags)" />
        public static float CalculateListFieldHeight(int itemCount)
        {
            return CalculateListFieldHeight(itemCount, DefaultItemHeight, 0);
        }

        #endregion

        #region SerializedProperty Control

        /// <summary>
        /// Draw list field control for serializable property array.
        /// </summary>
        /// <param name="arrayProperty">Serializable property.</param>
        /// <param name="fixedItemHeight">
        /// Use fixed height for items rather than
        /// <see cref="UnityEditor.EditorGUI.GetPropertyHeight(SerializedProperty)" />.
        /// </param>
        /// <param name="drawEmpty">Callback to draw custom content for empty list (optional).</param>
        /// <param name="flags">Optional flags to pass into list field.</param>
        private static void DoListField(SerializedProperty arrayProperty, float fixedItemHeight, ReorderableListControl.DrawEmpty drawEmpty, ReorderableListFlags flags)
        {
            var adaptor = new SerializedPropertyAdaptor(arrayProperty, fixedItemHeight);
            ReorderableListControl.DrawControlFromState(adaptor, drawEmpty, flags);
        }

        /// <summary>
        /// Draw list field control for serializable property array.
        /// </summary>
        /// <param name="position">Position of control.</param>
        /// <param name="arrayProperty">Serializable property.</param>
        /// <param name="fixedItemHeight">
        /// Use fixed height for items rather than
        /// <see cref="UnityEditor.EditorGUI.GetPropertyHeight(SerializedProperty)" />.
        /// </param>
        /// <param name="drawEmpty">Callback to draw custom content for empty list (optional).</param>
        /// <param name="flags">Optional flags to pass into list field.</param>
        private static void DoListFieldAbsolute(Rect position, SerializedProperty arrayProperty, float fixedItemHeight, ReorderableListControl.DrawEmptyAbsolute drawEmpty, ReorderableListFlags flags)
        {
            var adaptor = new SerializedPropertyAdaptor(arrayProperty, fixedItemHeight);
            ReorderableListControl.DrawControlFromState(position, adaptor, drawEmpty, flags);
        }

        /// <inheritdoc cref="DoListField(SerializedProperty, float, ReorderableListControl.DrawEmpty, ReorderableListFlags)" />
        public static void ListField(SerializedProperty arrayProperty, ReorderableListControl.DrawEmpty drawEmpty, ReorderableListFlags flags)
        {
            DoListField(arrayProperty, 0, drawEmpty, flags);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute(Rect, SerializedProperty, float, ReorderableListControl.DrawEmptyAbsolute, ReorderableListFlags)" />
        public static void ListFieldAbsolute(Rect position, SerializedProperty arrayProperty, ReorderableListControl.DrawEmptyAbsolute drawEmpty, ReorderableListFlags flags)
        {
            DoListFieldAbsolute(position, arrayProperty, 0, drawEmpty, flags);
        }

        /// <inheritdoc cref="DoListField(SerializedProperty, float, ReorderableListControl.DrawEmpty, ReorderableListFlags)" />
        public static void ListField(SerializedProperty arrayProperty, ReorderableListControl.DrawEmpty drawEmpty)
        {
            DoListField(arrayProperty, 0, drawEmpty, 0);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute(Rect, SerializedProperty, float, ReorderableListControl.DrawEmptyAbsolute, ReorderableListFlags)" />
        public static void ListFieldAbsolute(Rect position, SerializedProperty arrayProperty, ReorderableListControl.DrawEmptyAbsolute drawEmpty)
        {
            DoListFieldAbsolute(position, arrayProperty, 0, drawEmpty, 0);
        }

        /// <inheritdoc cref="DoListField(SerializedProperty, float, ReorderableListControl.DrawEmpty, ReorderableListFlags)" />
        public static void ListField(SerializedProperty arrayProperty, ReorderableListFlags flags)
        {
            DoListField(arrayProperty, 0, null, flags);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute(Rect, SerializedProperty, float, ReorderableListControl.DrawEmptyAbsolute, ReorderableListFlags)" />
        public static void ListFieldAbsolute(Rect position, SerializedProperty arrayProperty, ReorderableListFlags flags)
        {
            DoListFieldAbsolute(position, arrayProperty, 0, null, flags);
        }

        /// <inheritdoc cref="DoListField(SerializedProperty, float, ReorderableListControl.DrawEmpty, ReorderableListFlags)" />
        public static void ListField(SerializedProperty arrayProperty)
        {
            DoListField(arrayProperty, 0, null, 0);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute(Rect, SerializedProperty, float, ReorderableListControl.DrawEmptyAbsolute, ReorderableListFlags)" />
        public static void ListFieldAbsolute(Rect position, SerializedProperty arrayProperty)
        {
            DoListFieldAbsolute(position, arrayProperty, 0, null, 0);
        }

        /// <summary>
        /// Calculate height of list field for absolute positioning.
        /// </summary>
        /// <param name="arrayProperty">Serializable property.</param>
        /// <param name="flags">Optional flags to pass into list field.</param>
        /// <returns>
        /// Required list height in pixels.
        /// </returns>
        public static float CalculateListFieldHeight(SerializedProperty arrayProperty, ReorderableListFlags flags)
        {
            // We need to push/pop flags so that nested controls are properly calculated.
            var restoreFlags = DefaultListControl.Flags;
            try
            {
                DefaultListControl.Flags = flags;
                return DefaultListControl.CalculateListHeight(new SerializedPropertyAdaptor(arrayProperty));
            }
            finally
            {
                DefaultListControl.Flags = restoreFlags;
            }
        }

        /// <inheritdoc cref="CalculateListFieldHeight(SerializedProperty, ReorderableListFlags)" />
        public static float CalculateListFieldHeight(SerializedProperty arrayProperty)
        {
            return CalculateListFieldHeight(arrayProperty, 0);
        }

        #endregion

        #region SerializedProperty Control (Fixed Item Height)

        /// <inheritdoc cref="DoListField(SerializedProperty, float, ReorderableListControl.DrawEmpty, ReorderableListFlags)" />
        public static void ListField(SerializedProperty arrayProperty, float fixedItemHeight, ReorderableListControl.DrawEmpty drawEmpty, ReorderableListFlags flags)
        {
            DoListField(arrayProperty, fixedItemHeight, drawEmpty, flags);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute(Rect, SerializedProperty, float, ReorderableListControl.DrawEmptyAbsolute, ReorderableListFlags)" />
        public static void ListFieldAbsolute(Rect position, SerializedProperty arrayProperty, float fixedItemHeight, ReorderableListControl.DrawEmptyAbsolute drawEmpty, ReorderableListFlags flags)
        {
            DoListFieldAbsolute(position, arrayProperty, fixedItemHeight, drawEmpty, flags);
        }

        /// <inheritdoc cref="DoListField(SerializedProperty, float, ReorderableListControl.DrawEmpty, ReorderableListFlags)" />
        public static void ListField(SerializedProperty arrayProperty, float fixedItemHeight, ReorderableListControl.DrawEmpty drawEmpty)
        {
            DoListField(arrayProperty, fixedItemHeight, drawEmpty, 0);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute(Rect, SerializedProperty, float, ReorderableListControl.DrawEmptyAbsolute, ReorderableListFlags)" />
        public static void ListFieldAbsolute(Rect position, SerializedProperty arrayProperty, float fixedItemHeight, ReorderableListControl.DrawEmptyAbsolute drawEmpty)
        {
            DoListFieldAbsolute(position, arrayProperty, fixedItemHeight, drawEmpty, 0);
        }

        /// <inheritdoc cref="DoListField(SerializedProperty, float, ReorderableListControl.DrawEmpty, ReorderableListFlags)" />
        public static void ListField(SerializedProperty arrayProperty, float fixedItemHeight, ReorderableListFlags flags)
        {
            DoListField(arrayProperty, fixedItemHeight, null, flags);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute(Rect, SerializedProperty, float, ReorderableListControl.DrawEmptyAbsolute, ReorderableListFlags)" />
        public static void ListFieldAbsolute(Rect position, SerializedProperty arrayProperty, float fixedItemHeight, ReorderableListFlags flags)
        {
            DoListFieldAbsolute(position, arrayProperty, fixedItemHeight, null, flags);
        }

        /// <inheritdoc cref="DoListField(SerializedProperty, float, ReorderableListControl.DrawEmpty, ReorderableListFlags)" />
        public static void ListField(SerializedProperty arrayProperty, float fixedItemHeight)
        {
            DoListField(arrayProperty, fixedItemHeight, null, 0);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute(Rect, SerializedProperty, float, ReorderableListControl.DrawEmptyAbsolute, ReorderableListFlags)" />
        public static void ListFieldAbsolute(Rect position, SerializedProperty arrayProperty, float fixedItemHeight)
        {
            DoListFieldAbsolute(position, arrayProperty, fixedItemHeight, null, 0);
        }

        #endregion

        #region Adaptor Control

        /// <summary>
        /// Draw list field control for adapted collection.
        /// </summary>
        /// <param name="adaptor">Reorderable list adaptor.</param>
        /// <param name="drawEmpty">Callback to draw custom content for empty list (optional).</param>
        /// <param name="flags">Optional flags to pass into list field.</param>
        private static void DoListField(IReorderableListAdaptor adaptor, ReorderableListControl.DrawEmpty drawEmpty, ReorderableListFlags flags = 0)
        {
            ReorderableListControl.DrawControlFromState(adaptor, drawEmpty, flags);
        }

        /// <summary>
        /// Draw list field control for adapted collection.
        /// </summary>
        /// <param name="position">Position of control.</param>
        /// <param name="adaptor">Reorderable list adaptor.</param>
        /// <param name="drawEmpty">Callback to draw custom content for empty list (optional).</param>
        /// <param name="flags">Optional flags to pass into list field.</param>
        private static void DoListFieldAbsolute(Rect position, IReorderableListAdaptor adaptor, ReorderableListControl.DrawEmptyAbsolute drawEmpty, ReorderableListFlags flags = 0)
        {
            ReorderableListControl.DrawControlFromState(position, adaptor, drawEmpty, flags);
        }

        /// <inheritdoc cref="DoListField(IReorderableListAdaptor, ReorderableListControl.DrawEmpty, ReorderableListFlags)" />
        public static void ListField(IReorderableListAdaptor adaptor, ReorderableListControl.DrawEmpty drawEmpty, ReorderableListFlags flags)
        {
            DoListField(adaptor, drawEmpty, flags);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute(Rect, IReorderableListAdaptor, ReorderableListControl.DrawEmptyAbsolute, ReorderableListFlags)" />
        public static void ListFieldAbsolute(Rect position, IReorderableListAdaptor adaptor, ReorderableListControl.DrawEmptyAbsolute drawEmpty, ReorderableListFlags flags)
        {
            DoListFieldAbsolute(position, adaptor, drawEmpty, flags);
        }

        /// <inheritdoc cref="DoListField(IReorderableListAdaptor, ReorderableListControl.DrawEmpty, ReorderableListFlags)" />
        public static void ListField(IReorderableListAdaptor adaptor, ReorderableListControl.DrawEmpty drawEmpty)
        {
            DoListField(adaptor, drawEmpty);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute(Rect, IReorderableListAdaptor, ReorderableListControl.DrawEmptyAbsolute, ReorderableListFlags)" />
        public static void ListFieldAbsolute(Rect position, IReorderableListAdaptor adaptor, ReorderableListControl.DrawEmptyAbsolute drawEmpty)
        {
            DoListFieldAbsolute(position, adaptor, drawEmpty);
        }

        /// <inheritdoc cref="DoListField(IReorderableListAdaptor, ReorderableListControl.DrawEmpty, ReorderableListFlags)" />
        public static void ListField(IReorderableListAdaptor adaptor, ReorderableListFlags flags)
        {
            DoListField(adaptor, null, flags);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute(Rect, IReorderableListAdaptor, ReorderableListControl.DrawEmptyAbsolute, ReorderableListFlags)" />
        public static void ListFieldAbsolute(Rect position, IReorderableListAdaptor adaptor, ReorderableListFlags flags)
        {
            DoListFieldAbsolute(position, adaptor, null, flags);
        }

        /// <inheritdoc cref="DoListField(IReorderableListAdaptor, ReorderableListControl.DrawEmpty, ReorderableListFlags)" />
        public static void ListField(IReorderableListAdaptor adaptor)
        {
            DoListField(adaptor, null);
        }

        /// <inheritdoc
        ///     cref="DoListFieldAbsolute(Rect, IReorderableListAdaptor, ReorderableListControl.DrawEmptyAbsolute, ReorderableListFlags)" />
        public static void ListFieldAbsolute(Rect position, IReorderableListAdaptor adaptor)
        {
            DoListFieldAbsolute(position, adaptor, null);
        }

        /// <summary>
        /// Calculate height of list field for adapted collection.
        /// </summary>
        /// <param name="adaptor">Reorderable list adaptor.</param>
        /// <param name="flags">Optional flags to pass into list field.</param>
        /// <returns>
        /// Required list height in pixels.
        /// </returns>
        public static float CalculateListFieldHeight(IReorderableListAdaptor adaptor, ReorderableListFlags flags)
        {
            // We need to push/pop flags so that nested controls are properly calculated.
            var restoreFlags = DefaultListControl.Flags;
            try
            {
                DefaultListControl.Flags = flags;
                return DefaultListControl.CalculateListHeight(adaptor);
            }
            finally
            {
                DefaultListControl.Flags = restoreFlags;
            }
        }

        /// <inheritdoc cref="CalculateListFieldHeight(IReorderableListAdaptor, ReorderableListFlags)" />
        public static float CalculateListFieldHeight(IReorderableListAdaptor adaptor)
        {
            return CalculateListFieldHeight(adaptor, 0);
        }

        #endregion
    }
}
