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

namespace EditFavorite
{
    public partial class Form_EditFavorites : Form
    {

        private ToolStripMenuItem favorites;
        private char delim;
        private string title;
        private string[] skipwords;
        private string separator; //= "---------------"; 
        private Dictionary<string , ToolStripMenuItem> writeProtectItems = new Dictionary<string , ToolStripMenuItem>();

        /// <summary>
        /// コンストラクタ
        /// 各アイテムの説明はtagプロパティに格納しておく
        /// 編集結果はテキストとして生成し、第一引数のツールストリップアイテムのtagプロパティに
        /// 設定する。
        /// </summary>
        /// <param name="ToolStripMenuItem_favorites">ソースとなるメニューアイテム</param>
        /// <param name="title">ウィンドウタイトル</param>
        /// <param name="skipwords">編集対象外の文字列の配列</param>
        /// <param name="delimiter">編集結果のツールストリップ名とtag内容の区切り。</param>
        /// <param name="separator">セパレータ代替文字</param>
        public Form_EditFavorites(ToolStripMenuItem ToolStripMenuItem_favorites, 
                                    string title , 
                                    string[] skipwords, 
                                    char delimiter , 
                                    string separator)
        {
            InitializeComponent();
            this.favorites = ToolStripMenuItem_favorites;

            ToolStripMenuItem_favorites.Tag = null;

            this.delim = delimiter;
            this.title = title;
            this.skipwords = (skipwords == null) ? new string[] { }
                                                 : skipwords;
            this.separator = separator;

        }


        #region ツリー構築

        /// <summary>
        /// 親フォームから渡された、ToolStripMenuItem_favoritesを使って
        /// ツリーを構築する。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form_EditFavorites_Load(object sender, EventArgs e)
        {
            this.Text = title;

            this.treeView1.Nodes.Clear();
            TreeNode root = this.treeView1.Nodes.Add(this.favorites.Text);
            root.ImageKey = "folder.bmp";
            

            var itemList = new Dictionary<string, TreeNode>();

            foreach (var item in favorites.DropDownItems)
            {
                //セパレータならダミーアイテムを作成し、それを追加する
                if (item is ToolStripSeparator)
                {
                    var sep = new ToolStripMenuItem(separator);
                    add_childTreeNode(sep , root);
                }
                if (!(item is ToolStripMenuItem)) continue;

                ToolStripMenuItem curMenuItem = (ToolStripMenuItem)item;
                //編集対象外指定にヒットしたら、保存しておく
                if (skipwords.Contains(curMenuItem.Text))
                {
                    if (!writeProtectItems.ContainsKey(curMenuItem.Text))
                    {
                        writeProtectItems.Add(curMenuItem.Text , curMenuItem);
                    }
                    else
                    {
                        //重複対策
                        writeProtectItems.Add(curMenuItem.Text + DateTime.Now , curMenuItem);
                    }
                    //continue;
                }

                add_childTreeNode(curMenuItem, root);
            }

            //this.treeView1.ExpandAll();
        }//method


        private void add_childTreeNode(ToolStripMenuItem menuitem,TreeNode treenode_current)
        {
            //if (menuitem.DropDownItems.Count != 0 || menuitem.Tag == null)


            if (menuitem.DropDownItems.Count != 0 )
            {
                TreeNode newFolder = treenode_current.Nodes.Add(menuitem.Text);
                newFolder.ImageKey = "folder.bmp";

                foreach (var item in menuitem.DropDownItems)
                {
                    if (item is ToolStripMenuItem)
                    {
                        add_childTreeNode((ToolStripMenuItem)item , newFolder);
                    }
                    //セパレータのとき、ダミーのアイテムを作成し、セパレータとする
                    else if(item is ToolStripSeparator)
                    {
                        var sep = new ToolStripMenuItem(separator);
                        add_childTreeNode(sep , newFolder);
                    }
                }
            }
            else
            {
                TreeNode newnode =  treenode_current.Nodes.Add(menuitem.Text);
                newnode.Tag = menuitem.Tag;
                newnode.ImageKey = "web.bmp";
                if (IsEditable(newnode) == false)
                {
                    newnode.ForeColor = Color.FromArgb(128,128,128);
                }

            }
        }
        
        #endregion


