using System;
using System.Diagnostics;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Collections.Concurrent;
using System.Runtime.Serialization;

using miew.String;
using miew.String.Builder;
using miew.Debugging;
using miew.Enumerable;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public struct PathEdge
	{
		public PathEdge(IEnumerable<int> fix_path, Edge e)
		{
			this.path = fix_path as int[] ?? fix_path.ToArray();
			this.e = e;
		}
		public int[] path;
		public Edge e;

#if DEBUG
		public override string ToString()
		{
			return String.Format("{0} {1}", path.Select(fix => Grammar._singleton.tm.feat_arr[fix].feature.ToUpper()).StringJoin("."), e);
		}
#endif
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public struct FsPathPair
	{
		public FsPathPair(FsPath path1, FsPath path2)
		{
			this.path1 = path1;
			this.path2 = path2;
		}
		public FsPathPair(String s1, String s2)
		{
			this.path1 = new FsPath(s1);
			this.path2 = new FsPath(s2);
		}
		public FsPath path1;
		public FsPath path2;
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public sealed class FsPath : IList<String>, IEquatable<FsPath>
	{
		static public readonly FsPath Empty = new FsPath(new String[0]);

		static readonly Char[] dot_space = { '.', ' ' };
		static readonly Char[] outer_trim = { '\"', '\'', '(', ')', '[', ']' };
		readonly String[] arr;
		readonly int hc;

		public FsPath(IEnumerable<String> rgs)
		{
			/// interning strings here in order to guarantee the sufficiency of explicit reference 
			/// equality in the Equals() override
			this.arr = rgs.Select(s => String.Intern(s.ToLower().Trim())).Where(s => !String.IsNullOrEmpty(s)).ToArray();

			hc = arr.Length;
			for (int i = 0; i < arr.Length; i++)
				hc = hc ^ (i + arr[i].GetHashCode());
		}

		public FsPath(String s)
			: this(s.Trim(outer_trim).Split(dot_space, StringSplitOptions.RemoveEmptyEntries))
		{
		}

		public String this[int index]
		{
			get { return arr[index]; }
			set { throw new InvalidOperationException(); }
		}

		public int IndexOf(String item) { return Array.IndexOf<String>(arr, item); }
		public bool Contains(String item) { return Array.IndexOf<String>(arr, item) != -1; }
		public void CopyTo(String[] array, int arrayIndex) { Array.Copy(arr, array, arr.Length); }
		public int Count { get { return arr.Length; } }
		public bool IsReadOnly { get { return true; } }

		public IEnumerator<String> GetEnumerator() { return arr.AsEnumerable<String>().GetEnumerator(); }
		IEnumerator IEnumerable.GetEnumerator() { return arr.GetEnumerator(); }
		public void Insert(int index, string item) { throw new InvalidOperationException(); }
		public void RemoveAt(int index) { throw new InvalidOperationException(); }
		public void Add(String item) { throw new InvalidOperationException(); }
		public void Clear() { throw new InvalidOperationException(); }
		public bool Remove(String item) { throw new InvalidOperationException(); }

		public bool StartsWith(FsPath b)
		{
			if (Object.Equals(this, b))
				return true;
			int n = b.arr.Length;
			if (arr.Length < n)
				return false;
			for (int i = 0; i < n; i++)
				if (!Object.Equals(arr[i], b.arr[i]))
					return false;
			return true;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 'Object' overrides (and related)
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		public bool Equals(FsPath other)
		{
			if (other == null || hc != other.hc)
				return false;
			for (int i = 0; i < arr.Length; i++)
				if (!Object.Equals(arr[i], other.arr[i]))
					return false;
			return true;
		}

		public override bool Equals(Object obj)
		{
			FsPath o = obj as FsPath;
			return o != null && hc == o.hc && this.Equals(o);
		}

		public override int GetHashCode() { return hc; }

		public override String ToString() { return arr.StringJoin(".").ToUpper(); }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public class TypeMgrCompiledFsPath : IEquatable<TypeMgrCompiledFsPath>, IEnumerable<int>
	{
		readonly protected int[] rg_fix;
		readonly int hc;
		readonly protected TypeMgr tm;

		public TypeMgrCompiledFsPath(TypeMgr tm, IEnumerable<int> rgfix)
		{
			this.tm = tm;
			this.rg_fix = rgfix.ToArray();

			hc = rg_fix.Length;
			for (int i = 0; i < rg_fix.Length; i++)
			{
				int fix = rg_fix[i];
				Debug.Assert(fix.GetHashCode() == fix);
				if (fix == -1)
				{
					Array.Resize(ref rg_fix, i);
					break;
				}
				hc = hc ^ (i + fix);
			}
		}

		public TypeMgrCompiledFsPath(TypeMgr tm, IEnumerable<String> ies)
			: this(tm, ies.Select(s =>
				{
					FeatureInfo fi;
					return tm.feat_map.TryGetValue(s, out fi) ? fi.i_feat : -1;
				}))
		{
		}

		public TypeMgrCompiledFsPath(TypeMgr tm, FsPath fsp)
			: this(tm, (IEnumerable<String>)fsp)
		{
		}

		public TypeMgr TypeMgr { get { return tm; } }

		public bool GetEdge(Tfs tfs, out Edge e)
		{
			e = tfs.Edge;
			foreach (int i in rg_fix)
			{
				if (e.Mark == 0 || !tfs.TryGetEdge(i, e.Mark, out e))
					return false;
			}
			return true;
		}
		public int GetMark(Tfs tfs)
		{
			Edge e = tfs.Edge;
			foreach (int i in rg_fix)
			{
				if (e.Mark == 0 || !tfs.TryGetEdge(i, e.Mark, out e))
					throw new Exception();
			}
			return e.Mark;
		}
		public IEnumerable<PathEdge> GetListPathEdges(Tfs tfs)
		{
			Edge e = tfs.Edge;
			foreach (int i in rg_fix)
			{
				if (e.Mark == 0 || !tfs.TryGetEdge(i, e.Mark, out e))
					yield break;
			}

			/// e.Type must unify down to tt_list_parent, but we don't need to check this separately because the ListHead pool 
			/// is only appropriate for types that are lists, so the Mark won't be found if e.Type is not a list.
			Edge lh_edge;
			List<int> fix_path = new List<int>(rg_fix.Length + 4);
			fix_path.AddRange(rg_fix);
			while (tfs.TryGetEdge(tm.f_ix_list_head, e.Mark, out lh_edge))
			{
				fix_path.Add(tm.f_ix_list_head);
				yield return new PathEdge(fix_path, lh_edge);

				fix_path[fix_path.Count - 1] = tm.f_ix_list_tail;
				if (!tfs.TryGetEdge(tm.f_ix_list_tail, e.Mark, out e) || e.Mark == 0)
					yield break;
			}
		}

		public IEnumerable<ConstraintRef> WalkPath(Tfs tfs)
		{
			Edge e = tfs.Edge;
			foreach (int i in rg_fix)
			{
				yield return new ConstraintRef(tfs, e, i);
				if (e.Mark == 0 || !tfs.TryGetEdge(i, e.Mark, out e))
					throw new Exception();
			}
		}

		public Type GetType(Tfs tfs)
		{
			Edge e = tfs.Edge;
			foreach (int i in rg_fix)
				if (e.Mark == 0 || !tfs.TryGetEdge(i, e.Mark, out e))
					return null;
			return tm.GetEdgeType(e.FlagsId);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 'Object' overrides
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		public bool Equals(TypeMgrCompiledFsPath other)
		{
			if (other == null || hc != other.hc)
				return false;
			for (int i = 0; i < rg_fix.Length; i++)
				if (rg_fix[i] != other.rg_fix[i])
					return false;
			return true;
		}

		public override bool Equals(object obj)
		{
			throw new NotImplementedException("Please use strongly-typed override.");
		}

		public override int GetHashCode() { return hc; }

		public override String ToString()
		{
			return rg_fix.Select(i => tm.feat_arr[i].feature).StringJoin(".");
		}

		public IEnumerator<int> GetEnumerator() { return rg_fix.AsEnumerable<int>().GetEnumerator(); }

		IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
	};
}