#if ENABLE_ADDRESSABLE_PROFILER && UNITY_2022_2_OR_NEWER

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

namespace UnityEditor.AddressableAssets.Diagnostics
{
    internal class AddressablesProfilerDetailsTreeView
    {
        private const string k_ColumnVisibilityKeyPrefix = "com.unity.addressables.profiler.treeviewcolumn.";

        public AddressablesProfilerDetailsTreeView(TreeViewPane rootView)
        {
            m_TreeView = rootView.TreeView;
            if (m_TreeView == null)
            {
                Debug.LogError("Could not find treeView for ProfilerDetails view");
                return;
            }

            for (int i = 0; i < 32; ++i)
                m_LabelCache.Enqueue(new Label());

            m_LastColumnVisibles = new bool[m_TreeView.columns.Count];
            for (int i=0; i<m_TreeView.columns.Count; ++i)
            {
                Column col = m_TreeView.columns[i];
                col.sortable = true;
                col.visible = EditorPrefs.GetBool(k_ColumnVisibilityKeyPrefix + col.name, col.visible);
                m_LastColumnVisibles[i] = col.visible;
                if (col.name == TreeColumnNames.TreeColumnName)
                {
                    col.makeCell = MakeItem;
                    col.destroyCell = DestroyItem;
                    col.bindCell += BindName;
                }
                else
                {
                    col.makeCell = MakeLabel;
                    col.destroyCell = DestroyLabel;

                    if (col.name == TreeColumnNames.TreeColumnAddressedCount)
                        col.bindCell += BindAddressedCount;
                    else if (col.name == TreeColumnNames.TreeColumnType)
                        col.bindCell += BindType;
                    else if (col.name == TreeColumnNames.TreeColumnStatus)
                        col.bindCell += BindStatus;
                    else if (col.name == TreeColumnNames.TreeColumnPercentage)
                        col.bindCell += BindPercentage;
                    else if (col.name == TreeColumnNames.TreeColumnBundleSource)
                        col.bindCell += BindSource;
                    else if (col.name == TreeColumnNames.TreeColumnReferencedBy)
                        col.bindCell += BindReferencedBy;
                    else if (col.name == TreeColumnNames.TreeColumnReferencesTo)
                        col.bindCell += BindReferencesTo;
                }
            }

            m_TreeView.selectionChanged += TreeViewOnSelectionChanged;
        }

        public void Initialise(AddressablesProfilerDetailsDataInspector inspector)
        {
            m_DetailsInspector = inspector;
        }

        public void SetSortingChangeCallback(System.Action callback)
        {
            m_TreeView.columnSortingChanged += callback;
        }

        public SortColumnDescriptions SortDescriptions => m_TreeView.sortColumnDescriptions;

        private readonly MultiColumnTreeView m_TreeView;
        private int? m_SelectedId = null;

        private bool[] m_LastColumnVisibles;
        private readonly Queue<VisualElement> m_VisualsCache = new Queue<VisualElement>(32);
        private readonly Queue<Label> m_LabelCache = new Queue<Label>(32);
        private AddressablesProfilerDetailsDataInspector m_DetailsInspector;

        private void DestroyItem(VisualElement e)
        {
            m_VisualsCache.Enqueue(e);
        }

        private VisualElement MakeItem()
        {
            return m_VisualsCache.Count == 0 ? new AssetLabel(0, false) : m_VisualsCache.Dequeue();
        }

        private void DestroyLabel(VisualElement e)
        {
            m_LabelCache.Enqueue(e as Label);
        }

        private VisualElement MakeLabel()
        {
            Label lbl = m_LabelCache.Count == 0 ? new Label() : m_LabelCache.Dequeue();
            lbl.style.alignSelf = new StyleEnum<Align>(Align.FlexEnd);
            return lbl;
        }

        private void BindName(VisualElement element, int arg2)
        {
            ContentData contentData = m_TreeView.GetItemDataForIndex<ContentData>(arg2);
            (element as AssetLabel)?.SetContent(contentData);
        }

        private void BindType(VisualElement arg1, int arg2) {
            Bind(TreeColumnNames.TreeColumnType, arg1 as Label, arg2); }
        private void BindAddressedCount(VisualElement arg1, int arg2) {
            Bind(TreeColumnNames.TreeColumnAddressedCount, arg1 as Label, arg2); }
        private void BindReferencedBy(VisualElement arg1, int arg2) {
            Bind(TreeColumnNames.TreeColumnReferencedBy, arg1 as Label, arg2); }
        private void BindStatus(VisualElement arg1, int arg2) {
            Bind(TreeColumnNames.TreeColumnStatus, arg1 as Label, arg2); }
        private void BindPercentage(VisualElement arg1, int arg2) {
            Bind(TreeColumnNames.TreeColumnPercentage, arg1 as Label, arg2); }
        private void BindReferencesTo(VisualElement arg1, int arg2) {
            Bind(TreeColumnNames.TreeColumnReferencesTo, arg1 as Label, arg2); }

        private void Bind(string columnName, Label lbl, int itemIndex)
        {
            ContentData data = m_TreeView.GetItemDataForIndex<ContentData>(itemIndex);
            if (lbl == null)
                return;
            if (data == null)
            {
                lbl.text = "ERROR";
                return;
            }

            lbl.text = data.GetCellContent(columnName);
        }

        private void BindSource(VisualElement arg1, int arg2)
        {
            Label lbl = arg1 as Label;
            ContentData data = m_TreeView.GetItemDataForIndex<ContentData>(arg2);
            if (lbl == null || data == null )
            {
                lbl.text = "ERROR";
                return;
            }

            if (data is BundleData bData)
                lbl.text = bData.Source.ToString();
            else
                lbl.text = "";
        }

        public void SetRootItemsAndRebuild(IList<TreeViewItemData<ContentData>> rootItems)
        {
            m_TreeView.SetRootItems(rootItems);
            m_TreeView.Rebuild();

            if (m_SelectedId.HasValue)
            {
                var data = m_TreeView.GetItemDataForId<ContentData>(m_SelectedId.Value);
                if (data != null) // if data not found, then the item is not in the new frame
                    m_TreeView.SetSelectionById(m_SelectedId.Value);
                else
                    SingleSelection(null);
            }
        }

        private void TreeViewOnSelectionChanged(IEnumerable<object> obj)
        {
            // if m_TreeView.selectionType = SelectionType.Multiple, then can support multiple selection
            // currently static as Single
            foreach (object o in obj)
            {
                SingleSelection(o as ContentData);
                break;
            }
        }

        private void SingleSelection(ContentData selectedData)
        {
            m_DetailsInspector.SetSourceContent(selectedData);
            if (selectedData != null)
                m_SelectedId = selectedData.TreeViewID;
        }

        public void SaveColumnVisibility()
        {
            for (int i = 0; i < m_TreeView.columns.Count; ++i)
            {
                if (m_TreeView.columns[i].visible != m_LastColumnVisibles[i])
                {
                    m_LastColumnVisibles[i] = m_TreeView.columns[i].visible;
                    EditorPrefs.SetBool(k_ColumnVisibilityKeyPrefix + m_TreeView.columns[i].name, m_TreeView.columns[i].visible);
                }
            }
        }
    }
}

#endif