        #region ドラッグ対応

        //***************************************************************************
        //http://dobon.net/vb/dotnet/control/tvdraganddrop.html
        //このサイトから丸ごとコピペ
        private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
        {
            TreeView tv = (TreeView)sender;
            tv.SelectedNode = (TreeNode)e.Item;
            tv.Focus();
            //ノードのドラッグを開始する
            DragDropEffects dde =
                tv.DoDragDrop(e.Item, DragDropEffects.All);
            //移動した時は、ドラッグしたノードを削除する
            if ((dde & DragDropEffects.Move) == DragDropEffects.Move)
                tv.Nodes.Remove((TreeNode)e.Item);
        }

        private void treeView1_DragDrop(object sender, DragEventArgs e)
        {
            //ドロップされたデータがTreeNodeか調べる
            if (e.Data.GetDataPresent(typeof(TreeNode)))
            {
                TreeView tv = (TreeView)sender;
                //ドロップされたデータ(TreeNode)を取得
                TreeNode source =
                    (TreeNode)e.Data.GetData(typeof(TreeNode));
                //ドロップ先のTreeNodeを取得する
                TreeNode target =
                    tv.GetNodeAt(tv.PointToClient(new Point(e.X, e.Y)));
                //マウス下のNodeがドロップ先として適切か調べる
                if (target != null && target != source &&
                    !IsChildNode(source, target) &&
                    target.ImageKey == "folder.bmp")//相手先がフォルダーじゃないとドラッグさせない
                {
                    //ドロップされたNodeのコピーを作成
                    TreeNode cln = (TreeNode)source.Clone();
                    //Nodeを追加
                    target.Nodes.Add(cln);
                    //ドロップ先のNodeを展開
                    target.Expand();
                    //追加されたNodeを選択
                    tv.SelectedNode = cln;

                }
                else
                    e.Effect = DragDropEffects.None;
            }
            else
                e.Effect = DragDropEffects.None;
        }

        private void treeView1_DragOver(object sender, DragEventArgs e)
        {
            //ドラッグされているデータがTreeNodeか調べる
            if (e.Data.GetDataPresent(typeof(TreeNode)))
            {
                if ((e.KeyState & 8) == 8 &&
                    (e.AllowedEffect & DragDropEffects.Copy) ==
                    DragDropEffects.Copy)
                    //Ctrlキーが押されていればCopy
                    //"8"はCtrlキーを表す
                    e.Effect = DragDropEffects.Copy;
                else if ((e.AllowedEffect & DragDropEffects.Move) ==
                    DragDropEffects.Move)
                    //何も押されていなければMove
                    e.Effect = DragDropEffects.Move;
                else
                    e.Effect = DragDropEffects.None;
            }
            else
                //TreeNodeでなければ受け入れない
                e.Effect = DragDropEffects.None;

            //マウス下のNodeを選択する
            if (e.Effect != DragDropEffects.None)
            {
                TreeView tv = (TreeView)sender;
                //マウスのあるNodeを取得する
                TreeNode target =
                    tv.GetNodeAt(tv.PointToClient(new Point(e.X, e.Y)));
                //ドラッグされているNodeを取得する
                TreeNode source =
                    (TreeNode)e.Data.GetData(typeof(TreeNode));
                //マウス下のNodeがドロップ先として適切か調べる
                if (target != null && target != source && !IsChildNode(source, target))
                {
                    //Nodeを選択する
                    if (target.IsSelected == false)
                        tv.SelectedNode = target;
                }
                else
                    e.Effect = DragDropEffects.None;
            }
        }

        /// <summary>
        /// あるTreeNodeが別のTreeNodeの子ノードか調べる
        /// </summary>
        /// <param name="parent">親ノードか調べるTreeNode</param>
        /// <param name="child">子ノードか調べるTreeNode</param>
        /// <returns>子ノードの時はTrue</returns>
        private static bool IsChildNode(TreeNode parent, TreeNode child)
        {
            if (child.Parent == parent)
                return true;
            else if (child.Parent != null)
                return IsChildNode(parent, child.Parent);
            else
                return false;
        }
        //***************************************************************************

        #endregion


        /// <summary>
        /// アイテム削除
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_remove_Click(object sender , EventArgs e)
        {
            this.treeView1.SelectedNode.Remove();
        }



