using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Xml.Serialization;
using ClipClop.View;
using CaLib.User.PropertyGridUtil;
using System.Diagnostics;
using System.Drawing;

using ClipClop.Properties;
using CaLib.User.DynamicProperty;
using System.Globalization;
using ClipClop.Model.ImageHistory;

namespace ClipClop.Model
{
	#region 画像履歴
	#endregion

	/// <summary>
	/// User設定
	/// </summary>
	/// <see cref="http://www.atmarkit.co.jp/fdotnet/dotnettips/291pgridjapan/pgridjapan.html"/>
	[TypeConverter(typeof(PropertyDisplayConverter))]
	[TypeDescriptionProvider(typeof(DynamicTypeDescriptionProvider))]
	public class AplSetting
	{
		#region ホットキー
		/// <summary>
		/// ホットキー種別
		/// </summary>
		public enum Hotkeys
		{
			Active,
			DeleteHistory,
			TemplatePopup,
		}

		/// <summary>
		/// EXEをアクティブにするホットキー
		/// </summary>
        [DisplayNameLocalized(typeof(Resources), "PGDisplayNameOpenHistory"),
        CategoryLocalizedAttribute(typeof(Resources), "PGCategoryHotkey")]
		public System.Windows.Forms.Keys hkActive_ { get; set; }

		/// <summary>
		/// 履歴を一個削除するホットキー
		/// </summary>
        [DisplayNameLocalized(typeof(Resources), "PGDisplayNameDeleteHistory"),
        CategoryLocalizedAttribute(typeof(Resources), "PGCategoryHotkey")]
        public System.Windows.Forms.Keys hkDeleteHistory_ { get; set; }

		/// <summary>
		/// 定型文メニューを表示するホットキー
		/// </summary>
        [DisplayNameLocalized(typeof(Resources), "PGDisplayNameTemplatePopup"),
        CategoryLocalizedAttribute(typeof(Resources), "PGCategoryHotkey")]
        public System.Windows.Forms.Keys hkTemplatePopup_ { get; set; }
		#endregion

		#region 履歴
		[SortOrder(1)]
        [DisplayNameLocalized(typeof(Resources), "PGDisplayNameMinimizeOnStart"),
        DescriptionLocalizedAttribute(typeof(Resources), "PGDescriptionMinimizeOnStart"),
        CategoryLocalizedAttribute(typeof(Resources), "PGCategoryHistory")]
		public bool bMinimizeOnStart_ { get; set; }

		/// <summary>
		/// 保持する履歴の上限
		/// </summary>
		[SortOrder(2)]
		[DisplayNameLocalized(typeof(Resources), "PGDisplayNameMaxHistory"),
		DescriptionLocalizedAttribute(typeof(Resources), "PGDescriptionMaxHistory"),
		CategoryLocalizedAttribute(typeof(Resources), "PGCategoryHistory")]
		public int MaxHistory
		{
			get
			{
				return this.maxHistory_;
			}
			set
			{
				if (value < MAX_HISTORY_RANGE[0] || MAX_HISTORY_RANGE[1] < value)
				{
					throw new ArgumentOutOfRangeException();
				}
				this.maxHistory_ = value;
			}
		}
		int maxHistory_;
		static readonly int[] MAX_HISTORY_RANGE = {1,4095};//上限に根拠なし

		[SortOrder(3)]
		[DisplayNameLocalized(typeof(Resources), "PGDisplayNameUseBufferText"),
		DescriptionLocalizedAttribute(typeof(Resources), "PGDescriptionUseBufferText"),
		CategoryLocalizedAttribute(typeof(Resources), "PGCategoryHistory")]
		[RefreshProperties(RefreshProperties.All)]  // この値が変わると、MaxBufferTextが変わるため。
		public bool bUseBufferText_ { get; set; }

		/// <summary>
		/// 保持するとっておく文字列の上限
		/// </summary>
		[SortOrder(4)]
		[DisplayNameLocalized(typeof(Resources), "PGDisplayNameMaxBufferText"),
		DescriptionLocalizedAttribute(typeof(Resources), "PGDescriptionMaxBufferText"),
		CategoryLocalizedAttribute(typeof(Resources), "PGCategoryHistory")]
		public int MaxBufferText
		{
			get
			{
				return this.maxBufferText_;
			}
			set
			{
				if (value < MAX_BUFFER_RANGE[0] || MAX_BUFFER_RANGE[1] < value)
				{
					throw new ArgumentOutOfRangeException();
				}
				this.maxBufferText_ = value;
			}
		}
		int maxBufferText_;
		static readonly int[] MAX_BUFFER_RANGE = { 1, 128 };// 上限に根拠なし

