using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using miew.Debugging;
using miew.Enumerable;
using miew.String;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class MarkingTfs : Tfs
	{
		public MarkingTfs(TypeMgr tm)
			: base(tm)
		{
		}
		public MarkingTfs(TypeMgr tm, Edge e)
			: base(tm, e)
		{
		}
		public MarkingTfs(TypeMgr tm, Type t, bool f_coref)
			: base(tm)
		{
			Edge = CreateEdge(t, f_coref);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Auto-mark based on the bareness of the type
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public Edge CreateEdge(Type t, bool f_coref)
		{
			Debug.Assert(t != null && t.tm == this.tm);
			Edge.Flag f;

			if (t == tm.StringType)
				f = Edge.Flag.EtmString;
			else
			{
				f = (Edge.Flag)t.m_id;
				if ((t.m_flags & Type.Flags.HasAnyFeatures) > 0)
					f |= Edge.Flag.EtmNonBareType;
			}

			if (f_coref)
				f |= Edge.Flag.Coreference;

			int m = (f & (Edge.Flag.Coreference | Edge.Flag.EtmNonBareType)) == 0 ? 0 : next_mark++;

			return new Edge(f, m);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Auto-mark based on the bareness of the type
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public Edge CreateEdge(Edge.Flag f)
		{
			Debug.Assert(tm.GetEdgeType(f).IsBare == ((f & Edge.Flag.EtmNonBareType) == 0));

			return new Edge(f, (f & (Edge.Flag.Coreference | Edge.Flag.EtmNonBareType)) == 0 ? 0 : next_mark++);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Create an edge which will be used in this tray. The edge is not persisted into the tray.
		/// Caller may not submit a non-bare mark if the requirements for a non-bare mark are not met
		/// But caller can optionally submit a bare mark for any new edge; the current design enforces that non-bare
		/// types will always have a non-bare mark
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public Edge CreateRecycledEdge(Type t, int m, bool f_coref)
		{
			Debug.Assert(t != null && t.tm == this.tm);

			Edge.Flag f;
			if (t == tm.StringType)
				f = Edge.Flag.EtmString;
			else
			{
				f = (Edge.Flag)t.m_id;
				if ((t.m_flags & Type.Flags.HasAnyFeatures) > 0)
					f |= Edge.Flag.EtmNonBareType;
			}

			if (f_coref)
				f |= Edge.Flag.Coreference;

			if ((f & (Edge.Flag.Coreference | Edge.Flag.EtmNonBareType)) != 0)
			{
				if (m == 0)
					m = next_mark++;
			}
			else
			{
				/// Caller may not submit a non-bare mark if the requirements for a non-bare mark are not met
				Debug.Assert(m == 0);
			}
			return new Edge(f, m);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Create an edge with the specified type, coreference status, and mark.
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public Edge CreateRecycledEdge(Edge.Flag f, int m)
		{
			Debug.Assert(tm.GetEdgeType(f).IsBare == ((f & Edge.Flag.EtmNonBareType) == 0));

			if ((f & (Edge.Flag.Coreference | Edge.Flag.EtmNonBareType)) != 0)
			{
				if (m == 0)
					m = next_mark++;
			}
			else
			{
				/// Caller may not submit a non-bare mark if the requirements for a non-bare mark are not met
				Debug.Assert(m == 0);
			}
			return new Edge(f, m);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public void ChangeEdgeCoreference(FeatMark fm, bool f_coref)
		{
			Debug.Assert(fm.m != 0);

			Edge e = GetEdge(fm.i_feat, fm.m);
			if (f_coref)
			{
				Debug.Assert(!e.IsCoreferenced);
				if (tm.IsTopId(e))
					e = CreateEdge(tm.TopType, true);
				else
					e = CreateRecycledEdge(e.FlagsId | Edge.Flag.Coreference, e.Mark);
				SetEdge(fm.i_feat, fm.m, e);
			}
			else
			{
				Debug.Assert(e.IsCoreferenced);
				if (tm.IsTopId(e))
					RemoveEdge(fm.i_feat, fm.m);
				else
				{
					Debug.Assert(tm.GetEdgeType(e.FlagsId).IsBare == ((e.FlagsId & Edge.Flag.EtmNonBareType) == 0));
					SetEdge(fm.i_feat, fm.m, new Edge(e.FlagsId & ~Edge.Flag.Coreference, (e.FlagsId & Edge.Flag.EtmNonBareType) == 0 ? 0 : e.Mark));
				}
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Create a string value edge for a unique string value
		/// </summary>
		/// <remarks>
		/// note: empty string is distinct from unconstrained string type
		///			s1 := *top* & [ X *top* ].
		///			s2 := s1 & [ X "foo" ].
		///			s3 := s1 & [ X "" ].
		///			s4 := s2 & s3.
		/// </remarks>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public Edge CreateStringEdge(String s)
		{
			if (tm.IsPetrified)
				throw new Exception("Type hierarchy has already finished loading, cannot add more strings");

			int sid = tm.strings.GetOrAdd(s);

			return new Edge(Edge.Flag.EtmString | (Edge.Flag)sid, 0);
		}
	};
}