using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using CaLib.User;
using System.Diagnostics;

namespace ClipClop.User
{
	/// <summary>
	/// 改行だけの行は無視される
	/// - だけの行はセパレータ
	/// > が先頭にある行は階層化する
	/// *EX tab 表示する文字列:コマンド文字列
	/// *E	NOTEPAD "%1"
	/// *T	テキスト履歴(&T)
	/// *C	クリップボード履歴(&C)
	/// *P	テキスト(&P)
	/// </summary>
    public class QtClipContextMenuReader : IContextMenuReader
    {
		XmlDocument doc_;

		public XmlDocument GetDocument()
		{
			return this.doc_;
		}


		/// <summary>
		/// 読み込み処理。何かあると例外投入する。
		/// </summary>
		/// <param name="filePath"></param>
        public void Read(string filePath)
        {
			doc_ = new XmlDocument();

			// <?xml version="1.0" encoding="UTF-8"?>を作成
			doc_.AppendChild(doc_.CreateXmlDeclaration("1.0", "UTF-8", ""));

			XmlElement rootElement = doc_.CreateElement(ContextMenuSettingDefinition.Root_);
			rootElement.SetAttribute(ContextMenuSettingDefinition.RootAttribute.version.ToString(), ContextMenuSettingDefinition.GetCurrentVersion());
			rootElement.SetAttribute(ContextMenuSettingDefinition.RootAttribute.magicnumber.ToString(), ContextMenuSettingDefinition.MagicNumber);

			using( FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read ))
			{
				using (StreamReader sr = new StreamReader(fs, Encoding.GetEncoding("Shift_JIS")))
				{
					int count = 0;
					string line = "";
					XmlNode currentElement = rootElement;

					while ((line = sr.ReadLine()) != null)
					{
						count++;
						currentElement = Parse(currentElement, line);

						if (currentElement == null)
						{
							throw new FundamentalException(string.Format(global::ClipClop.Properties.Resources.EF001, filePath, count));
						}
					}
				}
			}

			doc_.AppendChild(rootElement);

			//doc_.Save(filePath+".test");
        }


		/// <summary>
		/// 読み込んだ文字列の解析を行う。
		/// </summary>
		/// <param name="currentElement"></param>
		/// <param name="line"></param>
		/// <returns></returns>
		XmlNode Parse(XmlNode currentElement, string line)
		{
			if (string.IsNullOrEmpty(line) )
			{
				//改行だけの行は無視される
				return currentElement;
			}

			if ( line[0] == '\t')
			{
				//タブが行頭にある行はコメント...何も足さない
				return currentElement;
			}

			if (line == "-")
			{
				// - だけの行はセパレータ
				XmlElement newElement = doc_.CreateElement(ContextMenuSettingDefinition.Separator_);
				currentElement.AppendChild(newElement);
				return currentElement;
			}

			if (line[0] == '>')
			{
				// > が先頭にある行は階層化する
				XmlElement newElement = doc_.CreateElement(ContextMenuSettingDefinition.Folder_);
				newElement.SetAttribute(ContextMenuSettingDefinition.Name_, line.Substring(1));
				currentElement.AppendChild(newElement);
				return newElement;
			}
			if (line[0] == '<')
			{ 
				//親に移動する。
				return currentElement.ParentNode;
			}

			// 定型文
			if (!ParseSentence(currentElement, line, this.doc_))
			{
				return null;
			}
			return currentElement;
		}

