using BookmarkManager;
using bzit.bomg.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Resources = BookmarkManager.AppResource;

namespace RyzStudio.Windows.Forms
{
    public partial class BookmarkTreeView : TreeView
    {
        public static string DecodePath(string value) => System.Web.HttpUtility.UrlDecode(value);

        public static void DeleteNode(TreeNode node)
        {
            if (node == null)
            {
                return;
            }

            if (node.TreeView.Nodes.Count <= 0)
            {
                return;
            }

            if (node.Equals(node.TreeView.Nodes[0]))
            {
                return;
            }

            node.Remove();
        }

        public static string EncodePath(string value)
        {
            return System.Web.HttpUtility.UrlEncode(value);
        }

        public static BookmarkItem GetNodeModel(TreeNode node)
        {
            if (node == null)
            {
                return null;
            }

            return (BookmarkItem)node.Tag;
        }

        public static string GetNodePath(TreeNode node)
        {
            string[] folderList = node.FullPath.Split('\n');
            if (folderList.Length < 2)
            {
                return null;
            }

            //if (folderList.Length < 2)
            //{
            //    return "/";
            //}

            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < (folderList.Length - 1); i++)
            {
                sb.Append("\\");
                sb.Append(EncodePath(folderList[i] ?? string.Empty));
            }

            sb.Append("\\");

            return sb.ToString();
        }

        public static NodeType GetNodeType(TreeNode node)
        {
            if (node.Tag == null)
            {
                if (node.Equals(node.TreeView.Nodes[0]))
                {
                    return NodeType.Root;
                }
                else
                {
                    return NodeType.Folder;
                }
            }
            else
            {
                if (node.Tag is BookmarkItem)
                {
                    return NodeType.Page;
                }
                else
                {
                    return NodeType.None;
                }
            }
        }

        public static void MoveDown(TreeNode node)
        {
            if (node == null)
            {
                return;
            }

            TreeNode tn = node;
            if (tn.Parent == null)
            {
                return;
            }

            TreeNode tn1 = tn.Parent;

            if (tn.Index >= (tn1.Nodes.Count - 1))
            {
                return;
            }

            int n = tn.Index + 1;

            tn1.Nodes.Remove(tn);
            tn1.Nodes.Insert(n, tn);

            node.TreeView.SelectedNode = tn;
        }

        public static void MoveUp(TreeNode node)
        {
            if (node == null)
            {
                return;
            }

            TreeNode tn = node;
            if (tn.Parent == null)
            {
                return;
            }

            if (tn.Index <= 0)
            {
                return;
            }

            int n = tn.Index - 1;

            TreeNode tn1 = tn.Parent;
            tn1.Nodes.Remove(tn);
            tn1.Nodes.Insert(n, tn);

            node.TreeView.SelectedNode = tn;
        }

        public static void Sort(TreeNode node)
        {
            if (node == null)
            {
                return;
            }

            string[] tnv = new string[0];
            TreeNode[] tna = new TreeNode[0];

            foreach (TreeNode tn2 in node.Nodes)
            {
                Array.Resize(ref tna, (tna.Length + 1));
                tna[(tna.Length - 1)] = tn2;

                Array.Resize(ref tnv, (tnv.Length + 1));
                tnv[(tnv.Length - 1)] = tn2.Text;
            }

            Array.Sort(tnv, tna);

            node.Nodes.Clear();

            foreach (TreeNode tn2 in tna)
            {
                node.Nodes.Add(tn2);
            }
        }


        public enum IconSet
        {
            Root = 0,
            Folder1,
            Folder2,
            Default
        }

        public enum NodeType
        {
            None = 0,
            Root,
            Folder,
            Page
        }


        protected const string DEFAULT_NEW_FOLDER_NAME = "New Folder";
        protected const string PATH_SEPARATOR = "\n";

        public event EventHandler OnNodeChanged = null;

        protected TreeNode draggingNode = null;
        protected bool hasChanged = false;


