using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using ClipClop.User;
using System.Diagnostics;
using System.Drawing;

namespace ClipClop.View
{
    class TemplateSettingTreeView : TreeView
    {
		public delegate void DragDropEventHandler();
		public event DragDropEventHandler DragDropItemChanged;

		private ImageList imageList_;
		private System.ComponentModel.IContainer components;

		public TemplateSettingTreeView()
		{
			InitializeComponent();

			this.ImageList = imageList_;

			//Drag&Dropにより移動・コピーできるようにする
			this.AllowDrop = true;
			this.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.OnItemDragEvent);
			this.DragOver += new System.Windows.Forms.DragEventHandler(this.OnDragOverEvent);
			this.DragDrop += new System.Windows.Forms.DragEventHandler(this.OnDragDropEvent);
		}

		#region Drag&Drop
		//Drag&Dropにより移動・コピーできるようにする

		//ノードがドラッグされた時
		private void OnItemDragEvent(object sender, ItemDragEventArgs e)
		{
			//TreeView tv = (TreeView)sender;

			this.SelectedNode = (TreeNode)e.Item;
			this.Focus();
			
			//ノードのドラッグを開始する
			DragDropEffects dde = this.DoDragDrop(e.Item, DragDropEffects.All);

			//移動した時は、ドラッグしたノードを削除する
			if ((dde & DragDropEffects.Move) == DragDropEffects.Move)
				this.Nodes.Remove((TreeNode)e.Item);
		}


