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


namespace ClipClop.View
{
	public partial class MainForm : ActiveForm
	{
		/// <summary>
		/// ホットキーの管理
		/// </summary>
		private CaLib.User.Hotkey hotkeyRegister_;

		/// <summary>
		/// 動作設定
		/// </summary>
		private ClipClop.Model.AplSetting setting_;

        /// <summary>
        /// クリップボード履歴フォーム
        /// </summary>
        private HistoryForm historyForm_;

        /// <summary>
		/// 定型文定義ファイル暗号化時のパスワード。
		/// 暗号化ファイルを使うのであればアプリ起動毎に入力してもらう。
		/// </summary>
		private string templateFilePassword_;

		/// <summary>
		/// クリップボード監視クラス
		/// </summary>
		private CaLib.User.ClipboardViewer cpViewer_;


		/// <summary>
		/// 
		/// </summary>
		ContextMenuSettingManager cntxtMenuSettingManager_ = new ContextMenuSettingManager();

		DynamicContextMenuStrip contextMenuStripPopup_ = null;

		//Load完了後にtrueにする
		bool bReady_ = false;

        
        ScriptManager scriptManager_ = new ScriptManager();



        public MainForm()
		{
			InitializeComponent();

			this.ShowInTaskbar = false;

            //枠を消す
            this.FormBorderStyle = FormBorderStyle.None;

            this.TopMost = true;
            //this.ShowInTaskbar = false;
            this.Size = Size.Empty;

			//設定の復元を行う
			setting_ = ClipClop.Model.AplSetting.Create(global::ClipClop.Properties.Settings.Default.AplSetting);

			templateFilePassword_ = null;
        
            this.historyForm_ = new HistoryForm();
            this.OpenToolStripMenuItem.Click += new System.EventHandler(this.historyForm_.Open_Click);
            this.notifyIcon.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.historyForm_.Open_Click);

			cpViewer_ = new CaLib.User.ClipboardViewer(this);
			cpViewer_.ClipboardHandler += this.OnClipBoardChanged;

        }




        
	    private void MainForm_Load(object sender, EventArgs e)
        {
			//base.OnLoad(e);

			//ホットキーを設定する
			if (!SetupHotkey())
			{
				Util.ShowError(global::ClipClop.Properties.Resources.EF002);
			}

			//設定を反映する
            this.historyForm_.Setup(setting_.bAlwaysTop_, setting_.Opacity_, setting_.WindowFont_, setting_.maxHistory_);

			if (!File.Exists(this.setting_.templateFilePath_) && !global::ClipClop.Properties.Settings.Default.CopiedInitialTemplate)
			{ 
				//定型文定義ファイルが存在しない場合で、まだ初期ファイルをコピーしていないならば
				string src = Path.Combine( Path.GetDirectoryName(Application.ExecutablePath), "InitialTemplate.xml");
				try
				{
					File.Copy(src, this.setting_.templateFilePath_);
					global::ClipClop.Properties.Settings.Default.CopiedInitialTemplate = true;
				}
				catch (Exception exp1)
				{
					Trace.WriteLine( "初期定型文定義ファイルコピーで例外：" + exp1.Message );
				}
			}

			try
			{
				// 定型文字列ペースト用メニュー

				contextMenuStripPopup_ = new DynamicContextMenuStrip(this.components);

                this.ContextMenuStrip = contextMenuStripPopup_;

				contextMenuStripPopup_.AplSetting = this.setting_;

                contextMenuStripPopup_.Closed += new ToolStripDropDownClosedEventHandler(OnContextMenuClosed);

				//設定ファイル読み込み
				ReadTemplateSettingFile(true);

				if (this.setting_.bInheritHistory_)
				{
                    //履歴の読み込み
                    this.historyForm_.Read(Path.Combine(AplSetting.APP_DATA_FOLDER_, global::ClipClop.Properties.Settings.Default.HistoryFileName));
				}
			}
			catch (Exception exp)
			{
				Util.ShowError(exp.Message);
			}

			HistoryToolStripMenuItem.Checked = this.cpViewer_.Valid;
			SetNotifyIcon(this.cpViewer_.Valid);

            UpdateScriptManager();

			bReady_ = true;

            if (!setting_.bMinimizeOnStart_)
            {
                this.historyForm_.Open();
            }

			Trace.WriteLine("MainForm_Load end");

            //最小化する...なぜ別スレッドから呼ぶ必要があるんだろう？
            (new Thread(new ThreadStart(hideFormThread))).Start();
        }

