using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Text;
using System.Linq;
using glue.Collections.XSpinLock;
using glue.Extensions.Enumerable;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial struct TfsEdge : IEquatable<TfsEdge>, ParseChart.IMotherDaughter, ISysObj
	{
		public readonly static IList<TfsEdge> rg_tfsedge_empty = new TfsEdge[0];

		public static TfsEdge TargetTfsEdge = new TfsEdge(Tray.TargetId, default(Edge));

		public TfsEdge(int ttid, Edge edge)
		{
			this.ttid = ttid;
			this.Edge = edge;
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int ttid;

		public Edge Edge;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public bool IsTarget { get { return (ttid & 0xFFFFFFC0) == Tray.TargetId; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Tray Tray { get { return TrayMgr.all_trays[ttid & 0x3F]; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int TfsId { get { return ttid >> 6; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public bool IsNull { get { return ttid == default(int); } }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		public bool AtomicallySetToNull()
		{
			return Interlocked.CompareExchange(ref ttid, default(int), ttid) == ttid;
		}

		public bool PseudoAtomicStoreTo(ref TfsEdge dest)
		{
			int obs = dest.ttid;
			if (Interlocked.CompareExchange(ref dest.ttid, this.ttid, obs) != obs)
				return false;
			dest.Edge = this.Edge;
			return true;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public Type Type
		{
			get
			{
				TypeMgr tm = TrayMgr.all_trays[ttid & 0x3F].tm;
				return (Edge.FlagsId & Edge.Flag.EtmMask) == agree.Edge.Flag.EtmString ?
					tm.StringType :
					tm.type_arr[(int)(Edge.FlagsId & tm.MultiIdMask)];
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public TfsEdge WrapEdgeInSameTfs(Edge e)
		{
			return new TfsEdge(this.ttid, e);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Create a copy of this TFS which will be treated as distinct if incorporated as some sub-part of a parent TFS
		/// multiple times during TFS expansion.
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public TfsEdge Clone()
		{
			return TrayMgr.all_trays[ttid & 0x3F].CloneTfs(this);
		}

		public String ToPathList(String label)
		{
			return TrayMgr.all_trays[ttid & 0x3F].ToPathList(Edge, label);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public HashSet<PoolMark> PoolMarksBelow
		{
			get { return TrayMgr.all_trays[ttid & 0x3F].PoolMarksBelow(Edge); }
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IDictionary<Edge, CorefFinder.Entry> Coreferences
		{
			get { return TrayMgr.all_trays[ttid & 0x3F].GetCoreferences(Edge); }
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IEnumerable<ConstraintPool> ConstrainedPoolsOnly
		{
			get
			{
				int m = Edge.Mark;
				if (m == 0)
					yield break;
				Tray tr = TrayMgr.all_trays[ttid & 0x3F];
				ConstraintPool[] rgcp = tr.Pools;
				foreach (int fix in tr.tm.GetEdgeType((Edge.Flag)Edge.FlagsId)._deprec_feat_indexes)
				{
					ConstraintPool cp = rgcp[fix];
					if (cp.ContainsInMark(m))
						yield return cp;
				}
			}
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IEnumerable<ConstraintPool> AllPools
		{
			get
			{
				Tray tr = TrayMgr.all_trays[ttid & 0x3F];
				ConstraintPool[] rgcp = tr.Pools;
				foreach (int fix in tr.tm.GetEdgeType((Edge.Flag)Edge.FlagsId)._deprec_feat_indexes)
					yield return rgcp[fix];
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		public void RegisterName(String s)
		{
			Tray.RegisterTfsName(this, s);
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public String Name
		{
			get
			{
#if DEBUG
				int tfsid = TfsId;
				String s_tfsid = tfsid == 2 ? "T" : tfsid.ToString("X");
				return String.Format("{0:X}:{1} {2}", ttid & 0x3F, s_tfsid, Edge);//Type.Name);
#else
				return Tray.GetTfsName(this) ?? (ttid & 0x3F).ToString("X") + ":" + TfsId.ToString("X");
#endif
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public bool Equals(TfsEdge other)
		{
			return Edge.Mark == other.Edge.Mark && Edge.FlagsId == other.Edge.FlagsId && ttid == other.ttid;
		}
		public static bool operator ==(TfsEdge x, TfsEdge y) { throw new NotImplementedException(); }
		public static bool operator !=(TfsEdge x, TfsEdge y) { throw new NotImplementedException(); }
		public override bool Equals(object obj)
		{
			return obj is TfsEdge &&
					Edge.Mark == ((TfsEdge)obj).Edge.Mark &&
					Edge.FlagsId == ((TfsEdge)obj).Edge.FlagsId &&
					ttid == ((TfsEdge)obj).ttid;
		}
		public override int GetHashCode()
		{
			return Edge.Mark + (int)Edge.FlagsId + (int)ttid;
		}

		public override string ToString() { return Name; }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// ParseChart.IMotherDaughter
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IEnumerable<TfsEdge> RuleDaughters
		{
			get
			{
				Edge e;
				Tray tr = TrayMgr.all_trays[ttid & 0x3F];
				if (!tr.RuleArgsPath.GetEdge(this.Edge.Mark, out e))
					yield break;

				foreach (Edge x in tr.GetListEdges(e))
					yield return WrapEdgeInSameTfs(x);
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// ParseChart.IMotherDaughter
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public bool SpinCompare(TfsEdge other)
		{
			return this.Type == other.Type;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// ParseChart.IMotherDaughter
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public TfsEdge Contents
		{
			get { return this; }
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// IDisposable(ParseChart.IMotherDaughter)
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public void Dispose()
		{
			TrayMgr.all_trays[ttid & 0x3F].ReleaseTfs(this);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// TfsCoref manages a dictionary of coreferenced edges. The dictionary uses this special comparer so that dictionary
		/// operations only consider the TFS id and the out mark, and not the type or coreference status of the edge.
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public class TfsMarkComparer : IEqualityComparer<TfsEdge>
		{
			static public TfsMarkComparer Instance = new TfsMarkComparer();

			public bool Equals(TfsEdge x, TfsEdge y) { return x.ttid == y.ttid && x.Edge.Mark == y.Edge.Mark; }
			public int GetHashCode(TfsEdge obj) { return (int)obj.ttid + obj.Edge.Mark; }
		};

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// ISysObj.SysObjName
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public string SysObjName
		{
			get { return ToString(); }
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// ISysObj.SysObjDescription
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public string SysObjDescription
		{
			get { return ToString(); }
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// ISysObj.SysObjChildren
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public glue.Collections.ReadOnly.IReadOnlyDictionary<string, ISysObj> SysObjChildren
		{
			get { return SysObjHelper<ISysObj>.Empty; }
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// ISysObj.SysObjParent
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public ISysObj SysObjParent
		{
			get { return Tray.tm.g; }
		}


		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		///
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		static readonly EdgeCount[] rg_ec_empty = new EdgeCount[0];
		public EdgeCount[] GetCorefCounts()
		{
			if ((Edge.FlagsId & Edge.Flag.EtmNonBareType) == 0)
				return rg_ec_empty;

			return new CorefFinder(TrayMgr.all_trays[ttid & 0x3F], Edge)
						.Result
						.Select(kvp => new EdgeCount(kvp.Key, kvp.Value.Count))
						.ToArray();
		}

	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial struct EdgeCount
	{
		public EdgeCount(Edge e, int count)
		{
			this.edge = e;
			this.count = count;
		}
		public Edge edge;
		public int count;
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial class Tfs
	{
		public Tfs(TfsEdge te, EdgeCount[] corefs)
		{
			this.te = te;
			this.corefs = corefs;
		}

		public Tfs(TfsEdge te)
		{
			this.te = te;
			this.corefs = te.GetCorefCounts();
		}

		public TfsEdge te;
		public EdgeCount[] corefs;

		public int CorefCount { get { return corefs.Length; } }
	};

}