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

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// ConstraintRef is a value type. It consists of:
	/// - reference to a top-level TFS which provides access to edges
	/// - a current 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.
	/// - a selected feature index, relative to the TypeMgr of the TFS
	///   
	/// 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 is not available given only an Edge.
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial struct ConstraintRef
	{
		public static readonly ConstraintRef[] NoneBelow = new ConstraintRef[0];

		public ConstraintRef(Tfs tfs, Edge host, int i_feat)
		{
			this.Host = host;
			this.i_feat = i_feat;
			this.tfs = tfs;
		}
		public ConstraintRef(Tfs host)
		{
			this.Host = host.Edge;
			this.tfs = host;
			this.i_feat = -1;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Tfs tfs;

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

		public void SetHostType(Type new_type)
		{
			if (HostType != new_type)
			{
				Host = ((MarkingTfs)tfs).CreateRecycledEdge(new_type, Host.Mark, Host.IsCoreferenced);
				if (!HostType.HasFeature(i_feat))
					i_feat = -1;
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// The selected feature is determined by a non-null ConstraintPool
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int i_feat;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public FeatureInfo FeatureInfo { get { return tfs.tm.feat_arr[i_feat]; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public String Feature { get { return tfs.tm.feat_arr[i_feat].feature; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Type HostType { get { return tfs.tm.GetEdgeType(Host.FlagsId); } }

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

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Stays in the same Tray
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public void SwitchToFeature(int i_feat)
		{
			if (this.i_feat==-1 || this.i_feat != i_feat)
			{
				if (i_feat == -1 || !HostType.HasFeature(i_feat))
					throw new Exception();
				this.i_feat = i_feat;
			}
		}

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

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		///
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Type ConstraintType
		{
			get
			{
				Edge e;
				if (!tfs.TryGetEdge(i_feat, Host.Mark, out e))
					return tfs.tm.TopType;
				return tfs.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 TFS
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public ConstraintRef NextConstraint(int i_nf)
		{
			return new ConstraintRef(tfs, tfs.GetEdge(this.i_feat, Host.Mark), i_nf);
		}

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

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

			if (glb_id != tid)
			{
				Edge old_edge = tfs.GetEdge(i_feat, Host.Mark);
				tfs.SetEdge(i_feat, Host.Mark, ((MarkingTfs)tfs).CreateRecycledEdge(t.tm.type_arr[glb_id], old_edge.Mark, old_edge.IsCoreferenced));
			}
			return true;
		}
	};
}