using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Xml;
using System.Windows.Forms;
using System.Drawing;
using System.Diagnostics;
using ClipClop.Model;
using System.IO;
using CaLib.User;

namespace ClipClop.User
{
    //デリゲートの宣言
    public delegate void StringPasteEventHandler(string message);

	/// <summary>
	/// 定義ファイルから読み込んだアイテムを表示するコンテキストメニュークラス
	/// </summary>
	public class DynamicContextMenuStrip : System.Windows.Forms.ContextMenuStrip
	{
		AplSetting aplSetting_;
        private ImageList imageList;
        private IContainer components;

        IntPtr lastAvtiveWindow_;

		public AplSetting AplSetting
		{
			set
			{
				this.aplSetting_ = value;
			}
		}

        public IntPtr LastAvtiveWindow
        {
            set
            {
                this.lastAvtiveWindow_ = value;
            }
        }

        

		public DynamicContextMenuStrip() : base()
		{
            InitializeComponent();
        }

		public DynamicContextMenuStrip(IContainer container)
			: base(container)
		{
            InitializeComponent();
		}

        void StringPasteEventHandler(string message)
        {
            if (this.lastAvtiveWindow_ == IntPtr.Zero)
            {
                return;
            }

#if DEBUG
            StringBuilder sb = new StringBuilder(512);
            WinApi.User32.GetWindowText(this.lastAvtiveWindow_, sb, sb.Capacity);
            Debug.WriteLine(string.Format("paste to \"{0}\", {1}", sb.ToString(), message));
#endif

            WinApi.User32.SetForegroundWindow(this.lastAvtiveWindow_);
            WinApi.User32.SendMessage(this.lastAvtiveWindow_, (int)WinApi.Message.id.WM_PASTE, IntPtr.Zero, IntPtr.Zero);
        }

		void PrepareLatterStringEventHandler(string message)
		{ 
		}
 

		//protected override void WndProc(ref Message m)
		//{
		//    base.WndProc(ref m);
		//}

		/// <summary>
		/// メニュー表示時に親ウィンドウが非表示だったかどうか
		/// </summary>
		//public bool ParentVisible { get; set; }


        public void ConstructMenu(ContextMenuSettingManager settingManager, ListBox.ObjectCollection history)
		{
			this.Items.Clear();
			//this.menuEventHanders_.Clear();

			if (null == settingManager.RootNode)
				return;

            RecursiveConstructMenu(settingManager.RootNode, this.Items, history);
		}

        void RecursiveConstructMenu(XmlNode parentnode, ToolStripItemCollection items, ListBox.ObjectCollection history)
		{
			foreach (XmlNode childXmlNode in parentnode.ChildNodes)
			{
                if (XmlNodeHelper.GetClassification(childXmlNode) == ContextMenuSettingDefinition.Classification.separator)
				{
					items.Add( new ToolStripSeparator() );
					continue;
				}

				ToolStripMenuItemEx menu = null;

                if (XmlNodeHelper.GetClassification(childXmlNode) == ContextMenuSettingDefinition.Classification.sentence)
                {
                    ContextMenuSettingDefinition.SentenceType m = XmlNodeHelper.GetSentenceType(childXmlNode);
                    switch (m)
                    {
                        // クリップボード履歴
                        case ContextMenuSettingDefinition.SentenceType.clipboardhistory:
                            menu = new ToolStripMenuItemClipboardHistory(childXmlNode, this.aplSetting_, history, this.StringPasteEventHandler);
                            break;
                        case ContextMenuSettingDefinition.SentenceType.execute:
                            menu = new ToolStripMenuItemEx(childXmlNode, this.aplSetting_);
                            menu.Image = FindExeImage(childXmlNode);
                            break;
							
                        default:
                            menu = new ToolStripMenuItemEx(childXmlNode, this.aplSetting_);
                            break;
                    }
                }
                else
                {
                    menu = new ToolStripMenuItemEx(childXmlNode, this.aplSetting_);
                }

                menu.OnStringPaste += this.StringPasteEventHandler;
				items.Add(menu);

				// 再帰呼び出し
                RecursiveConstructMenu(childXmlNode, menu.DropDownItems, history);
			}
		}