        void UpdateScriptManager()
        {
            if (!string.IsNullOrEmpty(this.setting_.sriptFilePath_))
            {
                try
                {
                    scriptManager_.Create();
                }
                catch (Exception scExp)
                {
                    Util.ShowError(scExp.Message);
                    scriptManager_.Destory();
                }
            }
        }

        #region フォームを閉じる
        delegate void HideFormDelegate();

        void HideForm()
        {
            if (this.InvokeRequired)
            {
                // 別スレッドから呼び出された場合
                Invoke(new HideFormDelegate(HideForm));
                return;
            }
            //see http://dobon.net/vb/dotnet/form/hideformwithtrayicon.html
            this.WindowState = FormWindowState.Minimized;
            this.Hide();
            this.Visible = false;
        }

        void hideFormThread()
        {
            Thread.Sleep(ClipClop.Properties.Settings.Default.WaitBeforeHide);  // Form (起動画面)が消えるまでの時間[ms]の設定
            HideForm();
        }
        #endregion

        /// <summary>
		/// ホットキーを設定する
		/// </summary>
		bool SetupHotkey()
		{
			//ホットキー登録解除
			if (hotkeyRegister_ != null)
			{
				hotkeyRegister_.Clear();
			}

			bool ret = true;
			hotkeyRegister_ = new CaLib.User.Hotkey(this.Handle);

			for (int i = 0; i < setting_.GetHotKeyCount(); i++)
			{
				System.Windows.Forms.Keys s = setting_.GetHotKeyAt(i);
				Debug.WriteLine(string.Format("SetupHotkey[{0}] {1}", i, s.ToString()));

				if (s == Keys.None)
					continue;

				CaLib.User.Hotkey.Input ipt = null;
				
				switch( (AplSetting.Hotkeys)i)
				{
					case AplSetting.Hotkeys.Active:
                        ipt = new Hotkey.Input(s, this.historyForm_.HotkeyEventHandler_Active);
						break;
					case AplSetting.Hotkeys.DeleteHistory:
						ipt = new Hotkey.Input(s, this.historyForm_.HotkeyEventHandler_DeleteHistory);
						break;
					case AplSetting.Hotkeys.TemplatePopup:
						ipt = new Hotkey.Input(s, this.HotkeyEventHandler_TemplatePopup);
						break;
				}
				ret &= hotkeyRegister_.Register(i, ipt);
			}

			return ret;
		}

		#region ホットキーイベント

		void HotkeyEventHandler_TemplatePopup()
        {
            Trace.WriteLine(string.Format("WindowState={0}, Visible={1}", this.WindowState.ToString(), this.Visible));

            if (this.Visible)
                return;//定型文メニュー表示中にホットキーを押された場合を考慮している。

//            IntPtr lastAvtive = FormUtil.GetActiveWindow();
            IntPtr lastAvtive = FormUtil.GetActiveWindow();

			//マウスカーソルの位置を画面座標で取得
			Point mp = Control.MousePosition;

			try
			{
				// 定型文字列ペースト用メニュー

				//設定ファイル読み込み...パスワードが空なら更新する
				bool bNewSetting = ReadTemplateSettingFile(string.IsNullOrEmpty(this.templateFilePassword_));

				if (bNewSetting || contextMenuStripPopup_.Items.Count<1)
				{
					//設定ファイルが新しくなったときだけ、再構築
					contextMenuStripPopup_.ConstructMenu(cntxtMenuSettingManager_,this.historyForm_.History);
				}

				if (contextMenuStripPopup_.Items.Count < 1)
				{
					Util.ShowWarn(global::ClipClop.Properties.Resources.MSG006);
					return;
				}

                contextMenuStripPopup_.LastAvtiveWindow = lastAvtive;

                this.Location = mp;
                base.Open();
			}
			catch(Exception exp)
			{
				Util.ShowError(exp.Message);
			}
			finally
			{
            }
		}

		#endregion

		bool ReadTemplateSettingFile(bool bUpdatePassword)
		{
			bool bReadNewSetting = cntxtMenuSettingManager_.Read(this.setting_.templateFilePath_, ContextMenuSettingManager.FileType.TreeXml);

			if ( bReadNewSetting && cntxtMenuSettingManager_.Document.IsEncrypted())
			{
				//暗号化されている場合

				Debug.Assert(cntxtMenuSettingManager_.Document.Status == XmlCipheredDocument.CipheringStatus.ciphered, this.setting_.templateFilePath_);

				if (bUpdatePassword)
				{
					if (!PasswordForm.ShowInputPassword(ref this.templateFilePassword_))
					{
						throw new FundamentalException(global::ClipClop.Properties.Resources.EF010);
					}
				}

				cntxtMenuSettingManager_.Document.Decrypt(this.templateFilePassword_);
			}

			return bReadNewSetting;
		}