		/// <summary>
		/// 読み込んだ定型文定義行を解析する。
		/// </summary>
		/// <param name="currentElement"></param>
		/// <param name="line"></param>
		/// <param name="doc"></param>
		/// <returns></returns>
		static bool ParseSentence(XmlNode currentElement, string line, XmlDocument doc)
		{
			int tabIndex = line.IndexOf('\t');

			if (line[0] == '*' && tabIndex == 1)
			{
				//特殊な定型文
				//"*" とタブが行頭にあると、その行はすべて分割して表示

				string s = line.Substring(tabIndex + 1).TrimStart();

				for( int i = 0; i < s.Length; i++ )
				{
					XmlElement node = doc.CreateElement(ContextMenuSettingDefinition.Sentence_);

					node.SetAttribute(ContextMenuSettingDefinition.Type_, ContextMenuSettingDefinition.Mode.template.ToString());
					//name要素は要らない
					//node.SetAttribute(ContextMenuSettingDefinition.Name_, string.Format("{0}", (char)('A'+i)));
					node.SetAttribute(ContextMenuSettingDefinition.Value_, string.Format("{0}", s[i]));
					currentElement.AppendChild(node);
				}
				return true;
			}

			XmlElement sentence = doc.CreateElement(ContextMenuSettingDefinition.Sentence_);

			if (tabIndex < 0)
			{
				//名前省略
				sentence.SetAttribute(ContextMenuSettingDefinition.Type_, ContextMenuSettingDefinition.Mode.template.ToString());
				sentence.SetAttribute(ContextMenuSettingDefinition.Value_, line);
				currentElement.AppendChild(sentence);
				return true;
			}

			//タブ文字がある場合...先頭がタブ文字であることはない
			Debug.Assert(tabIndex > 0);

			if (line[0] == '*' && tabIndex == 2 )
			{
				// *T とかに続けてタブ文字の場合
				//何か実行する

				switch (line[1])
				{
					case 'T': //テキスト保管一覧
						//sentence.SetAttribute(ContextMenuSettingDefinition.Type_, ContextMenuSettingDefinition.Mode.textfile.ToString());
						//sentence.SetAttribute(ContextMenuSettingDefinition.Name_, line.Substring(tabIndex + 1).TrimStart());
						//break;
						return true; //無視することにした

					case 'P': //テキスト保管２の一覧
						//sentence.SetAttribute(ContextMenuSettingDefinition.Type_, ContextMenuSettingDefinition.Mode.textfile.ToString());
						//sentence.SetAttribute(ContextMenuSettingDefinition.Name_, line.Substring(tabIndex + 1).TrimStart());
						//break;
						return true; //無視することにした

					case 'C': //クリップボード履歴
						sentence.SetAttribute(ContextMenuSettingDefinition.Type_, ContextMenuSettingDefinition.Mode.clipboardhistory.ToString());
						sentence.SetAttribute(ContextMenuSettingDefinition.Name_, line.Substring(tabIndex + 1).TrimStart());
						break;

					case 'E': //メニュー定義ファイルの編集(%1 はメニュー定義ファイル名で置き換えられる)
						sentence.SetAttribute(ContextMenuSettingDefinition.Type_, ContextMenuSettingDefinition.Mode.editfile.ToString());
						sentence.SetAttribute(ContextMenuSettingDefinition.Name_, global::ClipClop.Properties.Resources.MenuEdit);
						break;

					default:
						return false;
				}
				
				sentence.SetAttribute(ContextMenuSettingDefinition.Value_, line.Substring(tabIndex + 1).TrimStart());
				currentElement.AppendChild(sentence);
				return true;
			}

			if (line.StartsWith("*EX\t"))
			{
				//*EX の場合

				//"*EX" とタブが行頭にあると、任意のコマンド実行
				//文字列をタブで区切ると、１つ目はメニューに表示される文字列、２つ目は実際に入力される文字列
				//*EX は タブの後に、「表示する文字列:コマンド文字列」を記述します。

				string tmp = line.Substring(tabIndex + 1).TrimStart();

				string[] substring = tmp.Split('\t');

				if (substring.Length == 1)
				{
					substring = tmp.Split(':');
				}

				if (substring.Length != 2 || string.IsNullOrEmpty(substring[0]) || string.IsNullOrEmpty(substring[1]))
				{
					return false;
				}

				sentence.SetAttribute(ContextMenuSettingDefinition.Type_, ContextMenuSettingDefinition.Mode.execute.ToString());

				sentence.SetAttribute(ContextMenuSettingDefinition.Name_, substring[0]);
				sentence.SetAttribute(ContextMenuSettingDefinition.Value_, substring[1]);
				currentElement.AppendChild(sentence);

				return true;
			}

			if (line.StartsWith("*INIT\t"))
			{
				//*INIT の場合 テキスト保管２のファイルを読み込み直します。(v2.7)
				//現状何もしない
				return true;
			}


			//名前ありの定型文
			sentence.SetAttribute(ContextMenuSettingDefinition.Type_, ContextMenuSettingDefinition.Mode.template.ToString());
			sentence.SetAttribute(ContextMenuSettingDefinition.Name_, line.Substring(0, tabIndex));
			sentence.SetAttribute(ContextMenuSettingDefinition.Value_, line.Substring(tabIndex + 1).TrimStart());
			currentElement.AppendChild(sentence);
			return true;
		}
    }
}