        public BookmarkTreeView()
        {
            if (this.ImageList == null) this.ImageList = new ImageList();
            this.PathSeparator = PATH_SEPARATOR;

            this.AllowDrop = true;
            this.HideSelection = false;
            this.HotTracking = true;
            this.ImageIndex = 0;
            this.LabelEdit = true;
            this.PathSeparator = "\n";
            this.SelectedImageIndex = 0;
            this.ShowNodeToolTips = true;

            this.ImageList.ColorDepth = ColorDepth.Depth32Bit;
            this.ImageList.ImageSize = new Size(16, 16);
            this.ImageList.TransparentColor = Color.Transparent;

            ResetImageList();
        }


#region integrated behaviour

        protected override void OnItemDrag(ItemDragEventArgs e)
        {
            base.OnItemDrag(e);

            draggingNode = (TreeNode)e.Item;
            DoDragDrop(e.Item, DragDropEffects.Move);
        }

        protected override void OnDragDrop(DragEventArgs e)
        {
            base.OnDragDrop(e);

            if (draggingNode.Level <= 0)
            {
                return;
            }

            TreeNode node = this.GetNodeAt(this.PointToClient(new Point(e.X, e.Y)));
            if (node == null)
            {
                return;
            }

            if (ThreadControl.IsChild(draggingNode, node))
            {
                return;
            }

            TreeNode dn = draggingNode;
            if (node.Tag == null)
            {
                dn.Parent.Nodes.Remove(dn);
                node.Nodes.Insert(0, dn);
            }
            else
            {
                node.Parent.Nodes.Remove(dn);
                node.Parent.Nodes.Insert(node.Index + 1, dn);
            }

            this.HasChanged = true;
        }

        protected override void OnDragEnter(DragEventArgs e)
        {
            base.OnDragEnter(e);

            e.Effect = DragDropEffects.Move;
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            this.SelectedNode = this.GetNodeAt(e.Location);
        }

        protected override void OnDragOver(DragEventArgs e)
        {
            base.OnDragOver(e);

            this.SelectedNode = this.GetNodeAt(this.PointToClient(new Point(e.X, e.Y)));
        }

        protected override void OnBeforeLabelEdit(NodeLabelEditEventArgs e)
        {
            if (!AllowBeginEdit)
            {
                AllowBeginEdit = false;

                e.CancelEdit = true;
                return;
            }

            this.HasChanged = true;

            base.OnBeforeLabelEdit(e);
        }

        protected override void OnAfterLabelEdit(NodeLabelEditEventArgs e)
        {
            base.OnAfterLabelEdit(e);

            //if (e.Node.Tag == null)
            //{
            //    if (e.Label == null)
            //    {
            //        e.CancelEdit = true;
            //    }
            //    else
            //    {
            //        if (e.Label.Trim().Length <= 0)
            //        {
            //            e.CancelEdit = true;
            //        }
            //    }
            //}
            //else
            //{
            //    e.CancelEdit = true;
            //}

            AllowBeginEdit = false;
        }

        protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                switch (this.GetNodeType())
                {
                    case BookmarkTreeView.NodeType.Root:
                        if (this.RootContextMenu != null) this.RootContextMenu.Show(e.Node.TreeView, e.X, e.Y);
                        break;
                    case BookmarkTreeView.NodeType.Folder:
                        if (this.FolderContextMenu != null) this.FolderContextMenu.Show(e.Node.TreeView, e.X, e.Y);
                        break;
                    case BookmarkTreeView.NodeType.Page:
                        if (this.PageContextMenu != null) this.PageContextMenu.Show(e.Node.TreeView, e.X, e.Y);
                        break;
                    default:
                        break;
                }
            }

            base.OnNodeMouseClick(e);
        }

        protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
        {
            TreeNode tn = this.SelectedNode;
            if (tn == null)
            {
                return;
            }

            NodeType nodeType = this.GetNodeType();

            switch (e.KeyCode)
            {
                case Keys.Insert:
                    if (e.Modifiers == Keys.Shift)
                    {
                        if ((nodeType == NodeType.Root) || (nodeType == NodeType.Folder))
                        {
                            this.SelectedNode = this.AddFolder();
                        }
                        else if (nodeType == NodeType.Page)
                        {
                            this.SelectedNode = tn.Parent;
                            this.SelectedNode = this.AddFolder();
                        }
                    }

                    break;
                case Keys.Delete:
                    if (!tn.IsEditing)
                    {
                        this.DeleteNode();
                    }

                    break;
                case Keys.F2:
                    if ((nodeType == NodeType.Root) || (nodeType == NodeType.Folder))
                    {
                        this.AllowBeginEdit = true;

                        this.EditNode();
                    }

                    break;
                case Keys.F3:
                    switch (nodeType)
                    {
                        case NodeType.Root:
                        case NodeType.Folder:
                            try
                            {
                                Clipboard.SetText(tn.Text ?? string.Empty);
                            }
                            catch
                            {
                            }

                            break;
                        case NodeType.Page:
                            BookmarkItem viewModel = this.GetNodeModel();
                            if (viewModel != null)
                            {
                                try
                                {
                                    Clipboard.SetText(viewModel.SiteAddress ?? string.Empty);
                                }
                                catch
                                {
                                }
                            }

                            break;
                        default:
                            break;
                    }

                    break;
                case Keys.Up:
                    if (e.Modifiers == Keys.Control)
                    {
                        this.MoveUp();
                        this.HasChanged = true;
                    }

                    break;
                case Keys.Down:
                    if (e.Modifiers == Keys.Control)
                    {
                        this.MoveDown();
                        this.HasChanged = true;
                    }

                    break;
                case Keys.Apps:
                    this.OnNodeMouseClick(new TreeNodeMouseClickEventArgs(tn, MouseButtons.Right, 1, tn.Bounds.X, tn.Bounds.Y));
                    break;
                default: break;
            }

            base.OnPreviewKeyDown(e);
        }

#endregion

#region encapsulation

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new ImageList ImageList { get => base.ImageList; set => base.ImageList = value; }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new bool AllowDrop { get => base.AllowDrop; set => base.AllowDrop = value; }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new bool HideSelection { get => base.HideSelection; set => base.HideSelection = value; }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new bool HotTracking { get => base.HotTracking; set => base.HotTracking = value; }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new int ImageIndex { get => base.ImageIndex; set => base.ImageIndex = value; }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new bool LabelEdit { get => base.LabelEdit; set => base.LabelEdit = value; }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new string PathSeparator { get => base.PathSeparator; set => base.PathSeparator = value; }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new int SelectedImageIndex { get => base.SelectedImageIndex; set => base.SelectedImageIndex = value; }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new bool ShowNodeToolTips { get => base.ShowNodeToolTips; set => base.ShowNodeToolTips = value; }

