using System;
using UnityEditor;
using UnityEngine;

namespace Assets.Source.Editor.Utils
{
    [Flags]
    public enum EditorListOption
    {
        None = 0,
        ListSize = 1,
        ListLabel = 2,
        ElementLabels = 4,
        Buttons = 8,
        Default = ListSize | ListLabel | ElementLabels,
        NoElementLabels = ListSize | ListLabel,
        All = Default | Buttons
    }

    public static class EditorList
    {
        public delegate void ListElementHandler(int index, SerializedProperty prop);
        public delegate bool ListElementBoolGetter(int index, SerializedProperty prop);

        public class Parameters
        {
            public ListElementHandler PreElement;
            public ListElementHandler OnElement;
            public ListElementHandler PostElement;
            public ListElementHandler OnAddedElement;
            public ListElementHandler OnRemovedElement;
            public ListElementHandler OnElementStart;
            public ListElementHandler OnElementEnd;
            public ListElementBoolGetter OnAddButton;
            public ListElementBoolGetter OnDeleteButton;
            public bool ButtonsAreHorizontal = true;
            public EditorListOption Options;
            public GUILayoutOption[] LayoutOptions;
        }

        private static GUIContent
            moveButtonContent = new GUIContent("\u21b4", "move down"),
            duplicateButtonContent = new GUIContent("+", "duplicate"),
            deleteButtonContent = new GUIContent("-", "delete"),
            addButtonContent = new GUIContent("+");//, "add element");

        private static GUILayoutOption miniButtonWidth = GUILayout.Width(20f);

        public static void Show(SerializedProperty list,
                                EditorListOption options = EditorListOption.Default,
                                ListElementHandler onElement = null)
        {
            Show(list, new Parameters { Options = options, OnElement = onElement });
        }

        public static void Show(SerializedProperty list,
                                Parameters parameters)
        {
            if (!list.isArray)
            {
                EditorGUILayout.HelpBox(list.name + " is neither an array nor a list!", MessageType.Error);
                return;
            }

            bool
                showListLabel = (parameters.Options & EditorListOption.ListLabel) != 0,
                showListSize = (parameters.Options & EditorListOption.ListSize) != 0;

            if (showListLabel)
            {
                EditorGUILayout.PropertyField(list);
                EditorGUI.indentLevel += 1;
            }
            if (!showListLabel || list.isExpanded)
            {
                SerializedProperty size = list.FindPropertyRelative("Array.size");
                if (showListSize)
                    EditorGUILayout.PropertyField(size);

                if (size.hasMultipleDifferentValues)
                    EditorGUILayout.HelpBox("Not showing lists with different sizes.", MessageType.Info);
                else
                    ShowElements(list, parameters);
            }

            if (showListLabel)
                EditorGUI.indentLevel -= 1;
        }

        private static void ShowElements(SerializedProperty list, Parameters parameters)
        {
            bool
                showElementLabels = (parameters.Options & EditorListOption.ElementLabels) != 0,
                showButtons = (parameters.Options & EditorListOption.Buttons) != 0;

            for (int i = 0; i < list.arraySize; i++)
            {
                if (parameters.OnElementStart != null)
                    parameters.OnElementStart(i, list.GetArrayElementAtIndex(i));

                if (showButtons && parameters.ButtonsAreHorizontal)
                    EditorGUILayout.BeginHorizontal();

                if (parameters.PreElement != null)
                    parameters.PreElement(i, list.GetArrayElementAtIndex(i));

                if (parameters.OnElement != null)
                    parameters.OnElement(i, list.GetArrayElementAtIndex(i));
                else
                {
                    if (showElementLabels)
                        EditorGUILayout.PropertyField(list.GetArrayElementAtIndex(i), parameters.LayoutOptions);
                    else
                        EditorGUILayout.PropertyField(list.GetArrayElementAtIndex(i), GUIContent.none, parameters.LayoutOptions);
                }

                if (parameters.PostElement != null)
                    parameters.PostElement(i, list.GetArrayElementAtIndex(i));

                if (showButtons)
                {
                    ShowButtons(list, i, parameters);
                    if (parameters.ButtonsAreHorizontal)
                        EditorGUILayout.EndHorizontal();
                }

                if (parameters.OnElementEnd != null && i < list.arraySize)
                    parameters.OnElementEnd(i, list.GetArrayElementAtIndex(i));
            }
            if (showButtons)
                DrawAddButton(list, parameters);
        }

        private static void DrawAddButton(SerializedProperty list, Parameters parameters)
        {
            //bool firstElementBtnCheck = list.arraySize == 0 &&
            //                            (parameters.OnAddButton != null
            //                                ? parameters.OnAddButton(list.arraySize - 1,
            //                                    list.GetArrayElementAtIndex(list.arraySize - 1))
            //                                : GUILayout.Button(addButtonContent, EditorStyles.miniButton,
            //                                    parameters.LayoutOptions));
            bool firstElementBtnCheck = list.arraySize == 0 &&
                                        GUILayout.Button(addButtonContent, EditorStyles.miniButton,
                                            parameters.LayoutOptions);
            if (firstElementBtnCheck)
            {
                list.arraySize += 1;
                if (parameters.OnAddedElement != null)
                    parameters.OnAddedElement(0, list.GetArrayElementAtIndex(0));
            }

            bool lastElementBtnCheck = list.arraySize > 0 &&
                                       (parameters.OnAddButton != null
                                           ? parameters.OnAddButton(list.arraySize - 1,
                                               list.GetArrayElementAtIndex(list.arraySize - 1))
                                           : GUILayout.Button(addButtonContent, EditorStyles.miniButton, parameters.LayoutOptions));
            if (lastElementBtnCheck)
            {
                list.InsertArrayElementAtIndex(list.arraySize - 1);
                var lastProp = list.GetArrayElementAtIndex(list.arraySize - 1);
                if (parameters.OnAddedElement != null)
                    parameters.OnAddedElement(list.arraySize - 1, lastProp);
            }
        }

        private static void ShowButtons(SerializedProperty list, int index, Parameters parameters)
        {
            //if (GUILayout.Button(moveButtonContent, EditorStyles.miniButtonLeft, miniButtonWidth))
            //    list.MoveArrayElement(index, index + 1);

            //if (GUILayout.Button(duplicateButtonContent, EditorStyles.miniButtonMid, miniButtonWidth))
            //    list.InsertArrayElementAtIndex(index);

            bool deleteBtnCheck = parameters.OnDeleteButton != null
                ? parameters.OnDeleteButton(index, list.GetArrayElementAtIndex(index))
                : GUILayout.Button(deleteButtonContent,
                    EditorStyles.miniButtonRight,
                    miniButtonWidth);
            if (deleteBtnCheck)
            {
                int oldSize = list.arraySize;
                list.DeleteArrayElementAtIndex(index);
                if (list.arraySize == oldSize)
                    list.DeleteArrayElementAtIndex(index);
                if (parameters.OnRemovedElement != null)
                    parameters.OnRemovedElement(index, null);
            }
        }
    }

}