        #region 仮想関数

        protected override void WndProc(ref Message m)
		{
			if (hotkeyRegister_ != null )
			{
				hotkeyRegister_.OnKeyDown(m);
			}

			base.WndProc(ref m);
		}

		#endregion

		#region イベントハンドラー


		/// <summary>
		/// 終了メニュー
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void ExitToolStripMenuItem_Click(object sender, EventArgs e)
		{
			// タスクトレイからアイコンを取り除く
			this.notifyIcon.Visible = false;

			// アプリケーション終了
			Application.Exit();
		}

		private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
		{
            if (!base.CheckClosing(sender, e))
            {
                this.notifyIcon.Visible = true;			// Notifyアイコン表示
                //this.notifyIcon.ShowBalloonTip(500); // バルーンTip表示
                return;
            }

			//ホットキー登録解除
			hotkeyRegister_.Clear();

			//設定保存
			Properties.Settings.Default.Save();

			if (this.setting_.bInheritHistory_)
			{
				//履歴保存
				this.historyForm_.Save(Path.Combine(AplSetting.APP_DATA_FOLDER_,global::ClipClop.Properties.Settings.Default.HistoryFileName));
			}

            // 終了時、「すぐに」アイコンを消す (念のため)
            this.notifyIcon.Visible = false;
		}

        /// <summary>
        /// ContextMenuが閉じたら呼ばれるようにしているイベント
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void OnContextMenuClosed(object sender, ToolStripDropDownClosedEventArgs e)
        {
            this.Hide();
        }

		/// <summary>
		/// 履歴ON/OFFメニューがクリックされた
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void HistoryToolStripMenuItem_Click(object sender, EventArgs e)
		{
			System.Windows.Forms.ToolStripMenuItem m = sender as System.Windows.Forms.ToolStripMenuItem;
			this.cpViewer_.Valid = m.Checked;
			SetNotifyIcon(m.Checked);
		}

		private void SettingToolStripMenuItem_Click(object sender, EventArgs e)
		{
			string oldTemplate = this.setting_.templateFilePath_;

			//アプリケーション設定画面を表示する。
			using (SettingForm sf = new SettingForm(this.setting_, this.templateFilePassword_))
			{
				DialogResult result = sf.ShowDialog();
				if (result == DialogResult.OK)
				{
					this.setting_ = sf.Setting;
					global::ClipClop.Properties.Settings.Default.AplSetting = setting_.ToString();

					//ホットキーを設定する
					if (!SetupHotkey())
					{
						Util.ShowError(global::ClipClop.Properties.Resources.EF002);
					}

					//設定を反映する
                    this.historyForm_.Setup(setting_.bAlwaysTop_, setting_.Opacity_, setting_.WindowFont_, setting_.maxHistory_);

					//パスワードを更新する（コンストラクタで旧を渡してるので、変更に関係なく更新可能）
					this.templateFilePassword_ = sf.Password;

                    UpdateScriptManager();
				}
			}
		}

        /// <summary>
        /// 定型文設定メニュー
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void templateToolStripMenuItem_Click(object sender, EventArgs e)
        {
			using (TemplateSetting dlg = new TemplateSetting(this.setting_.templateFilePath_, this.templateFilePassword_))
			{
				DialogResult result = dlg.ShowDialog();
				if (result == DialogResult.OK)
				{
					this.cntxtMenuSettingManager_.Document = dlg.Document;
                    this.templateFilePassword_ = dlg.PasswordString;

                    try
                    {
                       if (this.cntxtMenuSettingManager_.Document == null)
                            throw new FundamentalException(global::ClipClop.Properties.Resources.EA001);

						//バックアップを作成する
					   if (this.setting_.bBackup_)
					   {				
						   string backup = GetBackupFileName(this.setting_.templateFilePath_);
						   Util.Compress(this.setting_.templateFilePath_, backup);

						   DeleteOldBackupFile(backup);
					   }

                       this.cntxtMenuSettingManager_.Document.SaveTo(this.setting_.templateFilePath_, this.templateFilePassword_);
                    }
                    catch (Exception exp)
                    {
						Util.ShowError(global::ClipClop.Properties.Resources.EF004 + Environment.NewLine + exp.Message);
                    }
				}
			}
        }

		public static string GetBackupFileName(string path)
		{
			string noExtName = Path.Combine( Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path));