        /// <summary>
        /// 選択したアイテムの内容を表示する
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            this.textBox_nodeTagInfo.Text =
                treeView1.SelectedNode.Tag == null ? ""
                                                   : treeView1.SelectedNode.Tag.ToString();
            
            //削除許可対象かチェック、ボタン設定
            this.button_delete.Enabled = IsDeletable(this.treeView1.SelectedNode);

            //編集対象外かチェックして、ボタン設定
            this.textBox_nodeTagInfo.Enabled = 
                this.treeView1.LabelEdit = 
                this.button_edit.Enabled = IsEditable(this.treeView1.SelectedNode);
        }


        /// <summary>
        /// ラベル編集終了
        /// すでに同じ名前があったら弾く
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void treeView1_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
        {
            e.Node.EndEdit(false);
            
            List<TreeNode> list = get_AllTreeNode(this.treeView1.Nodes[0]);
            
            int hit = (from p
                      in list
                       where p.Text == e.Node.Text
                      select p).Count<TreeNode>();

            if (hit >= 2)
            {
                MessageBox.Show("重複あり");
                e.Node.Text += (DateTime.Now).ToString();
                this.treeView1.LabelEdit = true;
                e.Node.BeginEdit();
                //e.CancelEdit = true;
            }
        }




        #region アイテム追加対応

        /// <summary>
        /// フォルダ追加
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_CreateFolder_Click(object sender, EventArgs e)
        {
            TreeNode newnode = this.treeView1.Nodes[0].Nodes.Add("新しいフォルダ");
            newnode.ImageKey = "folder.bmp";
            newnode.Tag = null;
            newnode.Text = "新しいフォルダ";
            
           // this.treeView1.LabelEdit = true;
            this.treeView1.SelectedNode = newnode;
            newnode.BeginEdit();
        }


        /// <summary>
        /// アイテム追加
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_CreateItem_Click(object sender , EventArgs e)
        {
            TreeNode newnode = this.treeView1.Nodes[0].Nodes.Add("新しいアイテム");
            newnode.ImageKey = "web.bmp";
            newnode.Tag = null;
            newnode.Text = "新しいアイテム";
            this.treeView1.LabelEdit = true;
            this.treeView1.SelectedNode = newnode;
            newnode.BeginEdit();
        }

        private void button_addSepareter_Click(object sender , EventArgs e)
        {
            TreeNode newnode = this.treeView1.Nodes[0].Nodes.Add(separator);
            newnode.ImageKey = "web.bmp";
            newnode.Tag = null;
        }
        
        #endregion

