using System;
using System.Collections.Generic;
using System.Text;
using UnityEditor.Categorization;
using UnityEditor.Rendering.Converter;
using UnityEngine;
using UnityEngine.UIElements;

namespace UnityEditor.Rendering.Converter
{
    internal class RenderPipelineConverterVisualElement : VisualElement
    {
        const string k_Uxml = "Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/Window/RenderPipelineConverterVisualElement.uxml";
        const string k_Uss = "Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Tools/Converter/Window/RenderPipelineConverterVisualElement.uss";

        static Lazy<VisualTreeAsset> s_VisualTreeAsset = new Lazy<VisualTreeAsset>(() => AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(k_Uxml));
        static Lazy<StyleSheet> s_StyleSheet = new Lazy<StyleSheet>(() => AssetDatabase.LoadAssetAtPath<StyleSheet>(k_Uss));

        Node<ConverterInfo> m_ConverterInfo;

        public string displayName => m_ConverterInfo.name;
        public string description => m_ConverterInfo.description;

        public ConverterState state => m_ConverterInfo.data.state;
        public IRenderPipelineConverter converter => m_ConverterInfo.data.converter as IRenderPipelineConverter;

        public bool isSelectedAndEnabled => converter.isEnabled && state.isSelected;

        VisualElement m_RootVisualElement;
        ListView m_ItemsList;
        HeaderFoldout m_HeaderFoldout;
        VisualElement m_ListViewHeader;
        HelpBox m_NoItemsFound;
        HelpBox m_PressScan;
        Label m_PendingLabel;
        Label m_WarningLabel;
        Label m_ErrorLabel;
        Label m_SuccessLabel;

        public Action showMoreInfo; // TODO Remove with the UX rework
        public Action converterSelected;

        public RenderPipelineConverterVisualElement(Node<ConverterInfo> converterInfo)
        {
            m_ConverterInfo = converterInfo;

            m_RootVisualElement = new VisualElement();
            s_VisualTreeAsset.Value.CloneTree(m_RootVisualElement);
            m_RootVisualElement.styleSheets.Add(s_StyleSheet.Value);

            m_HeaderFoldout = m_RootVisualElement.Q<HeaderFoldout>("conveterFoldout");
            m_HeaderFoldout.text = displayName;
            m_HeaderFoldout.tooltip = (converter.isEnabled) ? description : converter.isDisabledMessage;
            m_HeaderFoldout.SetEnabled(converter.isEnabled);
            m_HeaderFoldout.value = state.isExpanded;
            m_HeaderFoldout.RegisterCallback<ChangeEvent<bool>>((evt) =>
            {
                state.isExpanded = evt.newValue;
            });
            m_HeaderFoldout.showEnableCheckbox = true;
            m_HeaderFoldout.documentationURL = converterInfo.helpUrl;

            m_HeaderFoldout.enableToggle.SetValueWithoutNotify(state.isSelected);
            m_HeaderFoldout.enableToggle.RegisterCallback<ClickEvent>((evt) =>
            {
                state.isSelected = !state.isSelected;
                converterSelected?.Invoke();
                UpdateConversionInfo();
            });

            var allLabel = m_RootVisualElement.Q<Label>("all");
            allLabel.RegisterCallback<ClickEvent>((evt) =>
            {
                SetItemsActive(true);
                Refresh();
            });
            var noneLabel = m_RootVisualElement.Q<Label>("none");
            noneLabel.RegisterCallback<ClickEvent>((evt) =>
            {
                SetItemsActive(false);
                Refresh();
            });

            m_ListViewHeader = m_RootVisualElement.Q("listViewHeader");
            m_ItemsList = m_RootVisualElement.Q<ListView>("converterItems");
            m_ItemsList.showBoundCollectionSize = false;
            m_ItemsList.makeItem = () =>
            {
                var item = new RenderPipelineConverterItemVisualElement();
                item.itemSelectionChanged += UpdateInfo;
                return item;
            };
            m_ItemsList.bindItem = (element, index) =>
            {
                var item = element as RenderPipelineConverterItemVisualElement;
                item.Bind(state.items[index]);
            };
            m_ItemsList.selectionChanged += obj =>
            {
                state.items[m_ItemsList.selectedIndex].item.OnClicked();
            };

            // setup the images
            m_RootVisualElement.Q<Image>("pendingImage").image = CoreEditorStyles.iconPending;
            m_RootVisualElement.Q<Image>("pendingImage").tooltip = "Pending";
            m_RootVisualElement.Q<Image>("warningImage").image = CoreEditorStyles.iconWarn;
            m_RootVisualElement.Q<Image>("warningImage").tooltip = "Warnings";
            m_RootVisualElement.Q<Image>("errorImage").image = CoreEditorStyles.iconFail;
            m_RootVisualElement.Q<Image>("errorImage").tooltip = "Failed";
            m_RootVisualElement.Q<Image>("successImage").image = CoreEditorStyles.iconComplete;
            m_RootVisualElement.Q<Image>("successImage").tooltip = "Success";

            // Store labels to easy update afterwards
            m_PendingLabel = m_RootVisualElement.Q<Label>("pendingLabel");
            m_WarningLabel = m_RootVisualElement.Q<Label>("warningLabel");
            m_ErrorLabel = m_RootVisualElement.Q<Label>("errorLabel");
            m_SuccessLabel = m_RootVisualElement.Q<Label>("successLabel");

            m_NoItemsFound = m_RootVisualElement.Q<HelpBox>("noItemsFoundHelpBox");
            m_NoItemsFound.style.display = DisplayStyle.None;

            m_PressScan = m_RootVisualElement.Q<HelpBox>("pressScanHelpBox");

            Add(m_RootVisualElement);
            Refresh();
        }