        Dictionary<string, int> exePathToImageIndex =
                    new Dictionary<string, int>();

        public enum MenuImageIndex
        {
            DEFAULT_IMAGEINDEX_,
            DEFAULT_FOLDER_,
        };


        Image FindExeImage(XmlNode node)
        {
			string exe = LaunchExec.GetExecuteString(node);

            int index = -1;
            if (exePathToImageIndex.TryGetValue(exe, out index))
            {
                if (index < this.imageList.Images.Count)
                {
                    return  this.imageList.Images[index]; 
                }
                return this.imageList.Images[(int)MenuImageIndex.DEFAULT_IMAGEINDEX_];
            }

            try
            {
                Icon appIcon = Icon.ExtractAssociatedIcon(exe);
                this.imageList.Images.Add(appIcon);

                exePathToImageIndex.Add(exe, this.imageList.Images.Count - 1);

                return this.imageList.Images[this.imageList.Images.Count-1];
            }
            catch (Exception)
            { 
                //何もしない
            }

            try
            {
                FileAttributes fa =  File.GetAttributes(exe);
                if ((fa & FileAttributes.Directory) == FileAttributes.Directory )
                {
                    return this.imageList.Images[(int)MenuImageIndex.DEFAULT_FOLDER_];
                }
            }
            catch (Exception)
            { 
            }

            return this.imageList.Images[(int)MenuImageIndex.DEFAULT_IMAGEINDEX_];
        }

        class ToolStripMenuItemPastable : ToolStripMenuItem
        {
            //イベントデリゲートの宣言
            public event StringPasteEventHandler OnStringPaste;

            public ToolStripMenuItemPastable(string text)
                : base(text)
            {
            }

            protected void PasteString(string str)
            {
                // リストに二つ入るけれど、リストで先頭と同じなら挿入しないようにした。
				ClipboardAdapter.SetDataObject(str, true);

                if (OnStringPaste != null)
                {
                    OnStringPaste(str);
                }
            }
        }


        class ToolStripMenuItemEx : ToolStripMenuItemPastable
		{
			readonly XmlNode node_;

			readonly AplSetting aplSetting_;

			public ToolStripMenuItemEx(XmlNode node, AplSetting aplSetting)
                : base(XmlNodeHelper.GetDisplayString(node))
			{
				this.node_ = node;
				this.aplSetting_ = aplSetting;
				base.Click += this.OnMenuEvent;
			}

			public ToolStripMenuItem CreatetMenu()
			{
				ToolStripMenuItem menu = new ToolStripMenuItem();
				return menu;
			}


			public void StartProcess(string exeString)
			{

				System.Diagnostics.Process process = null;
				try
				{
					process = System.Diagnostics.Process.Start(exeString);
				}
				catch (Exception exp)
				{
					Util.ShowError(global::ClipClop.Properties.Resources.EF003 + Environment.NewLine + exp.Message + Environment.NewLine + exeString);
					return;
				}

				/*すでに起動していたら新しいプロセスリソースは生成されない
				if (process == null)
				{
					Util.ShowError(global::ClipClop.Properties.Resources.EF003);
				}
				*/
			}

