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 glue.Extensions.String;
using glue.Extensions.String.Builder;
using glue.Debugging;
using glue.Extensions.Enumerable;
using glue.Collections.XSpinLock;

namespace agree
{

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <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 guarantee the sufficiency of 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 static implicit operator String[](FsPath fsp) { return fsp.arr; }
		//public static implicit operator FsPath(String[] rgs) { return new FsPath(rgs); }

		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 Equals(FsPath other)
		{
			Debug.Assert(!Object.Equals(this, null));

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

		public static bool operator ==(FsPath a, FsPath b)
		{
			if (Object.Equals(a, null))
				return Object.Equals(b, null);
			return a.Equals(b);
		}
		public static bool operator !=(FsPath a, FsPath b)
		{
			if (Object.Equals(a, null))
				return !Object.Equals(b, null);
			return !a.Equals(b);
		}

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

		public override bool Equals(Object obj)
		{
			FsPath o = (FsPath)obj;
			return hc == o.hc && 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>
	{
		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++)
				hc = hc ^ (i + rg_fix[i].GetHashCode());
		}

		public TypeMgrCompiledFsPath(TypeMgr tm, IEnumerable<String> ies)
			: this(tm, ies.Select(s => tm.feat_map[s].i_feat))
		{
		}

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

		public TypeMgr TypeMgr { get { return tm; } }

		public bool GetEdge(TfsEdge te, out Edge e)
		{
			e = te.Edge;
			ConstraintPool[] rgcp = te.Tray.Pools;
			foreach (int i in rg_fix)
			{
				if (e.Mark == 0 || !rgcp[i].TryGetEdge(e.Mark, out e))
					return false;
			}
			return true;
		}

		public bool GetTfsEdge(TfsEdge te, out TfsEdge te_out)
		{
			Tray tr = te.Tray;
			te_out = tr.CreateTfs(te.Edge);
			ConstraintPool[] rgcp = tr.Pools;
			foreach (int i in rg_fix)
			{
				int m = te_out.Edge.Mark;
				if (m == 0 || !rgcp[i].TryGetEdge(m, out te_out.Edge))
					return false;
			}
			return true;
		}

		public Type GetType(TfsEdge te)
		{
			Edge e = te.Edge;
			ConstraintPool[] rgcp = te.Tray.Pools;
			foreach (int i in rg_fix)
				if (
					e.Mark == 0 ||
					!rgcp[i].TryGetEdge(e.Mark, out e))
					return null;
			return tm.GetEdgeType(e.FlagsId);
		}

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

		public bool Equals(TypeMgrCompiledFsPath other)
		{
			Debug.Assert(!Object.Equals(this, null));

			if (Object.Equals(other, null) || hc != other.hc || rg_fix.Length != other.rg_fix.Length)
				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();
		}

		public override int GetHashCode() { return hc; }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public sealed class TrayCompiledFsPath : TypeMgrCompiledFsPath, IList<ConstraintPool>
	{
		readonly Tray tr;
		readonly ConstraintPool[] rgcp;

		public TrayCompiledFsPath(Tray tr, IEnumerable<String> ies)
			: base(tr.tm, ies)
		{
			this.tr = tr;
			this.rgcp = rg_fix.Select(i => tr.Pools[i]).ToArray();
		}

		public TrayCompiledFsPath(Tray tr, FsPath fsp)
			: this(tr, (IEnumerable<String>)fsp)
		{
		}

		public bool GetEdge(int m, out Edge e_out)
		{
			e_out = new Edge((Edge.Flag)0, m);
			foreach (ConstraintPool cp in rgcp)
			{
				if (e_out.Mark == 0 || !cp.TryGetEdge(e_out.Mark, out e_out))
					return false;
			}
			return true;
		}

		public IEnumerable<Edge> EnumerateEdges(int m)
		{
			Edge e = new Edge((Edge.Flag)0, m);
			foreach (ConstraintPool cp in rgcp)
			{
				if (e.Mark == 0 || !cp.TryGetEdge(e.Mark, out e))
					yield break;
				yield return e;
			}
		}

		public IEnumerable<PoolEdge> EnumeratePoolEdges(int m)
		{
			PoolEdge pe;
			pe.Edge = new Edge((Edge.Flag)0, m);
			foreach (ConstraintPool cp in rgcp)
			{
				if (pe.Edge.Mark == 0 || !cp.TryGetEdge(pe.Edge.Mark, out pe.Edge))
					yield break;
				pe.Pool = cp;
				yield return pe;
			}
		}

		public bool GetTypeId(int m, out Edge.Flag f)
		{
			Edge e = new Edge((Edge.Flag)0, m);
			foreach (ConstraintPool cp in rgcp)
			{
				if (e.Mark == 0 || !cp.TryGetEdge(e.Mark, out e))
				{
					f = tm.MultiIdMask;
					return false;
				}
			}
			f = e.FlagsId;
			return true;
		}

		public Type GetType(int m)
		{
			if (rg_fix.Length == 4)
				return _GetType(m);

			Edge e = new Edge((Edge.Flag)0, m);
			foreach (ConstraintPool cp in rgcp)
			{
				if (e.Mark == 0 || !cp.TryGetEdge(e.Mark, out e))
					return null;
			}
			return tm.IsTopId(e) ? tm.TopType : tm.GetEdgeType(e.FlagsId);
		}

		Type _GetType(int m)
		{
			if (m == 0)
				throw new Exception();

			Edge e;

			if (!rgcp[0].TryGetEdge(m, out e))
				return null;

			if (e.Mark == 0)
				return null;

			if (!rgcp[1].TryGetEdge(e.Mark, out e))
				return null;

			if (e.Mark == 0)
				return null;

			if (!rgcp[2].TryGetEdge(e.Mark, out e))
				return null;

			if (e.Mark == 0)
				return null;

			if (!rgcp[3].TryGetEdge(e.Mark, out e))
				return null;

			if (e.Mark == 0)
				return null;

			return (e.FlagsId & Edge.Flag.EtmMask) == Edge.Flag.EtmString ? tm.StringType : tm.type_arr[(int)(e.FlagsId & tm.MultiIdMask)];
		}

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

		public bool Contains(ConstraintPool item)
		{
			return Array.IndexOf<ConstraintPool>(rgcp,item) != -1;
		}

		public int Count { get { return rgcp.Length; } }

		public bool IsReadOnly { get { return true; } }

		public void CopyTo(ConstraintPool[] array, int arrayIndex)
		{
			foreach (ConstraintPool cp in rgcp)
				array[arrayIndex++] = cp;
		}

		public IEnumerator<ConstraintPool> GetEnumerator()
		{
			return rgcp.AsEnumerable<ConstraintPool>().GetEnumerator();
		}

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

		public int IndexOf(ConstraintPool item)
		{
			return Array.IndexOf<ConstraintPool>(rgcp, item);
		}

		public void Add(ConstraintPool item) { throw new InvalidOperationException(); }
		public void Clear() { throw new InvalidOperationException(); }
		public bool Remove(ConstraintPool item) { throw new InvalidOperationException(); }
		public void Insert(int index, ConstraintPool item) { throw new InvalidOperationException(); }
		public void RemoveAt(int index) { throw new InvalidOperationException(); }
	};

}