        private void SetItemsActive(bool value)
        {
            foreach (var itemState in state.items)
                itemState.isSelected = value;
        }

        public void UpdateInfo()
        {
            UpdateConversionInfo();
            UpdateSelectedConverterItemsLabel();
            UpdateAllNoneLabels();
        }

        void UpdateSelectedConverterItemsLabel()
        {
            var text = $" ({state.selectedItemsCount} of {state.items.Count})";
            m_RootVisualElement.Q<Label>("converterStats").text = text;
        }

        void UpdateAllNoneLabels()
        {
            var allLabel = m_RootVisualElement.Q<Label>("all");
            var noneLabel = m_RootVisualElement.Q<Label>("none");

            var count = state.items.Count;
            int selectedCount = state.selectedItemsCount;

            bool noneSelected = selectedCount == 0;
            bool allSelected = selectedCount == count;
            SetSelected(allLabel, allSelected);
            SetSelected(noneLabel, noneSelected);
        }

        void SetSelected(Label label, bool selected)
        {
            if (selected)
            {
                label.RemoveFromClassList("not_selected");
                label.AddToClassList("selected");
            }
            else
            {
                label.RemoveFromClassList("selected");
                label.AddToClassList("not_selected");
            }
        }

        void UpdateConversionInfo()
        {
            if (state.isInitialized)
            {
                m_PressScan.style.display = DisplayStyle.None;
                if (state.items.Count > 0)
                {
                    m_NoItemsFound.style.display = DisplayStyle.None;
                    m_ListViewHeader.style.display = DisplayStyle.Flex;
                    m_ItemsList.style.display = DisplayStyle.Flex;
                    m_ItemsList.itemsSource = state.items;

                    m_PendingLabel.text = $"{state.pending}";
                    m_WarningLabel.text = $"{state.warnings}";
                    m_ErrorLabel.text = $"{state.errors}";
                    m_SuccessLabel.text = $"{state.success}";
                }
                else
                {
                    m_NoItemsFound.style.display = DisplayStyle.Flex;
                    m_ListViewHeader.style.display = DisplayStyle.None;
                    m_ItemsList.style.display = DisplayStyle.None;
                }
            }
            else
            {
                m_PressScan.style.display = DisplayStyle.Flex;

                m_NoItemsFound.style.display = DisplayStyle.None;
                m_ListViewHeader.style.display = DisplayStyle.None;
                m_ItemsList.style.display = DisplayStyle.None;
            }
        }

        public void Refresh()
        {
            m_ItemsList.Rebuild();
            UpdateInfo();
            m_HeaderFoldout.SetEnabled(converter.isEnabled);
        }

        public void Scan(Action onScanFinish)
        {
            state.Clear();
            state.isLoading = true;
            converter.Scan(OnConverterCompleteDataCollection);

            void OnConverterCompleteDataCollection(List<IRenderPipelineConverterItem> items)
            {
                // Set the item infos list to to the right index
                state.items = new List<ConverterItemState>(items.Count);

                foreach(var item in items)
                {
                    var converterItemState = new ConverterItemState()
                    {
                        item = item,
                        isSelected = true, // Default all the entries to true
                    };

                    state.items.Add(converterItemState);
                }

                state.isLoading = false;
                state.isInitialized = true;
                m_HeaderFoldout.value = true; // Expand the foldout when we perform a search
                m_ItemsList.itemsSource = state.items;

                Refresh();
                onScanFinish?.Invoke();
            }
        }

        public void Convert(string progressTitle)
        {
            if (state.pending == 0)
            {
                Debug.Log($"Skipping conversion, {displayName} has no pending items to convert.");
                return;
            }

            var sb = new StringBuilder($"Conversion results for item: {displayName}:{Environment.NewLine}");

            converter.BeforeConvert();
            int itemIndex = 0;
            int itemToConvertIndex = 0;
            foreach (var itemState in state.items)
            {
                if (EditorUtility.DisplayCancelableProgressBar(progressTitle,
                    $"({itemToConvertIndex} of {state.pending}) {itemState.item.name}",
                    itemToConvertIndex / (float)state.pending))
                    break;

                if (!itemState.hasConverted && itemState.isSelected)
                {
                    try
                    {
                        var status = converter.Convert(itemState.item, out var message);
                        switch (status)
                        {
                            case Status.Pending:
                                throw new InvalidOperationException("Converter returned a pending status when converting. This is not supported.");
                            case Status.Error:
                            case Status.Warning:
                                sb.AppendLine($"- {itemState.item.name} ({status}) ({message})");
                                break;
                            case Status.Success:
                            {
                                sb.AppendLine($"- {itemState.item.name} ({status})");
                                message = "Conversion successful!";
                            }
                                break;
                        }

                        itemState.conversionResult.Status = status;
                        itemState.conversionResult.Message = message;
                    }
                    catch(Exception ex)
                    {
                        Debug.LogError($"Exception {ex.Message} while converting {itemState.item.name} from {displayName}");
                    }
                    itemToConvertIndex++;
                }
                itemIndex++;
            }
            converter.AfterConvert();

            Refresh();

            sb.AppendLine(state.ToString());
            Debug.Log(sb);
        }
    }
}