#if DEBUG

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Collections;
using System.Runtime.InteropServices;

using miew.Debugging;
using miew.Enumerable;
using miew.ReadOnly;

namespace agree
{
	[DebuggerDisplay("{ToString(),nq}")]
	public unsafe partial class UnificationQd
	{
		[DebuggerDisplay("{ToString(),nq}")]
		public struct TfsixMark : IEquatable<TfsixMark>
		{
			public TfsixMark(short tfsix, int mark)
			{
				this.tfsix = tfsix;
				this.mark = mark;
			}
			public TfsixMark(TfsixEdge te)
			{
				this.tfsix = te.tfsix;
				this.mark = te.e.Mark;
			}
			public int mark;
			public short tfsix;

			public bool Equals(TfsixMark other)
			{
				return this.mark == other.mark && this.tfsix == other.tfsix;
			}
			public override string ToString()
			{
				return String.Format("tfsix: {0}  mark: {1}", tfsix, mark);
			}
		};

		public static System.IO.TextWriter dtw;
		static UnificationQd _singleton;
		public Scratch* _ps0, _ps1;

		public Stack<int> stk;
		public string CurrentPath { get { return stk == null ? "" : stk.Reverse().Select(fix => tm.feat_arr[fix].feature).StringJoin("."); } }

		[DebuggerStepThrough]
		void _check_args(Edge.Flag f, Scratch* ps0, Scratch* ps1)
		{
			Debug.Assert(ps0->tfsix > 0 && ps1->tfsix > 0);
			Debug.Assert(ps0->m_src != 0 && ps1->m_src != 0);
			Debug.Assert(ps0->m_src != ps1->m_src || ps0->tfsix != ps1->tfsix);
			Debug.Assert(*rgpfix[(int)(f & Edge.Flag.MultiIdMask)] > 0);
			//if (Debugger.IsAttached)
			//{
			//    _ps0 = bases[tfsix0] + (m0 < 0 ? m0 : m0 == TopmostMark ? 0 : GetMix(tfsix0, m0));
			//    _ps1 = bases[tfsix1] + (m1 < 0 ? m1 : m1 == TopmostMark ? 0 : GetMix(tfsix1, m1));
			//    Debug.Assert(_ps0 != _ps1);
			//}
			if (stk != null)
				stk.Push(0);
		}
		[DebuggerHidden]
		void _change_stack_top(ushort i_feat)
		{
			if (stk != null)
			{
				stk.Pop(); stk.Push(i_feat);
			}
		}
		[DebuggerHidden]
		void _debug_nps(Scratch* ps0, Scratch* ps1, Scratch* nps0, Scratch* nps1)
		{
			if (dtw != null)
			{
				sbyte suppress = 0;
				if (ps0->c_visits != 0)
					suppress++;
				if (ps1->c_visits != 0)
					suppress++;

				if (nps0 == null && nps1 == null)
				{ }
				else if (nps0 == null)
					dtw.WriteLine("D {0} {1} {2,-75} {3,-3} {4,2} {5,2} -> {6,2} {7,2} {8,-38} {9}", " ", nps1->tfsix, CurrentPath.ToUpper(), suppress == 0 ? "" : "s" + suppress.ToString(), ps0->c_visits, ps1->c_visits, " ", nps1->c_visits, "null", ShortInfo2a(nps1));
				else if (nps1 == null)
					dtw.WriteLine("D {0} {1} {2,-75} {3,-3} {4,2} {5,2} -> {6,2} {7,2} {8,-38} {9}", nps0->tfsix, " ", CurrentPath.ToUpper(), suppress == 0 ? "" : "s" + suppress.ToString(), ps0->c_visits, ps1->c_visits, nps0->c_visits, " ", ShortInfo2a(nps0), "null");
				else
					dtw.WriteLine("D {0} {1} {2,-75} {3,-3} {4,2} {5,2} -> {6,2} {7,2} {8,-38} {9}", nps0->tfsix, nps1->tfsix, CurrentPath.ToUpper(), suppress == 0 ? "" : "s" + suppress.ToString(), ps0->c_visits, ps1->c_visits, nps0->c_visits, nps1->c_visits, ShortInfo2a(nps0), ShortInfo2a(nps1));
			}

		}
		[DebuggerHidden]
		void _debug_nps_single(Scratch* ps, Scratch* nps)
		{
			if (dtw != null)
			{
				dtw.WriteLine("U  {0}  {1,-75} {2,-3}    {3,2} -> {4,2}    {5}",
					nps->tfsix,
					CurrentPath.ToUpper(),
					ps->c_visits != 0 ? "s1" : "",
					ps->c_visits,
					nps->c_visits,
					ShortInfo2a(nps));

			}
		}