		//ドラッグしている時
		private void OnDragOverEvent(object sender, DragEventArgs e)
		{
			//ドラッグされているデータがTreeNodeか調べる
			if (!IsTemplateSettingTreeNodeArg(e))
			{
				//TreeNodeでなければ受け入れない
				e.Effect = DragDropEffects.None;
				return;
			}

			if ((e.KeyState & KEYTYPE_CTRL_) == KEYTYPE_CTRL_ && (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;
				return;
			}


			Debug.Assert(e.Effect != DragDropEffects.None);

			//マウス下のNodeを選択する

			//マウスのあるNodeを取得する
			TemplateSettingTreeNode target = (TemplateSettingTreeNode)this.GetNodeAt(this.PointToClient(new Point(e.X, e.Y)));

			//ドラッグされているNodeを取得する
			TemplateSettingTreeNode source = (TemplateSettingTreeNode)e.Data.GetData(TREENODETYPE_);

			//マウス下のNodeがドロップ先として適切か調べる
			if (IsValidDestination(source, target, e.Effect))
			{
				//Nodeを選択する
				if (target.IsSelected == false)
				{
					this.SelectedNode = target;
				}
			}
			else
			{
				e.Effect = DragDropEffects.None;
			}

		}

		//ドロップされたとき
		private void OnDragDropEvent(object sender, DragEventArgs e)
		{
			//ドロップされたデータがTreeNodeか調べる
			if (!IsTemplateSettingTreeNodeArg(e))
			{
				e.Effect = DragDropEffects.None;
				return;
			}

			//ドロップされたデータ(TreeNode)を取得
			TemplateSettingTreeNode source = (TemplateSettingTreeNode)e.Data.GetData(TREENODETYPE_);

			//ドロップ先のTreeNodeを取得する
			TemplateSettingTreeNode target = (TemplateSettingTreeNode)this.GetNodeAt(this.PointToClient(new Point(e.X, e.Y)));

			//マウス下のNodeがドロップ先として適切か調べる
			if (IsValidDestination(source, target, e.Effect))
			{
				//ドロップされたNodeのコピーを作成
				TemplateSettingTreeNode cln = (TemplateSettingTreeNode)source.Clone();

				if (target.IsRoot() || target.IsFolder())
				{
					//Nodeを追加
					target.Nodes.Add(cln);

					//ドロップ先のNodeを展開
					target.Expand();
				}
				else
				{
					//フォルダでない場合
                    //TODO 改善　フォルダの一番上の子アイテムとして移動できない。残念。

					TemplateSettingTreeNode parent = target.Parent as TemplateSettingTreeNode;
					parent.Nodes.Insert(target.Index+1, cln);
				}


				//追加されたNodeを選択
				this.SelectedNode = cln;

				if (DragDropItemChanged != null)
					this.DragDropItemChanged();

				return;
			}

			e.Effect = DragDropEffects.None;
		}


		private static readonly Type TREENODETYPE_ = typeof(TemplateSettingTreeNode);
		private const int KEYTYPE_CTRL_ = 8;

		private static bool IsTemplateSettingTreeNodeArg(DragEventArgs e)
		{
			if (!e.Data.GetDataPresent(TREENODETYPE_))
			{
				return false;
			}
			return true;
		}

		/// <summary>
		/// ドラッグドロップによるコピー/移動を許可するかどうか判断する
		/// </summary>
		/// <param name="source"></param>
		/// <param name="target"></param>
		/// <returns></returns>
		private bool IsValidDestination(TemplateSettingTreeNode source, TemplateSettingTreeNode target, DragDropEffects effect)
		{
			Debug.Assert(effect != DragDropEffects.None);

			try
			{
				TemplateSettingTreeNodeAction action = new TemplateSettingTreeNodeAction(source, target, effect);

				if (action.IsCopyRoot || action.IsMoveRoot)
				{
					//ルートは移動もコピーも禁止
					return false;
				}

				if (action.IsDescendingOrder)
				{
					//親を子供に移動/コピーしてはダメ
					return false;
				}
			}
			catch(Exception)
			{
				//何か対応する必要なし
				return false;
			}
			return true;
		}

		#endregion

		/// <summary>
		/// データをセットする。
		/// </summary>
		/// <param name="doc"></param>
		/// <see cref="http://www.atmarkit.co.jp/fdotnet/dotnettips/259treeviewadd/treeviewadd.html"/>
		/// <see cref="http://wawatete.ddo.jp/exec/program/cs/xml_xmltreeview"/>
		public void Setup(XmlCipheredDocument doc)
		{
			this.Nodes.Clear();

			//TODO ○多量の場合を考慮する？
			XmlNode root = doc.GetRoot();
			if (root == null)
				return;

            TemplateSettingTreeNode treeNode 
                = new TemplateSettingTreeNode(new TemplateSettingTreeNodeData(root));

			int index = this.Nodes.Add(treeNode);

			RecursiveShowToTreeNode(root, treeNode);

			this.ExpandAll();
			this.SelectedNode = treeNode;
		}


		static void RecursiveShowToTreeNode(XmlNode parentnode, TreeNode parenttreeNode)
		{
			foreach (XmlNode childXmlNode in parentnode.ChildNodes)
			{
				// TreeNodeを新規作成
				TemplateSettingTreeNode tn 
                    = new TemplateSettingTreeNode(
                                        new TemplateSettingTreeNodeData(childXmlNode)
                                        );

				// ノードを追加
				parenttreeNode.Nodes.Add(tn);
				// 再帰呼び出し
				RecursiveShowToTreeNode(childXmlNode, tn);
			}
		}

        public XmlCipheredDocument CreateDocument()
        {
            XmlCipheredDocument doc = XmlCipheredDocument.CreateEmptyDocument();

            Debug.Assert(this.Nodes.Count==1);

            RecursiveTreeToXml(doc.GetRoot(), this.Nodes[0], doc);

            return doc;
        }

        static void RecursiveTreeToXml(XmlNode parentXmlNode, TreeNode parentTreeNode, XmlCipheredDocument doc)
        {
            foreach (TreeNode childTreeNode in parentTreeNode.Nodes)
            {
                TemplateSettingTreeNode treeNode = childTreeNode as TemplateSettingTreeNode;

                // XmlNodeを新規作成
                XmlElement newXmlElement = treeNode.CreateXmlNode(doc);

                // ノードを追加
                parentXmlNode.AppendChild(newXmlElement);

                // 再帰呼び出し
                RecursiveTreeToXml(newXmlElement, childTreeNode, doc);
            }
        }





        public void AppendChild(TemplateSettingTreeNodeData data)
		{
			if (this.SelectedNode == null)
				return;

			TemplateSettingTreeNode selected = this.SelectedNode as TemplateSettingTreeNode;
            selected.Nodes.Add(new TemplateSettingTreeNode(data));
		}

        public void AppendChild(TemplateSettingTreeNode newNode)
        {
            if (this.SelectedNode == null)
                return;

            TemplateSettingTreeNode selected = this.SelectedNode as TemplateSettingTreeNode;
            selected.Nodes.Add(newNode);
        }

		public void DeleteNode()
		{
			if (this.SelectedNode == null)
				return;

			TemplateSettingTreeNode selected = this.SelectedNode as TemplateSettingTreeNode;

			selected.DeleteNode();
		}



		

		private void InitializeComponent()
		{
			this.components = new System.ComponentModel.Container();
			System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TemplateSettingTreeView));
			this.imageList_ = new System.Windows.Forms.ImageList(this.components);
			this.SuspendLayout();
			// 
			// imageList_
			// 
			this.imageList_.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList_.ImageStream")));
			this.imageList_.TransparentColor = System.Drawing.Color.Transparent;
			this.imageList_.Images.SetKeyName(0, "status.png");
			this.imageList_.Images.SetKeyName(1, "ui-splitter.png");
			this.imageList_.Images.SetKeyName(2, "edit.png");
			this.imageList_.Images.SetKeyName(3, "folders-stack.png");
			this.imageList_.Images.SetKeyName(4, "application.png");
			this.imageList_.Images.SetKeyName(5, "EditTableHS.png");
			this.imageList_.Images.SetKeyName(6, "paste.png");
			this.imageList_.Images.SetKeyName(7, "NewCardHS.png");
			this.ResumeLayout(false);

		}



    }
}