			/// <summary>
			/// メニューがクリックされた
			/// </summary>
			/// <param name="sender"></param>
			/// <param name="e"></param>
			public void OnMenuEvent(object sender, EventArgs e)
			{
				if (ContextMenuSettingDefinition.Sentence_ != node_.Name)
					return;

				ContextMenuSettingDefinition.SentenceType sentenceType = XmlNodeHelper.GetSentenceType(this.node_);

				Debug.WriteLine(string.Format("{0},{1},{2}", XmlNodeHelper.GetDisplayString(node_), XmlNodeHelper.GetDisplayValue(node_), sentenceType));

				switch (sentenceType)
				{
					case ContextMenuSettingDefinition.SentenceType.execute:
						//Exe実行の場合

						string exeString = LaunchExec.GetExecuteString(node_);

						StartProcess(exeString);

						return;

					case ContextMenuSettingDefinition.SentenceType.editfile:
						StartProcess(this.aplSetting_.templateFilePath_);

						return;

                    case ContextMenuSettingDefinition.SentenceType.clipboardhistory:
                        return;
				}
				

				string value = ContextMenuSettingHelper.GetValueReplaced(node_, DateTime.Now);
                base.PasteString(value);//カレントのウィンドウに対してペーストを行う。


				if (sentenceType == ContextMenuSettingDefinition.SentenceType.complex)
				{
					string latter = XmlNodeHelper.GetLatterAttribute(node_);

					//クリップボードにパスワード文字列を入れる
					ClipboardAdapter.SetDataObject(latter, true);
				}
			}
		}

        class ToolStripMenuItemClipboardHistory : ToolStripMenuItemEx
        {
            ListBox.ObjectCollection history_;
            StringPasteEventHandler hdr_;

            public ToolStripMenuItemClipboardHistory(XmlNode node, AplSetting aplSetting, ListBox.ObjectCollection history, StringPasteEventHandler hdr)
                : base(node, aplSetting)
			{
                this.history_ = history;
                this.hdr_ = hdr;

                base.DropDownOpening += this.OnDropDownOpeningEvent;

                //openingで作ると、フォーカスされるまでメニューに三角印が表示されないので
                //ダミーを入れて安易に対応しておく。
                SubMenuItem menuHistory = new SubMenuItem("dummy");
                menuHistory.OnStringPaste += this.hdr_;
                this.DropDownItems.Add(menuHistory);
			}

            public void OnDropDownOpeningEvent(object sender, EventArgs e)
            {
                DropDownItems.Clear();

                foreach (object obj in history_)
                {
                    if (global::ClipClop.Properties.Settings.Default.MaxHistoryInMenu <= this.DropDownItems.Count)
                        break;

                    string s = obj as string;
                    if (string.IsNullOrEmpty(s))
                        continue;

                    SubMenuItem menuHistory = new SubMenuItem(s);
                    menuHistory.OnStringPaste += this.hdr_;
                    this.DropDownItems.Add(menuHistory);
                }

            } 

            class SubMenuItem : ToolStripMenuItemPastable
            {
                readonly string history_;
                public SubMenuItem(string history)
                    : base(GetDisplayString(history))
                {
                    Debug.WriteLine(string.Format("HistoryItem \"{0}\"", GetDisplayString(history)));

                    this.history_ = history;
                    base.Click += this.OnHistoryItemClickedEvent;
                }

                const string extensionMark_ = "...";

                static string GetDisplayString(string history)
                {
                    string s = history.Replace(Environment.NewLine, "");
                    s = s.Replace("\n", "");

                    if (s.Length <= global::ClipClop.Properties.Settings.Default.MaxHistoryItemLength)
                        return s;
                    s = s.Substring(0, global::ClipClop.Properties.Settings.Default.MaxHistoryItemLength) + extensionMark_;
                    return s;
                }

                /// <summary>
                /// クリップボード履歴メニューアイテムが選択されたら呼ばれる
                /// </summary>
                /// <param name="sender"></param>
                /// <param name="e"></param>
                public void OnHistoryItemClickedEvent(object sender, EventArgs e)
                {
                    base.PasteString(this.history_);
                }
            }

        }

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DynamicContextMenuStrip));
            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, "application.png");
            this.imageList.Images.SetKeyName(1, "folder-horizontal.png");
            this.ResumeLayout(false);

        }

		//protected override void WndProc(ref Message m)
		//{
		//    base.WndProc(ref m);
		//}

	}
}