        private void button_edit_Click(object sender, EventArgs e)
        {
            if (IsEditable(this.treeView1.SelectedNode) == true)
            {
                this.treeView1.LabelEdit = true;
                this.treeView1.SelectedNode.BeginEdit();
            }
        }
         
        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            string txt = this.textBox_nodeTagInfo.Text;
            //txt = txt.Replace(this.delim.ToString() ,"");
            this.treeView1.SelectedNode.Tag = txt;
            this.textBox_nodeTagInfo.Text = txt;
        }


        /// <summary>
        /// 保存ボタンクリック。
        /// このボタンを押さないと（DialogResult != DialogResult.OK）、変更が保存されない。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_save_Click(object sender, EventArgs e)
        {
            //string s = "";
            List<TreeNode> list = get_AllTreeNode(this.treeView1.Nodes[0]);

            //foreach (TreeNode item in list)
            //{
            //    if (item.Tag == null) item.Tag = "";

            //    s += item.FullPath
            //        + delim + item.Tag.ToString()
            //        + Environment.NewLine;
            //}


            //データ渡してからクローズ
            this.favorites.Tag = new object[] { list , this.writeProtectItems };
            this.DialogResult = DialogResult.OK;
            this.Close();
            
        }




        #region アップダウン対応
        private void button_up_Click(object sender, EventArgs e)
        {
            //トップなら抜ける
            if (this.treeView1.SelectedNode.Parent == null)
                return;

            //すでに一番うえなら抜ける
            if (this.treeView1.SelectedNode.PrevNode == null)
                return;


            //入れ物を準備しておく
            TreeNode[] parentCopy = new TreeNode[this.treeView1.SelectedNode.Parent.Nodes.Count];
            TreeNode parentOrg = this.treeView1.SelectedNode.Parent;
            int olderBroIndex = this.treeView1.SelectedNode.PrevNode.Index;

            int i;
            for (i = 0 ; i < parentOrg.Nodes.Count ; i++)
            {
                if (i == olderBroIndex)
                {
                    parentCopy[i] = (TreeNode)this.treeView1.SelectedNode.Clone();
                    i++;
                    parentCopy[i] = (TreeNode)parentOrg.Nodes[olderBroIndex].Clone();
                }
                else
                {
                    parentCopy[i] = (TreeNode)parentOrg.Nodes[i].Clone();
                }
            }

            parentOrg.Nodes.Clear();
            parentOrg.Nodes.AddRange(parentCopy);
            this.treeView1.SelectedNode = parentOrg.Nodes[olderBroIndex];
            this.treeView1.Focus();
        }

        private void button_Down_Click(object sender, EventArgs e)
        {
            //トップなら抜ける
            if (this.treeView1.SelectedNode.Parent == null)
                return;

            //すでに一番下なら抜ける
            if (this.treeView1.SelectedNode.NextNode == null)
                return;

            //入れ物を準備しておく
            TreeNode[] parentCopy = new TreeNode[this.treeView1.SelectedNode.Parent.Nodes.Count];
            TreeNode parentOrg = this.treeView1.SelectedNode.Parent;
            int youngerBroIndex = this.treeView1.SelectedNode.NextNode.Index;

            int i;
            for (i = 0; i < parentOrg.Nodes.Count; i++)
            {
                if (i == youngerBroIndex - 1)
                {
                    parentCopy[i] = (TreeNode)parentOrg.Nodes[youngerBroIndex].Clone();
                    i++;
                    parentCopy[i] = (TreeNode)this.treeView1.SelectedNode.Clone();
                }
                else
                {
                    parentCopy[i] = (TreeNode)parentOrg.Nodes[i].Clone();
                }
            }

            parentOrg.Nodes.Clear();
            parentOrg.Nodes.AddRange(parentCopy);
            this.treeView1.SelectedNode = parentOrg.Nodes[youngerBroIndex];
            this.treeView1.Focus();
        }

        #endregion

        #region ヘルパー

        /// <summary>
        /// 再帰して全ツリーノードを取得
        /// </summary>
        /// <param name="children">現在の子ノードコレクション</param>
        /// <returns></returns>
        private List<TreeNode> get_AllTreeNode(TreeNode parentNode)
        {
            var list = new List<TreeNode>();
            //まず親を追加
            list.Add(parentNode);

            //子供を調査。孫がいたら再帰
            foreach (TreeNode tr in parentNode.Nodes)
            {
                //孫いなかったら
                if (tr.Nodes.Count == 0)
                {
                    //子供だけ追加
                    list.Add(tr);
                }
                else
                {
                    //子供を引数として、孫を調査
                    list.AddRange(get_AllTreeNode(tr));
                }
            }
            return list;
        }

        /// <summary>
        /// 編集対象か？
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        private bool IsEditable(TreeNode node)
        {
            //初期化
            bool ret = true;


            ////セパレータ、ルートは編集対象外
            if (node.Text == this.separator || node.Parent == null)
            {
                ret = false;
            }
            else
            {
                //スキップワードが含まれているときは、編集対象外
                var hit = from p
                          in this.skipwords
                          where p == node.Text
                          select p;

                if (hit.Count<string>() > 0)
                {
                    ret = false;
                }
            }
            return ret;
        }

        /// <summary>
        /// 削除許可対象か？
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        private bool IsDeletable(TreeNode node)
        {
            //初期化
            bool ret = true;

            //スキップワードが含まれているときは、編集対象外
            var hit = from p
                        in this.skipwords
                        where node.Text.Contains(p)
                        select p;

            //ルートも対象外
            if (hit.Count<string>() > 0 || node.Parent == null)
            {
                ret = false;
            }

            return ret;
        }

        #endregion

        private void Form_EditFavorites_FormClosing(object sender, FormClosingEventArgs e)
        {
            //this.favorites.Tag = null;
            this.DialogResult = DialogResult.OK;
            this.Close();

        }



    }
}
