using System;
using System.Text;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// Doesn't carry the in-mark
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public struct PoolEdge
	{
		public PoolEdge(ConstraintPool cp, Edge edge)
		{
			this.Pool = cp;
			this.Edge = edge;
		}
		public ConstraintPool Pool;
		public Edge Edge;

		public override string ToString()
		{
			return String.Format("         {0} {1}", Pool.Feature.ToUpper(), Edge.ToString());
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// ConstraintRef is a value type. It consists of:
	/// - a ConstraintPool which represents the distinct repository for all uses of some single feature that is appropriate 
	///   for the hosting type;
	/// - a host edge, which consists of a 'hosting' Type (which may or may not be the same as the introducing type for the 
	///   constraint pool's feature), and an in-mark that selects an onwards edge in that constraint pool.
	///   
	/// This structure is therefore able to indicate a specific FEATURE-type pair within a TFS, while maintaining the 
	/// critically-important ability to alter that type assignment, which would normally be lost in a CLR value type.
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial struct ConstraintRef : IEquatable<ConstraintRef>
	{
		public ConstraintRef(Edge host, ConstraintPool cp)
		{
			this.Host = host;
			this.Pool = cp;
		}
		public ConstraintRef(TfsEdge host)
		{
			this.Host = host.Edge;
			this.Pool = host.Tray.UnknownPool;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// The hosting edge. For 'read' performance, the field itself is exposed, but do not 'write' this field; use
		/// SetHost() or SetHostType() instead.
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public Edge Host;

		public void SetHostType(Type new_type)
		{
			if (HostType != new_type)
			{
				Host = Pool.tr.CreateRecycledEdge(new_type, Host.Mark, Host.IsCoreferenced);
				if (!HostType.HasFeature(Pool.i_feat))
					Pool = Pool.tr.UnknownPool;
			}
		}

		public Type HostType { get { return Pool.tm.GetEdgeType(Host.FlagsId); } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public PoolMark PoolMark { get { return new PoolMark(Pool, Host.Mark); } }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// The selected feature is determined by a non-null ConstraintPool
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public ConstraintPool Pool;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public String Feature { get { return Pool.Feature; } }

		public int FeatureIndex { get { return HostType.mgr.GetFeatureIndex(Pool.Feature); } }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Stays in the same Tray
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public void SwitchToFeature(int i_feat)
		{
			if (Pool is UnknownPoolClass || Pool.i_feat != i_feat)
			{
				if (i_feat == -1 || !HostType.HasFeature(i_feat))
					throw new Exception();
				Pool = Pool.tr.Pools[i_feat];
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		///
		/// </summary>
		/// <remarks>
		/// value type semantics are weird for 'set' as a property so we use a method instead
		/// </remarks>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public Edge Constraint
		{
			get { return Pool.GetEdge(Host.Mark); }
		}
		public void SetConstraint(Edge e)
		{
			Pool.SetEdge(Host.Mark, e);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		///
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Type ConstraintType
		{
			get
			{
				Edge e;
				TypeMgr tm = Pool.tm;
				if (!Pool.TryGetEdge(Host.Mark, out e))
					return tm.TopType;
				return tm.GetEdgeType(e.FlagsId);
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// returns 'true' even if coreferenced
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public bool ConstraintTypeIsTop { get { return ConstraintType.IsTop; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public bool ConstraintTypeIsBare { get { return ConstraintType.IsBare; } }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// always stays in the same Tray
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public ConstraintRef NextConstraint(int i_feat)
		{
			return new ConstraintRef(Pool.GetEdge(Host.Mark), Pool.tr.Pools[i_feat]);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Unify types, only used by TdlNavigator
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public bool UnifyInConstraintType(Type t)
		{
			Debug.Assert(!(Pool is UnknownPoolClass), "UnifyInType: Edge has no feature selected.");

			int tid = ConstraintType.m_id;
			int glb_id = t.mgr.GetGlbCheckTopAndIdentity(tid, t.m_id);
			if (glb_id == -1)
				return false;

			if (glb_id != tid)
			{
				Edge old_edge = Pool.GetEdge(Host.Mark);
				Pool.SetEdge(Host.Mark, Pool.tr.CreateRecycledEdge(t.mgr.type_arr[glb_id], old_edge.Mark, old_edge.IsCoreferenced));
			}
			return true;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public bool Equals(ConstraintRef other)
		{
			return Host.Mark == other.Host.Mark && Pool == other.Pool && Host.FlagsId == other.Host.FlagsId;
		}
		public static bool operator ==(ConstraintRef x, ConstraintRef y) { return x.Equals(y); }
		public static bool operator !=(ConstraintRef x, ConstraintRef y) { return !x.Equals(y); }
		public override bool Equals(object obj)
		{
			throw new NotImplementedException();
		}
		public override int GetHashCode() { return (int)Host.FlagsId ^ Host.Mark ^ Pool.i_feat; }
	};
}