#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine.InputSystem.Editor.Lists;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.UIElements;

namespace UnityEngine.InputSystem.Editor
{
    internal class NameAndParametersListView : ViewBase<InputActionsEditorState>
    {
        private readonly Func<InputActionsEditorState, IEnumerable<ParameterListView>> m_ParameterListViewSelector;
        private VisualElement m_ContentContainer;
        private readonly Label m_NoParamsLabel;

        private SerializedProperty m_ListProperty;

        public NameAndParametersListView(VisualElement root, StateContainer stateContainer, SerializedProperty listProperty,
                                         Func<InputActionsEditorState, IEnumerable<ParameterListView>> parameterListViewSelector)
            : base(root, stateContainer)
        {
            m_ListProperty = listProperty;
            m_NoParamsLabel = root.Q<Label>("no-parameters-added-label");
            m_ParameterListViewSelector = parameterListViewSelector;

            CreateSelector(state => state);
        }

        public void OnAddElement(string name)
        {
            if (m_ListProperty == null)
                return;

            var interactionsOrProcessorsList = NameAndParameters.ParseMultiple(m_ListProperty.stringValue).ToList();
            var newElement = new NameAndParameters() { name = name};
            interactionsOrProcessorsList.Add(newElement);

            m_ListProperty.stringValue = NameAndParameters.ToSerializableString(interactionsOrProcessorsList);
            m_ListProperty.serializedObject.ApplyModifiedProperties();
        }

        private void MoveElementUp(int index)
        {
            var newIndex = index - 1;
            SwapElement(index, newIndex);
        }

        private void MoveElementDown(int index)
        {
            var newIndex = index + 1;
            SwapElement(index, newIndex);
        }

        private void SwapElement(int oldIndex, int newIndex)
        {
            var interactionsOrProcessors = NameAndParameters.ParseMultiple(m_ListProperty.stringValue).ToArray();
            var oldIndexIsValid = oldIndex >= 0 && oldIndex < interactionsOrProcessors.Length;
            var newIndexIsValid = newIndex >= 0 && newIndex < interactionsOrProcessors.Length;
            if (interactionsOrProcessors.Length == 0 || !newIndexIsValid || !oldIndexIsValid)
                return;
            MemoryHelpers.Swap(ref interactionsOrProcessors[oldIndex], ref interactionsOrProcessors[newIndex]);
            m_ListProperty.stringValue = NameAndParameters.ToSerializableString(interactionsOrProcessors);
            m_ListProperty.serializedObject.ApplyModifiedProperties();
        }

        private void DeleteElement(int index)
        {
            var interactionsOrProcessorsList = NameAndParameters.ParseMultiple(m_ListProperty.stringValue).ToList();
            interactionsOrProcessorsList.RemoveAt(index);
            m_ListProperty.stringValue = NameAndParameters.ToSerializableString(interactionsOrProcessorsList);
            m_ListProperty.serializedObject.ApplyModifiedProperties();
        }

        private void OnParametersChanged(ParameterListView listView, int index)
        {
            var interactionsOrProcessorsList = NameAndParameters.ParseMultiple(m_ListProperty.stringValue).ToList();
            interactionsOrProcessorsList[index] = new NameAndParameters { name = interactionsOrProcessorsList[index].name, parameters = listView.GetParameters() };
            m_ListProperty.stringValue = NameAndParameters.ToSerializableString(interactionsOrProcessorsList);
            m_ListProperty.serializedObject.ApplyModifiedProperties();
        }

        public override void RedrawUI(InputActionsEditorState state)
        {
            if (m_ContentContainer != null)
                rootElement.Remove(m_ContentContainer);

            m_ContentContainer = new VisualElement();
            rootElement.Add(m_ContentContainer);

            var parameterListViews = m_ParameterListViewSelector(state).ToList();
            if (parameterListViews.Count == 0)
            {
                m_NoParamsLabel.style.display = new StyleEnum<DisplayStyle>(DisplayStyle.Flex);
                return;
            }

            m_NoParamsLabel.style.display = new StyleEnum<DisplayStyle>(DisplayStyle.None);
            m_ContentContainer.Clear();
            for (int i = 0; i < parameterListViews.Count; i++)
            {
                var index = i;
                var buttonProperties = new ButtonProperties()
                {
                    onClickDown = () => MoveElementDown(index),
                    onClickUp = () => MoveElementUp(index),
                    onDelete = () => DeleteElement(index),
                    isDownButtonActive = index < parameterListViews.Count - 1,
                    isUpButtonActive = index > 0
                };
                new NameAndParametersListViewItem(m_ContentContainer, parameterListViews[i], buttonProperties);
                parameterListViews[i].onChange += () => OnParametersChanged(parameterListViews[index], index);
            }
        }

        public override void DestroyView()
        {
            if (m_ContentContainer != null)
            {
                rootElement.Remove(m_ContentContainer);
                m_ContentContainer = null;
            }
        }
    }

    internal struct ButtonProperties
    {
        public Action onClickUp;
        public Action onClickDown;
        public Action onDelete;
        public bool isUpButtonActive;
        public bool isDownButtonActive;
    }

    internal class NameAndParametersListViewItem
    {
        public NameAndParametersListViewItem(VisualElement root, ParameterListView parameterListView, ButtonProperties buttonProperties)
        {
            var itemTemplate = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
                InputActionsEditorConstants.PackagePath +
                InputActionsEditorConstants.ResourcesPath +
                InputActionsEditorConstants.NameAndParametersListViewItemUxml);

            var container = itemTemplate.CloneTree();
            root.Add(container);

            var header = container.Q<Toggle>();

            var moveItemUpButton = new Button();
            moveItemUpButton.AddToClassList(EditorGUIUtility.isProSkin ? "upDarkTheme" : "up");
            moveItemUpButton.AddToClassList("name-and-parameters-list-foldout-button");
            moveItemUpButton.SetEnabled(buttonProperties.isUpButtonActive);
            moveItemUpButton.clicked += buttonProperties.onClickUp;

            var moveItemDownButton = new Button();
            moveItemDownButton.AddToClassList(EditorGUIUtility.isProSkin ? "downDarkTheme" : "down");
            moveItemDownButton.AddToClassList("name-and-parameters-list-foldout-button");
            moveItemDownButton.SetEnabled(buttonProperties.isDownButtonActive);
            moveItemDownButton.clicked += buttonProperties.onClickDown;

            var deleteItemButton = new Button();
            deleteItemButton.AddToClassList(EditorGUIUtility.isProSkin ? "deleteDarkTheme" : "delete");
            deleteItemButton.AddToClassList("name-and-parameters-list-foldout-button");
            deleteItemButton.clicked += buttonProperties.onDelete;

            header.Add(moveItemUpButton);
            header.Add(moveItemDownButton);
            header.Add(deleteItemButton);

            var foldout = container.Q<Foldout>("Foldout");
            foldout.text = parameterListView.name;
            parameterListView.OnDrawVisualElements(foldout);

            foldout.Add(new IMGUIContainer(parameterListView.OnGUI));
        }
    }
}

#endif