#endregion

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool AllowBeginEdit { get; set; } = false;

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool HasChanged
        {
            get => hasChanged;
            protected set
            {
                hasChanged = value;

                OnNodeChanged?.Invoke(null, null);
            }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IconDatabase IconDatabase { get; set; } = null;

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ContextMenuStrip RootContextMenu { get; set; } = null;

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ContextMenuStrip FolderContextMenu { get; set; } = null;

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ContextMenuStrip PageContextMenu { get; set; } = null;


        public bool InitialiseIconDatabase(out string message, string filename)
        {
            message = string.Empty;

            //if (string.IsNullOrWhiteSpace(filename))
            //{
            //    return false;
            //}

            //if (iconDatabase == null)
            //{
            //    iconDatabase = new IconDatabase();
            //}

            //bool rv = false;
            //if (File.Exists(filename))
            //{
            //    rv = iconDatabase.LoadFile(filename);
            //    if (!rv)
            //    {
            //        rv = iconDatabase.Create(filename, true, null, true);
            //        if (!rv)
            //        {
            //            message = iconDatabase.LastError;
            //            return false;
            //        }
            //    }
            //}
            //else
            //{
            //    rv = iconDatabase.Create(filename, true, null, true);
            //    if (!rv)
            //    {
            //        message = iconDatabase.LastError;
            //        return false;
            //    }
            //}

            return true;
        }

        public TreeNode AddFolder(string name = "")
        {
            return this.AddFolder(this.SelectedNode, name);
        }

        public TreeNode AddFolder(TreeNode node, string name = "")
        {
            if (node == null)
            {
                return null;
            }

            if (node.Tag != null)
            {
                return null;
            }

            if (string.IsNullOrWhiteSpace(name))
            {
                name = DEFAULT_NEW_FOLDER_NAME;
            }

            return node.Nodes.Add(EncodePath(name), name, (int)IconSet.Folder1, (int)IconSet.Folder2);
        }

        public TreeNode AddItem(BookmarkItem item)
        {
            int iconIndex = FindIcon(item);

            TreeNode tn = new TreeNode(item.SiteName, iconIndex, iconIndex);
            tn.Tag = item;
            tn.ToolTipText = item.ToString();

            TreeNode tn2 = AddFolderPath(item.TreeviewPath);

            ThreadControl.Add(tn2, tn);

            this.HasChanged = true;

            return tn;
        }

        public TreeNode AddItem(TreeNode treeNode, BookmarkItem item)
        {
            if (treeNode == null) return null;
            if (item == null) return null;

            int iconIndex = FindIcon(item);

            TreeNode tn = new TreeNode(item.SiteName, iconIndex, iconIndex);
            tn.Tag = item;
            tn.ToolTipText = item.ToString();

            //treeNode.Nodes.Add(tn);
            ThreadControl.Add(treeNode, tn);

            this.HasChanged = true;

            return tn;
        }

        public void Clear()
        {
            ResetImageList();

            ThreadControl.Clear(this);

            this.HasChanged = false;
        }

        public void Clear(string name)
        {
            ResetImageList();

            var tt1 = this.ImageList;


            ThreadControl.Clear(this);
            ThreadControl.Add(this, this.Nodes, "", name?.Trim(), (int)IconSet.Root, (int)IconSet.Root);

            this.HasChanged = true;
        }

        public void CloseIconDatabase()
        {
            //iconDatabase.Close();
        }

        public void DeleteNode() => DeleteNode(this.SelectedNode);

        public void EditNode()
        {
            this.EditNode(this.SelectedNode);
        }

        public void EditNode(TreeNode node)
        {
            if (node == null)
            {
                return;
            }

            if (node.IsEditing)
            {
                return;
            }

            switch (GetNodeType(node))
            {
                case BookmarkTreeView.NodeType.Root:
                case BookmarkTreeView.NodeType.Folder:
                    this.AllowBeginEdit = true;
                    node.BeginEdit();
                    break;
                case BookmarkTreeView.NodeType.Page:
                    this.AllowBeginEdit = true;
                    node.BeginEdit();
                    break;
                default: break;
            }
        }

        public NodeType GetNodeType() => GetNodeType(this.SelectedNode);

        public string GetNodePath() => GetNodePath(this.SelectedNode);

        public BookmarkItem GetNodeModel() => GetNodeModel(this.SelectedNode);

        public List<BookmarkItem> GetBookmarkList()
        {
            List<BookmarkItem> rs = new List<BookmarkItem>();

            if (this.Nodes.Count <= 0)
            {
                return rs;
            }

            foreach (TreeNode item in this.Nodes)
            {
                TraverseBookmarkList(rs, item);
            }

            return rs;
        }

        public bool FindTextNode(TreeNode node, string term)
        {
            if (node == null)
            {
                return false;
            }

            if (this.Nodes.Count <= 0)
            {
                return false;
            }

            bool rt = false;
            bool inclusive = false;
            TreeNode tn = node;
            while (true)
            {
                if (tn == null)
                {
                    break;
                }

                if (inclusive)
                {
                    if (tn.Text.ToLower().Contains(term.ToLower()))
                    {
                        this.SelectedNode = tn;
                        this.SelectedNode.EnsureVisible();
                        rt = true;
                        break;
                    }
                }

                if (tn.Nodes.Count > 0)
                {
                    tn = tn.Nodes[0];
                    inclusive = true;
                }
                else
                {
                    if (tn.NextNode != null)
                    {
                        tn = tn.NextNode;
                        inclusive = true;
                    }
                    else
                    {
                        while (true)
                        {
                            tn = tn.Parent;
                            if (tn == null)
                            {
                                break;
                            }

                            if (tn.NextNode != null)
                            {
                                tn = tn.NextNode;
                                break;
                            }
                        }

                        inclusive = true;
                    }
                }
            }

            return rt;
        }

        public void MoveDown() => MoveDown(this.SelectedNode);

        public void MoveUp() => MoveUp(this.SelectedNode);

        public void SetNoChanges()
        {
            this.HasChanged = false;
        }

        public new void Sort() => Sort(this.SelectedNode);

        public void UpdateItem(TreeNode treeNode, BookmarkItem item)
        {
            if (treeNode == null) return;
            if (item == null) return;

            int iconIndex = FindIcon(item);

            treeNode.Text = item.SiteName;
            treeNode.ImageIndex = iconIndex;
            treeNode.SelectedImageIndex = iconIndex;
            treeNode.Tag = item;
            treeNode.ToolTipText = item.ToString();

            this.HasChanged = true;
        }


        protected int FindIcon(BookmarkItem item)
        {
            if (this.IconDatabase == null) return (int)IconSet.Default;
            if (item == null) return (int)IconSet.Default;
            if (string.IsNullOrWhiteSpace(item.SiteAddress)) return (int)IconSet.Default;

            string iconID = item?.ToHash();

            Image image = this.IconDatabase.FindIcon(iconID);
            if (image == null)
            {
                return (int)IconSet.Default;
            }

            if (this.ImageList.Images.ContainsKey(iconID))
            {
                this.ImageList.Images.RemoveByKey(iconID);
            }

            ThreadControl.Add(this, this.ImageList, iconID, image);

            return this.ImageList.Images.IndexOfKey(iconID);
        }

        protected TreeNode AddFolderPath(string path)
        {
            TreeNode tn = null;
            //TreeNode tn = this.Nodes[0];
            //if (tn == null)
            //{
            //    return tn;
            //}

            if (string.IsNullOrWhiteSpace(path))
            {
                return tn;
            }

            if (string.IsNullOrWhiteSpace(path.Trim('\\')))
            {
                return tn;
            }

            string[] folderList = path.Trim('\\').Split('\\');
            if (folderList.Length <= 0)
            {
                return tn;
            }

            for (int i=0; i<folderList.Length; i++)
            {
                string item = folderList[i];

                if (i <= 0)
                {
                    if (!this.Nodes.ContainsKey(item))
                    {
                        ThreadControl.Add(this, item, DecodePath(item), (int)IconSet.Root, (int)IconSet.Root);
                    }

                    tn = this.Nodes[item];
                }
                else
                {
                    if (!tn.Nodes.ContainsKey(item))
                    {
                        ThreadControl.Add(tn, item, DecodePath(item), (int)IconSet.Folder1, (int)IconSet.Folder2);
                    }

                    tn = tn.Nodes[item];
                }
            }

            return tn;
        }

        protected void ResetImageList()
        {
            ThreadControl.Clear(this, this.ImageList);
            ThreadControl.Add(this, this.ImageList, Resources.hexagon);
            ThreadControl.Add(this, this.ImageList, Resources.folder);
            ThreadControl.Add(this, this.ImageList, Resources.folder_explore);
            ThreadControl.Add(this, this.ImageList, Resources.file_text);
        }

        protected void TraverseBookmarkList(List<BookmarkItem> rs, TreeNode node)
        {
            foreach (TreeNode tn in node.Nodes)
            {
                NodeType nodeType = GetNodeType(tn);
                if (nodeType == NodeType.Folder)
                {
                    TraverseBookmarkList(rs, tn);
                }
                else if (nodeType == NodeType.Page)
                {
                    BookmarkItem nodeTag = GetNodeModel(tn);
                    nodeTag.TreeviewPath = GetNodePath(tn);

                    if (nodeTag != null)
                    {
                        rs.Add(nodeTag);
                    }
                }
            }
        }

    }
}