using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace FastReport.Controls
{
    /// <summary>
    /// TreeView control with multiselect support.
    /// </summary>
    /// <remarks>
    /// This control is for internal use only.
    /// </remarks>
    [ToolboxItem(false)]
    public class TreeViewMultiSelect : TreeView
    {
        List<TreeNode> selectedNodes;

        bool cancelNextSelection = false;
        bool passSelection = false;
        bool NewSelection = false;

        /// <summary>
        /// Creates a new instance of the TreeViewMultiSelect control.
        /// </summary>
        public TreeViewMultiSelect()
        {
            selectedNodes = new List<TreeNode>();
        }

        /// <summary>
        /// Gets a copy of list of selected nodes.
        /// </summary>
        public List<TreeNode> SelectedNodes
        {
            get
            {
                return new List<TreeNode>(selectedNodes);
            }
            /*set
            {
                if (value == null)
                {
                    unhighlightSelectedNodes();
                    selectedNodes.Clear();
                }
                else
                {
                    unhighlightSelectedNodes();
                    selectedNodes = value;
                    highlightSelectedNodes();
                }

                todo: update this.SelectedNode
            }*/
        }

        #region Triggers

        /// <inheritdoc/>
        protected override void OnGotFocus(EventArgs e)
        {
            base.OnGotFocus(e);
            paintSelectedNodes();
        }

        /// <inheritdoc/>
        protected override void OnLostFocus(EventArgs e)
        {
            base.OnLostFocus(e);
            paintSelectedNodes();
        }

        /// <inheritdoc/>
        protected override void OnNodeMouseClick(TreeNodeMouseClickEventArgs e)
        {
            base.OnNodeMouseClick(e);

            bool control = (ModifierKeys == Keys.Control);
            bool shift = (ModifierKeys == Keys.Shift);

            if (control && SelectedNode == e.Node && selectedNodes.Contains(e.Node) && selectedNodes.Count > 1)
            {
                passSelection = true;
                SelectedNode = selectedNodes[selectedNodes.Count - 2];
                passSelection = false;

                unpaintSelectedNodes();
                selectedNodes.Remove(e.Node);
                paintSelectedNodes();

                cancelNextSelection = true;
                NewSelection = true;
            }
            if (selectedNodes.Count > 1 && !control && !shift)
            {
                unpaintSelectedNodes();
                selectedNodes.Clear();
                selectedNodes.Add(e.Node);
                paintSelectedNodes();
            }
        }

        /// <inheritdoc/>
        protected override void OnBeforeSelect(TreeViewCancelEventArgs e)
        {
            base.OnBeforeSelect(e);

            if (passSelection)
                return;

            if (cancelNextSelection)
            {
                e.Cancel = true;
                cancelNextSelection = false;
                return;
            }

            bool control = (ModifierKeys == Keys.Control);

            if (control && selectedNodes.Contains(e.Node))
            {
                e.Cancel = true;
                unpaintSelectedNodes();
                selectedNodes.Remove(e.Node);
                paintSelectedNodes();
                return;
            }
        }

        /// <inheritdoc/>
        protected override void OnAfterSelect(TreeViewEventArgs e)
        {
            base.OnAfterSelect(e);

            if (passSelection)
                return;

            bool control = (ModifierKeys == Keys.Control);
            bool shift = (ModifierKeys == Keys.Shift);

            if (control && !shift)
            {
                if (selectedNodes.Contains(e.Node))
                {
                    unpaintSelectedNodes();
                    selectedNodes.Remove(e.Node);
                    paintSelectedNodes();
                }
                else
                {
                    unpaintSelectedNodes();
                    SelectedNode = e.Node;
                    NewSelection = true;
                    selectedNodes.Add(e.Node);
                    paintSelectedNodes();
                }
            }
            else if (!control && shift)
            {
                if (selectedNodes.Count > 0 && isSameLevel(SelectedNode, e.Node))
                {
                    TreeNode SelectNode = NewSelection == true ? SelectedNodes[SelectedNodes.Count - 1] : SelectedNodes[0];
                    NewSelection = false;
                    List<TreeNode> nodes = getNodesOnSameLevel(SelectNode);

                    int from = Math.Min(SelectNode.Index, e.Node.Index);
                    int to = Math.Max(SelectNode.Index, e.Node.Index);
                    bool needReverse = SelectNode.Index > e.Node.Index;

                    unpaintSelectedNodes();
                    selectedNodes.Clear();

                    for (int i = from; i <= to && i < nodes.Count; i++)
                        selectedNodes.Add(nodes[i]);
                    

                    if (needReverse)
                        selectedNodes.Reverse();

                    paintSelectedNodes();
                }
            }
            else
            {
                unpaintSelectedNodes();
                selectedNodes.Clear();
                selectedNodes.Add(e.Node);
                paintSelectedNodes();
            }
        }

        #endregion

        #region Helpers

        bool isSameLevel(TreeNode node1, TreeNode node2)
        {

            TreeNode next = node1.NextNode;
            if (node1 == node2)
            {
                return true;
            }
            while (next != null)
            {
                if (next == node2)
                    return true;

                next = next.NextNode;
            }

            TreeNode prev = node1.PrevNode;
            while (prev != null)
            {
                if (prev == node2)
                    return true;

                prev = prev.PrevNode;
            }

            return false;
        }

        List<TreeNode> getNodesOnSameLevel(TreeNode node)
        {
            List<TreeNode> nodes = new List<TreeNode>();
            nodes.Add(node);

            TreeNode next = node.NextNode;
            while (next != null)
            {
                nodes.Add(next);
                next = next.NextNode;
            }

            TreeNode prev = node.PrevNode;
            while (prev != null)
            {
                nodes.Insert(0, prev);
                prev = prev.PrevNode;
            }

            return nodes;
        }

        void paintSelectedNodes()
        {
            Color backColor = Focused ? SystemColors.Highlight : SystemColors.Control;
            Color foreColor = Focused ? SystemColors.HighlightText : ForeColor;

            foreach (TreeNode node in selectedNodes)
            {
                node.BackColor = backColor;
                node.ForeColor = foreColor;
            }
        }

        void unpaintSelectedNodes()
        {
            foreach (TreeNode node in selectedNodes)
            {
                node.BackColor = BackColor;
                node.ForeColor = ForeColor;
            }
        }

        #endregion
    }
}