		/// <summary>
		/// 前回の履歴を引き継ぐかどうか
		/// </summary>
		[SortOrder(5)]
		[DisplayNameLocalized(typeof(Resources), "PGDisplayNameInheritHistory"),
		DescriptionLocalizedAttribute(typeof(Resources), "PGDescriptionInheritHistory"),
		CategoryLocalizedAttribute(typeof(Resources), "PGCategoryHistory")]
		public bool bInheritHistory_ { get; set; }

		/// <summary>
		/// 最前面に表示
		/// </summary>
		[SortOrder(6)]
		[DisplayNameLocalized(typeof(Resources), "PGDisplayNameAlwaysTop"),
		DescriptionLocalizedAttribute(typeof(Resources), "PGDescriptionAlwaysTop"),
		CategoryLocalizedAttribute(typeof(Resources), "PGCategoryHistory")]
		public bool bAlwaysTop_ { get; set; }

		/// <summary>
		/// フォームの透明度
		/// </summary>
		[SortOrder(7)]
		[DisplayNameLocalized(typeof(Resources), "PGDisplayNameOpacity"),
		DescriptionLocalizedAttribute(typeof(Resources), "PGDescriptionOpacity"),
		CategoryLocalizedAttribute(typeof(Resources), "PGCategoryHistory")]
		public double Opacity 
		{
			get { return opacity_; }
			set {
				if (value < OPACITY_RANGE[0] || OPACITY_RANGE[1] < value)
				{
					throw new ArgumentOutOfRangeException();
				}
				opacity_ = value;
			}
		}
		double opacity_;
		static readonly double[] OPACITY_RANGE = { 0.0, 1.0 };

		[SortOrder(8)]
		[DisplayNameLocalized(typeof(Resources), "PGDisplayNameCheckFileList"),
		DescriptionLocalizedAttribute(typeof(Resources), "PGDescriptionCheckFileList"),
		CategoryLocalizedAttribute(typeof(Resources), "PGCategoryHistory")]
		public bool bCheckFileList_ { get; set; }

		/// <summary>
		/// 画像履歴
		/// </summary>
		[SortOrder(9)]
		[DisplayNameLocalized(typeof(Resources), "PGDisplayNameImageHistory"),
		DescriptionLocalizedAttribute(typeof(Resources), "PGDescriptionImageHistory"),
		CategoryLocalizedAttribute(typeof(Resources), "PGCategoryHistory")]
		[TypeConverterAttribute(typeof(CustomObjectConverter<ImageHistoryOptions>))]
		public ImageHistoryOptions imageHistoryOptions_ { get; set; }


		#region フォント
		//[CategoryAttribute("ドキュメント設定")]
		[XmlIgnore] // XmlSerializer から隠す
		[DisplayNameLocalized(typeof(Resources), "PGDisplayWindowFont"),
		DescriptionLocalizedAttribute(typeof(Resources), "PGDescriptionWindowFont"),
		CategoryLocalizedAttribute(typeof(Resources), "PGCategoryHistory")]
		public Font WindowFont_ { get; set; }

		[EditorBrowsable(EditorBrowsableState.Never)]
		[Browsable(false)]
		public string DisplayFontAsString
		{
			get { return ConvertToString(WindowFont_); }
			set { WindowFont_ = ConvertFromString<Font>(value); }
		}

		public static string ConvertToString<T>(T value)
		{
			return TypeDescriptor.GetConverter(typeof(T)).ConvertToString(value);
		}
		public static T ConvertFromString<T>(string value)
		{
			return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(value);
		}
		#endregion
		#endregion

		#region 定型文定義

		public enum StorageCategory
		{
			[EnumDisplayName(typeof(Resources), "PGEnumDisplayNameLocal")]
			local,

			[EnumDisplayName(typeof(Resources), "PGEnumDisplayNameGoogle")]
			google,
		}

		[DisplayNameLocalized(typeof(Resources), "PGDisplayNameStorageCategory"),
	   DescriptionLocalizedAttribute(typeof(Resources), "PGDescriptionStorageCategory"),
	   CategoryLocalizedAttribute(typeof(Resources), "PGCategoryTemplate")]
		[TypeConverter(typeof(EnumDisplayNameConverter))]
		[RefreshProperties(RefreshProperties.All)]  // この値が変わると、sriptFileFolder_が変わるため。
		[ReadOnlyAttribute(true)]//TODO グーグル対応
		public StorageCategory storageCategory_ { get; set; }