		void _check_counts(arr_tfs_entry[] entries, Scratch* ps_m)
		{
			//var chkarr = entries
			//    .GroupBy(ate => ate.e_Mark)
			//    .Select(g => new { e = g.First().e, mark = g.Key, count = g.Count(), fmx = g.Select(xg => xg.fm).ToArray() })
			//    .Where(g => g.count > 1)
			//    .OrderBy(g => g.mark)
			//    .ToArray();

			chkarr = entries
				.Take(i_ate)
				.GroupBy(ate => ate.e_Mark)
				.ToDictionary(g => g.Key, g => g.Count());

			chktfs = entries
				.Take(i_ate)
				.GroupBy(ate => ate.mark)
				.ToDictionary(g => g.Key, g => g.ToDictionary(xx => xx.i_feat, xx => xx.e));

			_check_counts(TopmostMark, ps_m);

			//Debug.Print("ent: {0,3}  c_edges: {1,3} c_tot: {2,3} c_totns: {3,3} ps->d->c_visits {4} ps_x->forward {5}",
			//    entries.Length, c_edges, c_tot, c_totns, ps_d->c_visits, ps_x->forward);

			if (c_edges != entries.Length)
			{
				Console.Out.WriteColorSync("$red [{0}  {1} {2}] ", c_edges, entries.Length, ctrl.ts_input.Source.Text);
			}
			else
			{
				//Console.Write(".");
			}
		}


		Scratch* true_bases(int tfsix)
		{
			if (tfsix > c_participants + 1)
				throw new Exception();
			if (tfsix == c_participants + 1)
				return next_base;
			return bases[tfsix] + participants[tfsix].next_coref_mark + 1;
		}

