using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Reflection;

namespace X03.PageElements.Notations
{

	abstract public class Nestable : PageElements.Notation, Plugin.PageElement
	{
		new static public IEnumerable<Type> GetPluginClasses()
		{
			return Utils.GetPluginClasses(typeof(PageElements.Notations.Nestable));
		}
		private static PageElement createNestableElement(Type notationClass, PageElement parent, List<PageElement> children)
		{
			return (PageElement)Assembly.GetAssembly(notationClass).CreateInstance(notationClass.FullName, false, BindingFlags.CreateInstance, null, new object[] { parent, children }, null, null);
		}
		public static List<PageElement> Generate(Type notationClass, PageElement parent, List<PageElement> elements, HashSet<Type> targetClasses)
		{
			var ret = new List<PageElement>(elements);
			var patterns = notationClass.GetField("Patterns", BindingFlags.Static | BindingFlags.Public).GetValue(null) as string[];
			if (patterns == null || patterns.Length < 2)
				return ret;
			var beginElementIndexes = new Stack<int>();
			var beginMatchGroups = new Stack<Group>();
			PageElement mergeTarget = null;
			Match m = null;
			var idx = 0;
			while (idx <= ret.Count - 1)
			{
				if (ret[idx] is PageElements.Wikitext)
				{
					PageElements.Wikitext wikitext = ret[idx] as PageElements.Wikitext;
					m = Regex.Match(wikitext.ToWikitext(), @"(?<begin>" + patterns[0] + @")|(?<end>" + patterns[1] + @")", RegexOptions.Singleline | RegexOptions.IgnoreCase);
					while (m.Success)
					{
						Debug.Assert(ret[idx] is PageElements.Wikitext);
						if (m.Groups["begin"].Success)
						{
							// begin発見
							beginElementIndexes.Push(idx);
							beginMatchGroups.Push(m.Groups["begin"]);
						}
						else if (m.Groups["end"].Success && beginElementIndexes.Count >= 1 && beginMatchGroups.Count >= 1)
						{
							// end発見→PageElement生成
							Group beginMatchGroup;
							int beginElementIndex;
							int beginStringIndex;
							int beginStringLength;
							int endElementIndex;
							int endStringIndex;
							int endStringLength;
							{
								// idx用意
								beginMatchGroup = beginMatchGroups.Pop();
								beginElementIndex = beginElementIndexes.Pop();
								beginStringIndex = beginMatchGroup.Index;
								beginStringLength = beginMatchGroup.Length;
								endElementIndex = idx;
								endStringIndex = m.Groups["end"].Captures[0].Index;
								endStringLength = m.Groups["end"].Captures[0].Length;
							}
							var additions = new List<PageElement>();
							{
								// 退避 ret.GetRange() -> additions, ret.RemoveRange()
								additions.AddRange(ret.GetRange(beginElementIndex, endElementIndex - beginElementIndex + 1));
								ret.RemoveRange(beginElementIndex, endElementIndex - beginElementIndex + 1);
								idx = beginElementIndex - 1;
								//endElementIndex = -1;
							}
							{
								Debug.Assert(beginElementIndex <= endElementIndex);
								var subs = new string[6] { "", "", "", "", "", "" };
								{
									// additions.First()を3分割
									// additions.Last()を3分割
									Debug.Assert(additions.First() is PageElements.Wikitext);
									Debug.Assert(additions.Last() is PageElements.Wikitext);
									var first = ((PageElements.Wikitext)additions.First()).ToWikitext();
									var last = ((PageElements.Wikitext)additions.Last()).ToWikitext();
									subs[0] = first.Substring(0, beginStringIndex - 1 + 1);
									subs[1] = first.Substring(beginStringIndex, beginStringLength);
									if (beginElementIndex == endElementIndex)
									{
										subs[2] = "";
										subs[3] = last.Substring(beginStringIndex + beginStringLength, (endStringIndex - 1) - (beginStringIndex + beginStringLength) + 1);
									}
									else
									{
										subs[2] = first.Substring(beginStringIndex + beginStringLength);
										subs[3] = last.Substring(0, endStringIndex - 1 + 1);
									}
									subs[4] = last.Substring(endStringIndex, endStringLength);
									subs[5] = last.Substring(endStringIndex + endStringLength);
									Debug.Print("subs: {0}, first: {1}, last: {2}", subs.Sum(x => x.Length), first.Length, last.Length);
									if (beginElementIndex == endElementIndex) Debug.Assert(subs.Sum(x => x.Length) == first.Length && subs.Sum(x => x.Length) == last.Length);
									else Debug.Assert(subs.Sum(x => x.Length) == first.Length + last.Length);
								}
								{
									{
										// additions:retに追加する分
										if (beginElementIndex == endElementIndex)
										{
											additions.Remove(additions.First());
											Debug.Assert(additions.Count == 0);
										}
										else
										{
											additions.Remove(additions.First());
											additions.Remove(additions.Last());
										}
										if (subs[2].Length > 0)
											additions.Insert(0, new PageElements.Wikitext(subs[2]));
										Debug.Assert(subs[1] == beginMatchGroup.Value);
										additions.Insert(0, new PageElements.Affix(parent, subs[1])); // m[begin].Value
										if (subs[3].Length > 0)
											additions.Add(new PageElements.Wikitext(subs[3]));
										Debug.Assert(subs[4] == m.Value);
										additions.Add(new PageElements.Affix(parent, subs[4])); // m[end].Value
									}
									if (subs[5].Length > 0)
									{
										ret.Insert(beginElementIndex, new PageElements.Wikitext(subs[5]));
										idx++;
									}
									if (notationClass.GetMethod("Merge") != null)
									{
										if (beginStringIndex == 0 && mergeTarget != null && mergeTarget.GetType().Name == notationClass.Name)
										{
											// 連結(既存インスタンスと統合)。新しいインスタンスは生成しない。
											notationClass.GetMethod("Merge").Invoke(null, new object[] { parent, PageElements.Wikitext.Tokenize(parent, additions, targetClasses), mergeTarget });
											//mergeTarget はそのまま
										}
										else
										{
											// createElement
											mergeTarget = createNestableElement(notationClass, parent, PageElements.Wikitext.Tokenize(parent, additions, targetClasses));
											ret.Insert(beginElementIndex, mergeTarget);
											idx++;
										}
									}
									else
									{
										// createElement
										ret.Insert(beginElementIndex, createNestableElement(notationClass, parent, PageElements.Wikitext.Tokenize(parent, additions, targetClasses)));
										idx++;
									}
									if (subs[0].Length > 0)
									{
										ret.Insert(beginElementIndex, new PageElements.Wikitext(subs[0]));
										idx++;
									}
								}
								idx--;	// 追加された最後の要素を指すように、直後に実行されるidx++に合わせて調整
								break;
								//continue;
							}
						}
						else
						{
							//Debug.Fail(m.Value);
						}
						m = m.NextMatch();
					}
				}
				else
				{
				}
				idx++;
			}
			return ret;
		}
		public override void Accept(PageElementVisitor visitor)
		{
			throw new NotImplementedException();
		}
		public override void InAction()
		{
			throw new NotImplementedException();
		}
		public override string CreateTag()
		{
			throw new NotImplementedException();
		}
		public static string[] Patterns;
		public Nestable(PageElement parent, List<PageElement> children)
			: base(parent, children)
		{
		}
	}

}