// Copyright (c) Wojciech Figat. All rights reserved.

using System.Collections.Generic;
using FlaxEditor.Content;
using FlaxEditor.Content.GUI;
using FlaxEditor.GUI;
using FlaxEditor.GUI.Tree;

namespace FlaxEditor.Windows
{
    public partial class ContentWindow
    {
        private static readonly List<ContentFolderTreeNode> NavUpdateCache = new List<ContentFolderTreeNode>(8);

        private void OnTreeSelectionChanged(List<TreeNode> from, List<TreeNode> to)
        {
            bool setLastViewFolder = !IsLayoutLocked;
            if (!_showAllContentInTree && to.Count > 1)
            {
                _tree.Select(to[^1]);
                return;
            }
            if (_showAllContentInTree && to.Count > 1)
            {
                if (setLastViewFolder)
                {
                    var activeNode = GetActiveTreeSelection(to);
                    if (activeNode is ContentItemTreeNode itemNode)
                        SaveLastViewedFolder(itemNode.Item?.ParentFolder?.Node);
                    else
                        SaveLastViewedFolder(activeNode as ContentFolderTreeNode);
                }
                UpdateUI();
                return;
            }

            // Navigate
            var source = from.Count > 0 ? from[0] as ContentFolderTreeNode : null;
            var targetNode = GetActiveTreeSelection(to);
            if (targetNode is ContentItemTreeNode itemNode2)
            {
                if (setLastViewFolder)
                    SaveLastViewedFolder(itemNode2.Item?.ParentFolder?.Node);
                UpdateUI();
                itemNode2.Focus();
                return;
            }

            var target = targetNode as ContentFolderTreeNode;
            Navigate(source, target);

            if (setLastViewFolder)
                SaveLastViewedFolder(target);
            target?.Focus();
        }

        /// <summary>
        /// Navigates to the specified target content location.
        /// </summary>
        /// <param name="target">The target.</param>
        public void Navigate(ContentFolderTreeNode target)
        {
            Navigate(SelectedNode, target);
        }

        private void Navigate(ContentFolderTreeNode source, ContentFolderTreeNode target)
        {
            if (target == null)
                target = _root;

            // Check if can do this action
            if (_navigationUnlocked && source != target)
            {
                // Lock navigation
                _navigationUnlocked = false;

                // Check if already added to the Undo on the top
                if (source != null && (_navigationUndo.Count == 0 || _navigationUndo.Peek() != source))
                {
                    // Add to Undo list
                    _navigationUndo.Push(source);
                }

                // Clear redo list
                _navigationRedo.Clear();

                DoNavigate(target);
            }
        }

        /// <summary>
        /// Navigates backward.
        /// </summary>
        public void NavigateBackward()
        {
            // Check if navigation is unlocked
            if (_navigationUnlocked && _navigationUndo.Count > 0)
            {
                // Pop node
                ContentFolderTreeNode node = _navigationUndo.Pop();

                // Lock navigation
                _navigationUnlocked = false;

                // Add to Redo list
                _navigationRedo.Push(SelectedNode);

                DoNavigate(node);
                if (!_showAllContentInTree)
                    _view.SelectFirstItem();
            }
        }

        /// <summary>
        /// Navigates forward.
        /// </summary>
        public void NavigateForward()
        {
            // Check if navigation is unlocked
            if (_navigationUnlocked && _navigationRedo.Count > 0)
            {
                // Pop node
                ContentFolderTreeNode node = _navigationRedo.Pop();

                // Lock navigation
                _navigationUnlocked = false;

                // Add to Undo list
                _navigationUndo.Push(SelectedNode);

                DoNavigate(node);
                if (!_showAllContentInTree)
                    _view.SelectFirstItem();
            }
        }

        /// <summary>
        /// Navigates directory up.
        /// </summary>
        public void NavigateUp()
        {
            ContentFolderTreeNode target = _root;
            ContentFolderTreeNode current = SelectedNode;

            if (current?.Folder.ParentFolder != null)
            {
                target = current.Folder.ParentFolder.Node;
            }

            Navigate(target);
        }

        /// <summary>
        /// Clears the navigation history.
        /// </summary>
        public void NavigationClearHistory()
        {
            _navigationUndo.Clear();
            _navigationRedo.Clear();
            UpdateUI();
        }

        private void DoNavigate(ContentFolderTreeNode node)
        {
            // Select node
            if (!_showAllContentInTree)
                RefreshView(node);
            _tree.Select(node);
            node.ExpandAllParents();

            // Set valid sizes for stacks
            //RedoList.SetSize(32);
            //UndoList.SetSize(32);

            // Update search
            if (!_showAllContentInTree)
                UpdateItemsSearch();

            // Unlock navigation
            _navigationUnlocked = true;

            UpdateUI();

            // Clear auto-select cache for new/imported files
            _newFilesCache?.Clear();
            _newFilesCacheSize = 0;
        }

        private void UpdateNavigationBar()
        {
            if (_navigationBar == null)
                return;

            bool wasLayoutLocked = _navigationBar.IsLayoutLocked;
            _navigationBar.IsLayoutLocked = true;

            // Remove previous buttons
            _navigationBar.DisposeChildren();

            // Spawn buttons
            var nodes = NavUpdateCache;
            nodes.Clear();
            ContentFolderTreeNode node = SelectedNode;
            while (node != null)
            {
                nodes.Add(node);
                node = node.ParentNode;
            }
            float margin = 1;
            float x = NavigationBar.DefaultButtonsMargin;
            float h = _toolStrip.ItemsHeight - 2 * margin;
            for (int i = nodes.Count - 1; i >= 0; i--)
            {
                var button = new ContentNavigationButton(nodes[i], x, margin, h);
                button.PerformLayout();
                x += button.Width + NavigationBar.DefaultButtonsMargin;
                _navigationBar.AddChild(button);
                if (i > 0)
                {
                    var separator = new ContentNavigationSeparator(button, x, margin, h);
                    separator.PerformLayout();
                    x += separator.Width + NavigationBar.DefaultButtonsMargin;
                    _navigationBar.AddChild(separator);
                }
            }
            nodes.Clear();

            // Update
            _navigationBar.IsLayoutLocked = wasLayoutLocked;
            _navigationBar.PerformLayout();
            UpdateNavigationBarBounds();
        }

        /// <summary>
        /// Gets the selected tree node.
        /// </summary>
        public ContentFolderTreeNode SelectedNode
        {
            get
            {
                var selected = GetActiveTreeSelection(_tree.Selection);
                if (selected is ContentItemTreeNode itemNode)
                    return itemNode.Parent as ContentFolderTreeNode;
                return selected as ContentFolderTreeNode;
            }
        }

        /// <summary>
        /// Gets the current view folder.
        /// </summary>
        public ContentFolder CurrentViewFolder => SelectedNode?.Folder;

        private TreeNode GetActiveTreeSelection(List<TreeNode> selection)
        {
            if (selection == null || selection.Count == 0)
                return null;
            return _showAllContentInTree && selection.Count > 1
                ? selection[^1]
                : selection[0];
        }

        /// <summary>
        /// Shows the root folder.
        /// </summary>
        public void ShowRoot()
        {
            _tree.Select(_root);
        }

        private void SaveLastViewedFolder(ContentFolderTreeNode node)
        {
            Editor.ProjectCache.SetCustomData(ProjectDataLastViewedFolder, node?.Path ?? string.Empty);
        }
    }
}