		/// <summary>
		/// 定型文が定義されるファイルのパス
		/// </summary>
		/// <remarks>System.Designへの参照設定を追加した。</remarks>
		[DisplayNameLocalized(typeof(Resources), "PGDisplayNameTemplateFilePath"),
	   DescriptionLocalizedAttribute(typeof(Resources), "PGDescriptionTemplateFilePath"),
	   CategoryLocalizedAttribute(typeof(Resources), "PGCategoryTemplate")]
		[Editor(typeof(System.Windows.Forms.Design.FileNameEditor), typeof(System.Drawing.Design.UITypeEditor))]
		public string templateFilePath_ { get; set; }

		///// <summary>
		///// 定型文定義ファイルを暗号化するかどうか
		///// </summary>		
		//[PropertyDisplayName("暗号化指定"), Description("定型文定義ファイルの内容を暗号化するかどうか"), Category("定型文定義")]
		//public bool bEncryptTemplateFile_ { get; set; }

		[DisplayNameLocalized(typeof(Resources), "PGDisplayNameBackup"),
	   DescriptionLocalizedAttribute(typeof(Resources), "PGDescriptionBackup"),
	   CategoryLocalizedAttribute(typeof(Resources), "PGCategoryTemplate")]
		public bool bBackup_ { get; set; }

		#endregion

        #region スクリプト

		[DisplayNameLocalized(typeof(Resources), "PGDisplayNameUseScript"),
	   DescriptionLocalizedAttribute(typeof(Resources), "PGDescriptionUseScript"),
	   CategoryLocalizedAttribute(typeof(Resources), "PGCategoryScript")]
		[RefreshProperties(RefreshProperties.All)]  // この値が変わると、sriptFileFolder_が変わるため。
		public bool useSript_ { get; set; }

        /// <summary>
        /// 定型文が定義されるファイルのパス
        /// </summary>
        /// <remarks>System.Designへの参照設定を追加した。</remarks>
		[DisplayNameLocalized(typeof(Resources), "PGDisplayNameScriptFileFolder"),
	   DescriptionLocalizedAttribute(typeof(Resources), "PGDescriptionScriptFileFolder"),
	   CategoryLocalizedAttribute(typeof(Resources), "PGCategoryScript")]
		[EditorAttribute(typeof(System.Windows.Forms.Design.FolderNameEditor), typeof(System.Drawing.Design.UITypeEditor))]
		public string sriptFileFolder_ { get; set; }
        #endregion

		//public Version ApplicationVersion = Environment.Version;