		Dictionary<int, int> chkarr;
		Dictionary<int, Dictionary<int, Edge>> chktfs;

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void _check_counts(int m_reference, Scratch* ps)
		{
			Dictionary<int, Edge> d;
			chktfs.TryGetValue(m_reference, out d);
			ArrayTfs tfs = participants[ps->tfsix];
			ushort* pfix = rgpfix[(int)(ps->f & Edge.Flag.MultiIdMask)], pfix_end = *pfix++ + pfix;
			do
			{
				if (stk != null) stk.Push(*pfix);
				{
					Scratch* nps = _fallback_fetch(tfs, ps, *pfix);
					if (nps == null || nps->tfsix == 0)
						goto _continue;

					Edge ne1;
					if (!d.TryGetValue(*pfix, out ne1))
					{
						Debug.Assert(nps->m_copy == 0, "Count failed", "#2, Input: {0}", ctrl.ts_input.Source.Text);
						Debug.Assert(nps->f == 0, "Count failed", "#3, Input: {0}", ctrl.ts_input.Source.Text);
						//Debug.Assert(nps->c_visits <= 1);
					}
					else
					{
						Debug.Assert(ne1.Mark == nps->m_copy, "Count failed", "#4, Input: {0}", ctrl.ts_input.Source.Text);
						Debug.Assert(ne1.FlagsId == nps->f, "Count failed", "#5, Input: {0}", ctrl.ts_input.Source.Text);
						int expected = nps->c_visits > 1 ? chkarr[ne1.Mark] : 1;
						Debug.Assert(expected == nps->c_visits, "Count failed", "#6, Input: {0}", ctrl.ts_input.Source.Text);
						if ((nps->f & Edge.Flag.EtmNonBareType) != 0)
							_check_counts(ne1.Mark, nps);
					}
				}
			_continue: ;
				if (stk != null) stk.Pop();
			}
			while (++pfix < pfix_end);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		Scratch* _get_address(byte tfsix, int mix, int nm)
		{
			Debug.Assert(tfsix != 0);
			return bases[tfsix] + (nm < 0 ? nm : mix);
		}

		public Edge GetArgArrayTfsEdge(Scratch* ps)
		{
			Debug.Assert(c_participants > 0 && ps >= true_bases(1));
			int tfsix = GetTfsix(ps);
			Tfs tfs = participants[tfsix];
			int mix = (int)(ps - bases[tfsix]);
			if (mix > 0)
			{
				if (tfs is ArrayTfs)
					return ((ArrayTfs)tfs).entries[mix - 1].e;
				else
					return default(Edge);
			}
			else if (mix == 0)
			{
				Debug.Assert(tfs.Edge.Mark == TopmostMark);
				return tfs.Edge;
			}
			return tfs.GetCorefEdge(mix);
		}

		FeatMark GetFm(Scratch* ps)
		{
			int tfsix = GetTfsix(ps);
			return GetFmFromMix(tfsix, (int)(ps - bases[tfsix]));
		}
		FeatMark GetFmFromMix(int tfsix, int mix)
		{
			if (mix <= 0)
				return default(FeatMark);
			return participants[tfsix].entries[mix - 1].fm;
		}

		//int GetMix(int tfsix, int m)
		//{
		//    Debug.Assert(m != 0);
		//    if (m <= 0)
		//        return m;
		//    ArrayTfs tfs = participants[tfsix];
		//    FeatMark fm = tfs.GetHostFeatMark(m);
		//    Debug.Assert(fm.m != 0);
		//    m = fm.m;
		//    return tfs.GetEdgeIndex(fm.i_feat, ref m);
		//}

		int GetTfsix(Scratch* ps)
		{
			//Debug.Assert(c_participants > 0 && ps >= true_bases[1]);
			if (IsInitialized(ps))
				return ps->tfsix;
			int tfsix = 1;
			while (ps >= true_bases(tfsix + 1))
				tfsix++;
			return tfsix;
		}

		public static bool IsInitialized(Scratch* ps) { return ps->tfsix != 0; }
		public static bool IsMaster(Scratch* ps) { return IsInitialized(ps) && ps->forward >= 0; }
		public bool AreJoined(Scratch* ps0, Scratch* ps1)
		{
			_dereference(ref ps0);
			_dereference(ref ps1);
			return ps0 == ps1;
		}

		public IList<ScratchDebug> _forwarders(Scratch* ps_seek)
		{
			if (!IsInitialized(ps_seek))
				return new ScratchDebug[0];
			_dereference(ref ps_seek);

			List<ScratchDebug> lsd = new List<ScratchDebug>();
			Scratch* ps = ps_base + 1;
			Scratch* ps_end = ps_base + c_slot_alloc;
			while (ps < ps_end)
			{
				if (ps->forward < 0)
				{
					Scratch* _tmp = ps;
					do
						_tmp = ps_base - _tmp->forward;
					while (_tmp->forward < 0);
					if (_tmp == ps_seek)
						lsd.Add(new ScratchDebug(this, ps));
				}
				ps++;
			}
			return lsd;
		}

		public override string ToString()
		{
			String s = String.Format("c_tfs: {0} c_edges {1}  ", c_participants, 0);//c_edges);
			s += "\"" + CurrentPath + "\"";
			if (_ps0 != null && _ps1 != null)
			{
				s += String.Format("  {0} ⊓ {1}",
					"{" + ShortInfo2(_ps0) + "}",
					"{" + ShortInfo2(_ps1) + "}");
			}
			return s;
		}

		[DebuggerDisplay("{ToString(),nq}")]
		public struct ParticipantInfo
		{
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			UnificationQd u;
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			byte tfsix;

			public ParticipantInfo(UnificationQd u, byte tfsix)
			{
				this.u = u;
				this.tfsix = tfsix;
			}

			//[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
			public ArrayTfs ArrayTfs { get { return u.participants[tfsix] as ArrayTfs; } }

			public ScratchDebug[] _scr_debug
			{
				get
				{
					int c = _slot_count(u.participants[tfsix], null);
					ScratchDebug[] rgsd = new ScratchDebug[c];
					for (int i = 0; i < c; i++)
						rgsd[i] = new ScratchDebug(u, tfsix, i);
					return rgsd;
				}
			}

			public ScratchDebug[] _scr_debug_in_use
			{
				get
				{
					List<ScratchDebug> lsd = new List<ScratchDebug>();
					int c = _slot_count(u.participants[tfsix], null);
					Scratch* ps = u.true_bases(tfsix);
					for (int i = 0; i < c; i++, ps++)
						if (IsInitialized(ps))
							lsd.Add(new ScratchDebug(u, tfsix, i));
					return lsd.ToArray();
				}
			}

			public ScratchDebug[] _scr_forwarded
			{
				get
				{
					List<ScratchDebug> lsd = new List<ScratchDebug>();
					int c = _slot_count(u.participants[tfsix], null);
					Scratch* ps = u.true_bases(tfsix);
					for (int i = 0; i < c; i++, ps++)
						if (ps->forward < 0)
							lsd.Add(new ScratchDebug(u, tfsix, i));
					return lsd.ToArray();
				}
			}

			public override string ToString()
			{
				Tfs tfs = u.participants[tfsix];
				int offs, c = _slot_count(tfs, &offs);
				String rs = String.Format("[{0}...{1}...{2}]",
					(int)(u.true_bases(tfsix) - u.ps_base),
					(int)(u.bases[tfsix] - u.ps_base),
					(int)((u.true_bases(tfsix + 1) - 1) - u.ps_base));
				String s = String.Format("{0,17} tfsix: {1}  [{2} {3}/{4}] ({5}) {6}",
					rs,
					tfsix,
					-offs,
					tfs.next_mark - 1,
					tfs.ScratchAlloc,
					c,
					tfs);
				return s;
			}
		}

		[DebuggerDisplay("{ToString(),nq}")]
		public partial struct Scratch
		{
			[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
			public ScratchDebug _src_debug
			{
				get { return new ScratchDebug(_singleton, _this); }
			}

			public override string ToString()
			{
				return new ScratchDebug(_singleton, _this).ToString();
			}
		};

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// tfsix : index of the TFS in participants[...]
		/// rix : raw, mixed index within a single TFS
		/// mix : mixed index adjusted to be referenced off bases[tfsix]
		///			'mark' if less than zero. it is always valid to reference bases[tfsix]+mix with it
		/// gix : global index of any Scratch field, based only on ps_base
		///			global slot 0 is reserved for indicating 'no forwarding'
		/// ps : pointer to scratch structures
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerDisplay("{ToString(),nq}")]
		public struct ScratchDebug
		{
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			UnificationQd u;
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			int tfsix;
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			int rix;

			[DebuggerHidden]
			public ScratchDebug(UnificationQd u, int tfsix, int rix)
			{
				this.u = u;
				this.tfsix = tfsix;
				this.rix = rix;
			}
			[DebuggerHidden]
			public ScratchDebug(UnificationQd u, int tfsix, Scratch* ps)
				: this(u, ps == null ? 0 : tfsix, ps == null ? -1 : (int)(ps - u.true_bases(tfsix)))
			{
			}
			[DebuggerHidden]
			public ScratchDebug(UnificationQd u, Scratch* ps)
				: this(u, u.GetTfsix(ps), ps)
			{
			}
			//[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
			public TfsEdge _dbg_node
			{
				get
				{
					if (tfsix == 0 || rix == -1)
						return default(TfsEdge);
					Scratch* ps = u.true_bases(tfsix) + rix;
					if (!IsInitialized(ps))
						return default(TfsEdge);
					return u.participants[tfsix].GetSectionTfsEdge(u.GetArgArrayTfsEdge(ps));
				}
			}

			public ScratchDebug[] _references
			{
				get
				{
					if (tfsix == 0 || rix == -1)
						return null;
					var rg = u._forwarders(u.true_bases(tfsix) + rix);
					return rg as ScratchDebug[] ?? rg.ToArray();
				}
			}

			public ScratchDebug _fwd_target
			{
				get
				{
					if (tfsix == 0 || rix == -1)
						return this;
					Scratch* ps_fwd = u.true_bases(tfsix) + rix;
					if (!IsInitialized(ps_fwd))
						return this;
					u._dereference(ref ps_fwd);
					return new ScratchDebug(u, ps_fwd);
				}
			}

			public override string ToString()
			{
				Scratch* ps_this = u.true_bases(tfsix) + rix;
				//if (!IsInitialized(ps_this))
				//    return "(?)";

				String s = String.Format("{0} ", u.ShortInfo1(ps_this));

				int om = 0;
				String s1;
				int mix = (int)(ps_this - u.bases[tfsix]);
				if (mix < 0)
					s1 = String.Format("m {0,3}", mix);
				else if (mix == 0)
					s1 = "TOPMST";
				else
				{
					ArrayTfs tfs = u.participants[tfsix];
					s1 = "[" + (mix - 1) + "] ";
					arr_tfs_entry ate = tfs.entries[mix - 1];
					if (ate.mark != 0)
					{
						s1 += ate.mark + "/" + u.tm.feat_arr[ate.i_feat].feature.ToUpper();
						om = ate.e_Mark;
					}
				}
				s += String.Format("{0,-20} ", s1);

				if (ps_this->tfsix == 0)
				{
					if (om < 0)
						s += " " + om;
				}
				else
				{
					if (ps_this->forward >= 0)
					{
						s += String.Format("{0,26} ", Edge._FlagsReport(ps_this->f, false));

						//if (ps_this->forward != FirstVisit && ps_this->forward != Terminator)
						//    s += "Error c_ref != 0, forwarded. ";
						s += String.Format("visit: {0} ", ps_this->c_visits);
						//if ((ps_this->c_ref > 1) != (ps_this->c_visits > 1))
						//    s += "≠ ";

						if (ps_this->forward != 0)
							s += String.Format("(fwd: {0}) ", ps_this->forward);
					}
					else
					{
						if (ps_this->m_src != 0)
							s += String.Format("m{0} ", ps_this->m_src).PadLeft(27);
						else
							s += "☐ ".PadLeft(27);

						//if (ps_this->c_ref != 0)
						//    s += String.Format("ERROR c_ref=={0}. ", ps_this->c_ref);
						//if (ps_this->c_ref == 0)
						{
							Scratch* ps_fwd = ps_this;
							u._dereference(ref ps_fwd);
							s += "fwd: {" + u.ShortInfo2(ps_fwd) + "} ";
						}
					}
				}
				return s;
			}
		};

		public String ShortInfo0(Scratch* ps)
		{
			int tfsix = GetTfsix(ps);
			return String.Format("{0}@{1}", tfsix, GetFm(ps));
		}
		public String ShortInfo0a(Scratch* ps)
		{
			int tfsix = GetTfsix(ps);
			int mix = (int)(ps - bases[tfsix]);
			String s = String.Format("{0}@{1}", tfsix, (int)(ps - true_bases(tfsix)));
			if (mix < 0)
				s += String.Format("({0})", mix.ToString());
			else if (mix > 0)
				s += String.Format("({0})", GetFmFromMix(tfsix, mix).ToString());
			return s;
		}
		public String ShortInfo1(Scratch* ps)
		{
			int tfsix = GetTfsix(ps);
			return String.Format("{0,3}:{1}@{2,-3}", (int)(ps - ps_base), tfsix, (int)(ps - true_bases(tfsix)));
		}
		public String ShortInfo2(Scratch* ps)
		{
			String s = ShortInfo0a(ps);
			if (IsInitialized(ps))
			{
				if (IsMaster(ps))
				{
					s += " " + Edge._FlagsReport(ps->f, false);
					s += ", visit: " + ps->c_visits;
				}
				else
					s += "m" + ps->m_src;
			}
			return s;
		}
		public String ShortInfo2a(Scratch* ps)
		{
			String s = ShortInfo0a(ps);
			if (IsInitialized(ps))
			{
				if (IsMaster(ps))
				{
					s += " " + Edge._FlagsReport(ps->f, false);
					if (ps->m_copy != 0)
						s += " -> " + ps->m_copy.ToString();
				}
				else if (ps->m_src != 0)
					s += " m" + ps->m_src;
			}
			return s;
		}

		[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		public ParticipantInfo[] _dbg_participants
		{
			get
			{
				return Enumerable.Range(1, c_participants).Select(i => new ParticipantInfo(this, (byte)i)).ToArray();
			}
		}
	};
}
#endif