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 : Form
	{
		/// <summary>
		/// 履歴に残すかどうかフラグ
		/// </summary>
		private bool valid_;

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

		/// <summary>
		/// ホットキーの管理
		/// </summary>
		private CaLib.User.Hotkey hotkeyRegister_;

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

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

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

		DynamicContextMenuStrip contextMenuStripPopup_ = null;

		public MainForm()
		{
			valid_ = true;
			cpViewer_ = new CaLib.User.ClipboardViewer(this);
			cpViewer_.ClipboardHandler += this.OnClipBoardChanged;

			InitializeComponent();

            FormUtil.GeometryFromString(Properties.Settings.Default.WindowGeometryMainForm, this);

			this.ShowInTaskbar = false;

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

			templateFilePassword_ = null;
		}

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

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

			//設定を反映する
			AdaptSetting();		

			listBoxHistory.Clear();

			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);

				contextMenuStripPopup_.AplSetting = this.setting_;

				contextMenuStripPopup_.Closed += new ToolStripDropDownClosedEventHandler(contextMenuStripPopup_Closed);

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

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

			HistoryToolStripMenuItem.Checked = this.valid_;

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

        }

        private void MainForm_Shown(object sender, EventArgs e)
        {
        }

        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();
        }

		void AdaptSetting()
		{
			this.TopMost = setting_.bAlwaysTop_;
			this.Opacity = setting_.Opacity_;

			this.listBoxHistory.Font = setting_.WindowFont_;

			//履歴最大数設定
			listBoxHistory.SetMax(setting_.maxHistory_);
		}

		/// <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.HotkeyEventHandler_Active);
						break;
					case AplSetting.Hotkeys.DeleteHistory:
						ipt = new Hotkey.Input(s, this.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_Active()
		{
			this.Open();
		}
		
		void HotkeyEventHandler_DeleteHistory()
		{
			this.listBoxHistory.RemoveLast();
		}


        static bool bPopuping_ = false;

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

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

            bPopuping_ = true;

//            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.listBoxHistory.Items);
				}

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

                contextMenuStripPopup_.LastAvtiveWindow = lastAvtive;

                using (ContextMenuParentForm contextMenuParentForm = new ContextMenuParentForm(this.contextMenuStripPopup_))
                {
                    contextMenuParentForm.Location = mp;
                    contextMenuParentForm.ShowDialog();
                }			
			}
			catch(Exception exp)
			{
				Util.ShowError(exp.Message);
			}
			finally
			{
                bPopuping_ = false;
            }
		}

		#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;
		}

		/// <summary>
		/// 定型文メニューが閉じたときに呼ばれます。
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		public void contextMenuStripPopup_Closed(object sender, ToolStripDropDownClosedEventArgs e)
		{
			//if( false == contextMenuStripPopup_.ParentVisible )
			//	base.Hide();
        }

        #region 仮想関数

        protected override void WndProc(ref Message m)
		{
			if ((m.Msg == (int)WinApi.Message.id.WM_SYSCOMMAND) && (m.WParam == (IntPtr)WinApi.Message.wparam.SC_MINIMIZE))
			{
				//最小化ボタンクリックで、FormをHideするだけにする
				base.Hide(); // 隠す 
				return;
			}

			if (hotkeyRegister_ != null )
			{
				hotkeyRegister_.OnKeyDown(m);
			}

			base.WndProc(ref m);
		}

		#endregion

		#region イベントハンドラー

		/// <summary>
		/// ダブルクリック
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
		{
			Open();
		}

		/// <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 (e.CloseReason == CloseReason.UserClosing)
			{
				e.Cancel = true;						// 終了処理キャンセル
				this.Visible = false;					// フォーム非表示
				this.notifyIcon.Visible = true;			// Notifyアイコン表示
				//this.notifyIcon.ShowBalloonTip(500); // バルーンTip表示
				return;
			}

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

            Properties.Settings.Default.WindowGeometryMainForm = FormUtil.GeometryToString(this);

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

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

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

		/// <summary>
		/// 開くメニュー
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OpenToolStripMenuItem_Click(object sender, EventArgs e)
		{
			Open();
		}

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


		/// <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;
			valid_ = 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);
					}

					//設定を反映する
					AdaptSetting();

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

        /// <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);

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


		private void 先頭に移動ToolStripMenuItem_Click(object sender, EventArgs e)
		{
			listBoxHistory.MoveHead(false);
		}

		private void 削除ToolStripMenuItem_Click(object sender, EventArgs e)
		{
			listBoxHistory.DeleteSelectedItem();
		}

		private void 改行を削除して先頭へToolStripMenuItem_Click(object sender, EventArgs e)
		{
			listBoxHistory.MoveHead(true);
		}

		/// <summary>
		/// リストボックスのアイテム選択が変わったときに呼ばれます。
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void listBoxHistory_SelectedIndexChanged(object sender, EventArgs e)
		{
			//操作メニューの有効無効更新
			if (listBoxHistory.SelectedIndex < 0)
				SetListContextMenuStripEnabled(false);
			else
				SetListContextMenuStripEnabled(true);
		}

		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


		void Open()
		{
			this.Visible = true;
			if (this.WindowState == FormWindowState.Minimized)
			{
				// 最小化をやめる
				this.WindowState = FormWindowState.Normal;
			}
			this.Activate();
		}

		private void SetListContextMenuStripEnabled(bool enable)
		{
			foreach (ToolStripItem item in listContextMenuStrip.Items)
			{
				item.Enabled = enable;
			}
			listContextMenuStrip.Enabled = enable;
		}

		private void 全て削除toolStripMenuItem_Click(object sender, EventArgs e)
		{
			listBoxHistory.Clear();
			SetListContextMenuStripEnabled(false);
		}


		/// <summary>
		/// タスクトレイのコンテキストメニューを表示する前に呼ばれます。
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void contextMenuStrip_Opened(object sender, EventArgs e)
		{
			//this.パスワードPToolStripMenuItem.Enabled = this.setting_.bEncryptTemplateFile_;
		}

        static string MakeFileName(string input)
        {
            char[] invalidChars = Path.GetInvalidFileNameChars();

            foreach( char c in invalidChars )
            {
               while(true)
               {
                   int n = input.IndexOf(c);
                   if (n < 0)
                       break;
                   input = input.Remove(n, 1);
               }
            }
            return input.Length < 255 ? input : input.Substring(0, 255);
        }

        private void ファイルに保存toolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (listBoxHistory.SelectedIndex < 0)
                return;
            string s = listBoxHistory.SelectedItem.ToString();

            using (SaveFileDialog d = new SaveFileDialog())
            {
                d.DefaultExt = "txt";
                d.OverwritePrompt = true;
                d.InitialDirectory = string.IsNullOrEmpty(ClipClop.Properties.Settings.Default.InitialDirectoryTextFile) ? 
                    string.Empty : ClipClop.Properties.Settings.Default.InitialDirectoryTextFile;
				d.FileName = MakeFileName(s);
				d.Filter = "TEXTファイル(*.TXT)|*.TXT|すべてのファイル(*.*)|*.*";

                DialogResult ret = d.ShowDialog();
                if (ret != DialogResult.OK)
                    return;

                ClipClop.Properties.Settings.Default.InitialDirectoryTextFile = Path.GetDirectoryName(d.FileName);

                try
                {
                    using (StreamWriter sw = new StreamWriter(d.FileName, false, Encoding.UTF8))
                    {
                        sw.Write(s);
                        sw.Close();
                    }
                }
                catch (Exception ex)
                {
                    Util.ShowError(ex.Message);
                }
            }
        }






		//private void パスワードPToolStripMenuItem_Click(object sender, EventArgs e)
		//{
		//    PasswordForm.ShowInputPassword(ref this.templateFilePassword_);
		//}

	}
}
