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

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed partial class TargetTfs : BootstrapTfs//, ITargetTfs
	{
		public TargetTfs(TypeMgr tm, int c_edge_hint)
			: base(tm, c_edge_hint)
		{
			c_corefs = 0;
		}

		public Dictionary<Int64, Unification.UnifCoref> corefs = null;
		public int c_corefs = 0;

#if true
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Prune a sub-graph, and then remove any unused coreferences (and unmark the edge coreference bit) which 
		/// have only a single use remaining.
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public bool _prune_below(int i_feat, int m)
		{
			Edge e;
			if (!TryGetEdge(i_feat, m, out e))
				return false;

			int c_reentrancies = 0;
			Unification.UnifCoref uc = null;
			if (e.FlagsId < 0)
			{
				/// find coreference for this edge
				corefs.TryGetValue(((long)id << 32) | (uint)e.Mark, out uc);
				while (uc.f_redirect)
					uc = uc.next;

				/// if we are pruning the opportunistic reentrancy hint, then clear it
				if (uc.single_fm.i_feat == i_feat && uc.single_fm.m == m)
					uc.single_fm = default(FeatMark);

				c_reentrancies = --uc.c_fm;
				e = uc.e_cur;
			}

			/// only remove *below* a coreferenced edge if it has no reentrancies remaining. This allows sub-graphs to 
			/// be pruned without disturbing the coreferenced portions that are still accessible from without.
			if (c_reentrancies == 0 && (e.FlagsId & agree.Edge.Flag.EtmNonBareType) != 0)
				foreach (int fix in tm.rgrgfix_by_type[(int)(e.FlagsId & Edge.Flag.MultiIdMask)])
					_prune_below(fix, e.Mark);

			/// remove the edge
			if (!RemoveEdge(i_feat, m))
				Debug.Assert(false);

			/// all done if not coreferenced, or if additional reentrancies still point to this edge.
			if (uc == null || c_reentrancies > 1)
				return true;

			/// 0 or 1 reentrancies remain. we will decrement the coreference count at the end of this function.
			if (c_reentrancies == 1)
			{
				/// if there's were 2 reentrancies and now there's just 1 remaining, we're retaining *this* edge, 
				/// so clear its coreference bit, as stored by the *other* reentrancy, and discard the equivalence
				/// class. We make these adjustments now, because that other edge might not be getting pruned,
				/// so we might never encounter it. First, find out if the opportunistic hint is any good.
				FeatMark fm = uc.single_fm;
				if (fm.m == 0)
				{
					/// hint is no good; we must find the other reentrancy by exhaustive search
					ulong ul = d.First(kvp =>
					{
						if (kvp.Value.FlagsId >= 0)
							return false;
						var ux = corefs[((long)id << 32) | (uint)kvp.Value.Mark];
						while (ux.f_redirect)
							ux = ux.next;
						return ux == uc;
					}).Key;
					fm = new FeatMark((int)(ul >> 32), (int)ul);
				}
				Debug.Assert(ContainsFeatMark(fm.i_feat, fm.m));

				uc.c_fm = 0;
#if DEBUG
				uc.single_fm = default(FeatMark);
#endif
				/// clear coreference bit (or remove) in the other reentrancy.
				Edge.Flag nf = e.FlagsId & ~Edge.Flag.Coreference;
				if (nf == 0)
					RemoveEdge(fm.i_feat, fm.m);
				else
					SetEdge(fm.i_feat, fm.m, new Edge(nf, (nf & Edge.Flag.EtmNonBareType) == 0 ? 0 : e.Mark));
			}
			c_corefs--;
			return true;
		}
#endif

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public Tfs ToArrayTfs()
		{
#if DEBUG
			CheckCoreferences();
#endif
			ArrayTfs tfs = new ArrayTfs(tm.da, this, null);
			//MarkingArrayTfs tfs = new MarkingArrayTfs(tm, this.Edge, d, id, corefs, ref c_corefs);
			d = null;
			tfs.Name = this.Name;
			return tfs;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public MotherDaughterTfs ToMotherDaughterTfs(int[] rg_feat_delete)
		{
#if DEBUG
			CheckCoreferences();
#endif
			MotherDaughterTfs tfs = new MotherDaughterTfs(tm.da, this, rg_feat_delete);
			d = null;
			tfs.Name = this.Name;
			return tfs;
		}
#if DEBUG
		public override void CheckCoreferences()
		{
			//if (d == null)
			//{
			//    base.CheckCoreferences();
			//    return;
			//}

			///// walking method:
			//var walk = GetReentrancies(Edge)
			//    .Select(kvp => new KeyValuePair<Edge, ReentrancyFinder.Entry>(corefs[((long)id << 32) | (uint)kvp.Key.Mark].LoopMaster.e_cur, kvp.Value))	/// apply coref info
			//    .GroupBy(kvp => kvp.Key)
			//    .ToDictionary(
			//        grp => grp.Key,
			//        grp => grp.SelectMany(ue => ue.Value).Select(cref => cref.FeatMark).ToHashSet().Count);	/// ignore types since they might not be updated yet

			//if (walk.Count == 0)
			//    return;

			///// table scan method:
			//var table = d.Values
			//    .Where(e => e.FlagsId < 0)
			//    .Select(e => corefs[((long)id << 32) | (uint)e.Mark].LoopMaster.e_cur)	/// apply coref info
			//    .GroupBy(e => e.Mark)
			//    .ToDictionary(grp => grp.First(), grp => grp.Count());

			//if (walk.Count > table.Count)
			//{
			//    var ex = walk.Keys.Except(table.Keys).ToArray();
			//    if (Debugger.IsAttached)
			//        Debugger.Break();
			//    else
			//        throw new Exception();
			//}
			//if (table.Count > walk.Count)
			//{
			//    var ex = table.Keys.Except(walk.Keys).ToArray();
			//    if (Debugger.IsAttached)
			//        Debugger.Break();
			//    else
			//        throw new Exception();
			//}

			//var dqe = table.Where(kvp => walk[kvp.Key] != kvp.Value).ToArray();

			//if (dqe.Length > 0)
			//{
			//    if (Debugger.IsAttached)
			//        Debugger.Break();
			//    else
			//        throw new Exception();
			//}
		}
#endif
	};

#if false
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed partial class MarkingArrayTargetTfs : MarkingArrayTfs
	{
		public MarkingArrayTargetTfs(TypeMgr tm, int c_edge_hint)
			: base(tm, c_edge_hint)
		{
		}

		public Dictionary<Int64, Unification.UnifCoref> corefs = null;

		/// Number of distinct coreferences
		//public int c_corefs = 0;

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Prune a sub-graph, and then remove any unused coreferences (and unmark the edge coreference bit) which 
		/// have only a single use remaining.
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if false
		public bool _prune_below(int i_feat, int m)
		{
			Edge e;
			if (!TryGetEdge(i_feat, m, out e))
				return false;

			int c_reentrancies = 0;
			Unification.UnifCoref uc = null;
			if (e.FlagsId < 0)
			{
				/// find coreference for this edge
				corefs.TryGetValue(((long)id << 32) | (uint)e.Mark, out uc);
				while (uc.f_redirect)
					uc = uc.next;

				/// if we are pruning the opportunistic reentrancy hint, then clear it
				if (uc.single_fm.i_feat == i_feat && uc.single_fm.m == m)
					uc.single_fm = default(FeatMark);

				c_reentrancies = --uc.c_fm;
				e = uc.e_cur;
			}

			/// only remove *below* a coreferenced edge if it has no reentrancies remaining. This allows sub-graphs to 
			/// be pruned without disturbing the coreferenced portions that are still accessible from without.
			if (c_reentrancies == 0 && (e.FlagsId & agree.Edge.Flag.EtmNonBareType) != 0)
				foreach (int fix in tm.rgrgfix_by_type[(int)(e.FlagsId & Edge.Flag.MultiIdMask)])
					_prune_below(fix, e.Mark);

			/// remove the edge
			if (!RemoveEdge(i_feat, m))
				Debug.Assert(false);

			/// all done if not coreferenced, or if additional reentrancies still point to this edge.
			if (uc == null || c_reentrancies > 1)
				return true;

			/// 0 or 1 reentrancies remain. we will decrement the coreference count at the end of this function.
			if (c_reentrancies == 1)
			{
				/// if there's were 2 reentrancies and now there's just 1 remaining, we're retaining *this* edge, 
				/// so clear its coreference bit, as stored by the *other* reentrancy, and discard the equivalence
				/// class. We make these adjustments now, because that other edge might not be getting pruned,
				/// so we might never encounter it. First, find out if the opportunistic hint is any good.
				FeatMark fm = uc.single_fm;
				if (fm.m == 0)
				{
					/// hint is no good; we must find the other reentrancy by exhaustive search
					fm = this.FeatMarkEdges.First(fme =>
					{
						if (fme.e.FlagsId >= 0)
							return false;
						var ux = corefs[((long)id << 32) | (uint)fme.e.Mark];
						while (ux.f_redirect)
							ux = ux.next;
						return ux == uc;
					}).FeatMark;
				}
				Debug.Assert(ContainsFeatMark(fm.i_feat, fm.m));

				uc.c_fm = 0;
#if DEBUG
				uc.single_fm = default(FeatMark);
#endif
				/// clear coreference bit (or remove) in the other reentrancy.
				Edge.Flag nf = e.FlagsId & ~Edge.Flag.Coreference;
				if (nf == 0)
					RemoveEdge(fm.i_feat, fm.m);
				else
					SetEdge(fm.i_feat, fm.m, new Edge(nf, (nf & Edge.Flag.EtmNonBareType) == 0 ? 0 : e.Mark));
			}

			this.c_corefs--;
			return true;
		}
#endif

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public Tfs ToArrayTfs()
		{
			var matfs = new MarkingArrayTfs(this);
			matfs.Name = this.Name;
			return matfs;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public MotherDaughterTfs ToMotherDaughterTfs()
		{
			var tfs = new MotherDaughterTfs(this);
			tfs.Name = this.Name;
			return tfs;
		}
	};
#endif
}