		public static string APP_DATA_FOLDER_ = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),Application.ProductName);

		/// <summary>
		/// コンストラクタ
		/// </summary>
		public AplSetting()
		{
			Directory.CreateDirectory(APP_DATA_FOLDER_);
			Trace.WriteLine("Create " + APP_DATA_FOLDER_);

			//bEncryptTemplateFile_ = false;

			bMinimizeOnStart_ = false;
			maxHistory_ = 100;
			bUseBufferText_ = false;
			maxBufferText_ = 25;
			bInheritHistory_ = true;

			imageHistoryOptions_ = new ImageHistoryOptions();

			storageCategory_ = StorageCategory.local;
			//templateFilePath_ = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), Properties.Settings.Default.DefaultTemplateFileName);
			templateFilePath_ = Path.Combine(APP_DATA_FOLDER_, Properties.Settings.Default.DefaultTemplateFileName);
			bBackup_ = true;

			useSript_ = true;

			hkActive_ = Keys.A | Keys.Shift | Keys.Control;
			hkDeleteHistory_ = Keys.D | Keys.Shift | Keys.Control;
			hkTemplatePopup_ = Keys.Q | Keys.Shift | Keys.Control;
			
			bAlwaysTop_ = false;

			opacity_ = OPACITY_RANGE[1];

			bCheckFileList_ = false;

			sriptFileFolder_ = string.Empty;

			WindowFont_ = global::ClipClop.Properties.Settings.Default.DefaultFont;
		}

		public static AplSetting Create(string setting)
		{
			if (string.IsNullOrEmpty(setting))
				return new AplSetting();

			try
			{
				XmlSerializer serializer = new XmlSerializer(typeof(AplSetting));

				AplSetting cls = null;
				using (StringReader sr = new StringReader(setting))
				{
					cls = (AplSetting)serializer.Deserialize(sr);
				}
				return cls;
			}
			catch
			{
				return new AplSetting();
			}
		}

		public override string ToString()
		{
			//XmlSerializerオブジェクトを作成
			//書き込むオブジェクトの型を指定する
			XmlSerializer serializer = new XmlSerializer(typeof(AplSetting));

			string ret = string.Empty;
			using (StringWriter sr = new StringWriter())
			{
				serializer.Serialize(sr, this);
				ret = sr.ToString();
			}
			return ret;
		}


		public int GetHotKeyCount()
		{
			return Enum.GetNames(typeof(Hotkeys)).Length;
		}

		public System.Windows.Forms.Keys GetHotKeyAt(int index)
		{
			switch ((Hotkeys)index)
			{
				case Hotkeys.Active:
					return this.hkActive_;
				case Hotkeys.DeleteHistory:
					return this.hkDeleteHistory_;
				case Hotkeys.TemplatePopup:
					return this.hkTemplatePopup_;
			}
			return System.Windows.Forms.Keys.None;
		}

		#region 動的変更
		/// <summary>
		/// DynamicTypeDescriptionProvider　から呼ばれる関数（この関数名で文字列検索して呼んでいる）
		/// </summary>
		/// <param name="pdc"></param>
		public void ModifyDynamicProperties(PropertyDescriptorList pdl)
		{
			//TODO 関数にする

			PropertyDescriptor pdSriptFileFolder = pdl.Find("sriptFileFolder_", false);
			if (pdSriptFileFolder != null)
			{
				//このあと追加するので取り除く
				pdl.Remove(pdSriptFileFolder);

				if (useSript_ == true)
				{
					pdl.Add(TypeDescriptor.CreateProperty(this.GetType(), pdSriptFileFolder, new BrowsableAttribute(true), new ReadOnlyAttribute(false)));
				}
				else
				{
					pdl.Add(TypeDescriptor.CreateProperty(this.GetType(), pdSriptFileFolder, new BrowsableAttribute(false)));
				}
			}

			PropertyDescriptor pdTemplateFilePath = pdl.Find("templateFilePath_", false);
			if (pdTemplateFilePath != null)
			{
				//このあと追加するので取り除く
				pdl.Remove(pdTemplateFilePath);

				switch(this.storageCategory_)
				{
					case StorageCategory.local:
						pdl.Add(TypeDescriptor.CreateProperty(this.GetType(), pdTemplateFilePath, new BrowsableAttribute(true), new ReadOnlyAttribute(false)));

						break;
					case StorageCategory.google:
						pdl.Add(TypeDescriptor.CreateProperty(this.GetType(), pdTemplateFilePath, new BrowsableAttribute(false)));
						break;
				}
			}

			PropertyDescriptor pdMaxBufferText = pdl.Find("MaxBufferText", false);
			if (pdMaxBufferText != null)
			{
				//このあと追加するので取り除く
				pdl.Remove(pdMaxBufferText);

				if (this.bUseBufferText_ == true)
				{
					pdl.Add(TypeDescriptor.CreateProperty(this.GetType(), pdMaxBufferText, new BrowsableAttribute(true), new ReadOnlyAttribute(false)));
				}
				else{
					pdl.Add(TypeDescriptor.CreateProperty(this.GetType(), pdMaxBufferText, new BrowsableAttribute(false)));
				}
			}

			/*
					PropertyDescriptor pd = pdl.Find("PropertyC", false);

					pdl.Remove(pd);

					switch (PropertyB)
					{
						case Action.Show:
						pdl.Add( TypeDescriptor.CreateProperty( this.GetType(), pd, new BrowsableAttribute(true), 
						new ReadOnlyAttribute(false)));             
						break;
						case Action.Hide:
						pdl.Add(TypeDescriptor.CreateProperty(this.GetType(), pd, new BrowsableAttribute(false)));
						break;
						case Action.MakeReadOnly:
						pdl.Add(TypeDescriptor.CreateProperty(this.GetType(), pd, new BrowsableAttribute(true),
						new ReadOnlyAttribute(true)));
						break;
					}

					PropertyDescriptor pdOnFlyE = pdl.Find("PropertyE", false);

					if (PropertyD == true && pdOnFlyE == null)
					{
						PropertyDescriptor pdNew = new PseudoPropertyDescriptor(this, true, "PropertyE", typeof(bool),
						new BrowsableAttribute(true),
						new ReadOnlyAttribute(false),
						new DescriptionAttribute("This property was created on the fly(Pseudo)."), 
						new SortOrderAttribute(5));
						pdNew.AddValueChanged(this, new EventHandler(this.OnChanged)); // we want a change notification

						// NOTE: In you practical scnerio, you would probably keep a reference of this new PropertyDescriptor
						// one class level memeber variable.  This way you can access its info whenever you need it

						pdl.Add(pdNew);
					}
					else if (PropertyD == false && pdOnFlyE != null)
					{
						pdl.Remove(pdOnFlyE);
					}
			*/
		}


		/// <summary>
		/// PropertyDescriptor.AddValueChangedで指定する、変更時に呼ばれる
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void OnChanged(object sender, EventArgs e)
		{
			//PseudoPropertyDescriptor ppd = (PseudoPropertyDescriptor)sender;
		}


		#endregion
	}
}