			int i = 0;
			while (true)
			{
				string w = string.Format("{0}{1}_{2}.{3}", noExtName, DateTime.Now.ToString("yyyyMMdd"), i++, ClipClop.Properties.Settings.Default.BackupFileExt);

				if (!File.Exists(w))
					return w;
			}

			throw new FundamentalException(global::ClipClop.Properties.Resources.EA001);
		}

		public static void DeleteOldBackupFile(string currnetBackup)
		{
			string filter = string.Format("*.{0}", ClipClop.Properties.Settings.Default.BackupFileExt);

			string[] files = Directory.GetFiles(Path.GetDirectoryName(currnetBackup), filter);
			foreach (string s in files)
			{
				if (s.Equals(currnetBackup))
					continue;
				try
				{
					File.Delete(s);
				}
				catch (Exception)
				{
					//...何もしない
				}
			}
		}


		private void aboutAToolStripMenuItem_Click(object sender, EventArgs e)
		{
			using (AboutForm f = new AboutForm())
			{
				f.ShowDialog();
			}
		}

		private void helpGToolStripMenuItem_Click(object sender, EventArgs e)
		{
			try
			{
				System.Diagnostics.Process.Start(ClipClop.Properties.Resources.ProjectPage);
			}
			catch (Exception exp)
			{
				Util.ShowError(exp.Message);
			}
		}

		#endregion

        private void MainForm_Activated(object sender, EventArgs e)
        {
            if( this.ContextMenuStrip != null)
                this.ContextMenuStrip.Show(Control.MousePosition);
        }

        private void MainForm_Deactivate(object sender, EventArgs e)
        {
            if (this.ContextMenuStrip.Visible == false)
            {
                //ContextMenuのアイテムが選択された
            }
            else
            {
                //フォーカスが外れた
                this.Close();
            }
        }

		static NotifyIconTimer notifyIconTimer_ = new NotifyIconTimer();

		/// <summary>
		/// クリップボードにテキストがコピーされると呼び出される
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>
		private void OnClipBoardChanged(object sender, CaLib.User.ClipboardEventArgs args)
		{
            string input = args.Text;

            if (this.cpViewer_.Valid && false == string.IsNullOrEmpty(input))
			{
                if (scriptManager_.Valid && !string.IsNullOrEmpty(this.setting_.sriptFilePath_)) 
                {
                    try
                    {
                        input = scriptManager_.Execute(this.setting_.sriptFilePath_, "clipboard_text", input);
                    }
                    catch (Exception exp)
                    {
                        Trace.WriteLine(exp.Message);

                        //Util.ShowError(exp.Message);
                        input = args.Text;
                    }
                }

                historyForm_.InsertHead(input);

				if (bReady_ && notifyIconTimer_.IsWorkiing == false )
				{
					Debug.Assert(this.notifyIcon.Icon != null);

					//ちょっと待ってから元のアイコンに戻す
					notifyIconTimer_.prev_ = SwapNotifyIcon(ClipClop.Properties.Resources.paste_yellow);
					notifyIconTimer_.Tick += new EventHandler(NotifyIconClock);
					notifyIconTimer_.working_ = true;
					notifyIconTimer_.Start();
				}
			}
			else
			{
                Trace.WriteLine(string.Format("NOT insert to list {0}.", string.IsNullOrEmpty(input) ? "null" : input));
			}
		}

		#region NotifyIcon操作

		class NotifyIconTimer : System.Windows.Forms.Timer
		{
			public Icon prev_;
			public bool working_;

			public bool IsWorkiing
			{
				get
				{
					return this.working_;
				}
			}

			public NotifyIconTimer()
			{
				working_ = false;
				this.Interval = ClipClop.Properties.Settings.Default.BlickNotifyIconInterval;
			}
		}

		public void NotifyIconClock(object sender, EventArgs e)
		{
			NotifyIconTimer t = sender as NotifyIconTimer;
			if (t != null)
			{
				t.Stop();
			}
			SwapNotifyIcon(t.prev_);
			notifyIconTimer_.working_ = false;
		}

		private Icon SwapNotifyIcon(Icon ic)
		{
			Icon prev = this.notifyIcon.Icon;
			this.notifyIcon.Icon = ic;
			return prev;
		}

		private void SetNotifyIcon(bool bActive)
		{
			if (bActive)
			{
				this.notifyIcon.Icon = ClipClop.Properties.Resources.pasteActive;
			}
			else
			{
				this.notifyIcon.Icon = ClipClop.Properties.Resources.pasteDark;
			}
		}
		#endregion
	}
}
