#define TARGET_ARGS_DELETE

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

using miew.Debugging;
using miew.String;
using miew.Enumerable;

#if ILFUNC
using IlFunc;
#endif

#pragma warning disable 0169

#if NODE_INDEX
namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	[DebuggerDisplay("{ToString(),nq}")]
	public unsafe struct NodeIndexEntry : IComparable<NodeIndexEntry>
	{
		static public NodeIndexEntry NoFeature;

		static NodeIndexEntry()
		{
			NoFeature = new NodeIndexEntry { f = 0, i_feat = ushort.MaxValue, out_mark = 0 };
		}

		public ushort i_feat;
		public short out_mark;
		public Edge.Flag f;
#if DEBUG
		public override string ToString()
		{
			if (i_feat == ushort.MaxValue)
				return " ============ ";
			FeatureInfo fi = Grammar._singleton.tm.feat_arr[i_feat];
			return String.Format("{0} {1}  out_mark: {2}  f: {3}", fi.i_feat, fi.feature.ToUpper(), out_mark, Edge._FlagsReport(f, true));
		}
#endif

		public int CompareTo(NodeIndexEntry other) { return this.i_feat - other.i_feat; }

		public static void _quick_sort(NodeIndexEntry* p_base, int left, int right)
		{
			do
			{
				int a = left;
				int b = right;
				int sp = a + ((b - a) >> 1);

				if (a != sp)
					_swap_if_gt(p_base, a, sp);
				if (a != b)
					_swap_if_gt(p_base, a, b);
				if (sp != b)
					_swap_if_gt(p_base, sp, b);
				int y = p_base[sp].i_feat;
				do
				{
					while (p_base[a].i_feat < y)
						a++;
					while (y < p_base[b].i_feat)
						b--;
					if (a > b)
						break;
					if (a < b)
					{
						NodeIndexEntry _t = p_base[a];
						p_base[a] = p_base[b];
						p_base[b] = _t;
					}
					a++;
					b--;
				}
				while (a <= b);

				if ((b - left) <= (right - a))
				{
					if (left < b)
						_quick_sort(p_base, left, b);
					left = a;
				}
				else
				{
					if (a < right)
						_quick_sort(p_base, a, right);
					right = b;
				}
			}
			while (left < right);
		}

		static void _swap_if_gt(NodeIndexEntry* p_base, int i0, int i1)
		{
			NodeIndexEntry* pnie0, pnie1;
			NodeIndexEntry nie0, nie1;
			if ((nie0 = *(pnie0 = p_base + i0)).i_feat > (nie1 = *(pnie1 = p_base + i1)).i_feat)
			{
				*pnie0 = nie1;
				*pnie1 = nie0;
			}
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public unsafe struct NodeIndex
	{
		public static int NodeSize(int c_entries, int c_mark_range, int c_in_mark_usage)
		{
			int c = sizeof(NodeIndex);
			c += sizeof(NodeIndexEntry*) * (c_mark_range + 1);
			c += sizeof(NodeIndexEntry) * (c_entries + c_in_mark_usage);
			return c;
		}
		public int add_to_mark;
		public int c_limit;
		public int c_entries;
		public int c_in_mark_usage;
		public int c_mark_range;
		public int _spare;
		///
		/// ... NodeIndexEntry*[0 ... c_mark_range]
		/// 
		/// ... NodeIndexEntry[0 ... ]
		/// 

#if DEBUG
		public _debug_node_index_entry[] _entries
		{
			get
			{
				int c_limit = c_entries + c_in_mark_usage;
				_debug_node_index_entry[] rg = new _debug_node_index_entry[c_limit];
				fixed (NodeIndex* _this = &this)
				{
					NodeIndexEntry** ppnie = (NodeIndexEntry**)(_this + 1);
					NodeIndexEntry* pnie_cur = (NodeIndexEntry*)&ppnie[c_mark_range];
					for (int i = 0; i < c_limit; i++)
					{
						ushort ix;
						for (ix = 0; ix < c_mark_range; ix++)
							if (ppnie[ix] == pnie_cur)
								goto found;
						ix = ushort.MaxValue;
					found:
						rg[i] = new _debug_node_index_entry(pnie_cur, ix, ix - add_to_mark);
						pnie_cur++;
					}
				}
				return rg;
			}
		}

		[DebuggerDisplay("{ToString(),nq}")]
		public unsafe struct _debug_node_index_entry
		{
			NodeIndexEntry* pnie;
			int m;
			ushort ix;
			public _debug_node_index_entry(NodeIndexEntry* pnie, ushort ix, int m)
			{
				this.pnie = pnie;
				this.m = m;
				this.ix = ix;
			}

			public override string ToString()
			{
				if (pnie->i_feat == ushort.MaxValue)
					return " ============ ";
				String z = "";
				if (ix != ushort.MaxValue)
					z = String.Format("{0}/{1}", ix, m);

				FeatureInfo fi = Grammar._singleton.tm.feat_arr[pnie->i_feat];
				return String.Format("{0,6} {1} {2}  out_mark: {3}  f: {4}", z,
					fi.i_feat, fi.feature.ToUpper(), pnie->out_mark, Edge._FlagsReport(pnie->f, true));
			}
		};
#endif
	};

	public unsafe partial class ArrayTfs : Tfs
	{
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		void BuildNodeIndex(Allocator ctrl, int c_actual, int next_mark, int next_coref_mark, int min_coref_in_mark, bool f_sort)
		{
			min_coref_in_mark = -min_coref_in_mark;
			int fpt = tm.fcm.MaxFeaturesPerType;
			int c_mark_range = min_coref_in_mark + next_mark;
			int c_in_mark_usage = 0;

			int* nie_feat_counts = stackalloc int[c_mark_range];
			NodeIndexEntry* nie_base = stackalloc NodeIndexEntry[fpt * c_mark_range];

			fixed (arr_tfs_entry* _base = entries)
			{
				arr_tfs_entry* end = _base + c_actual;
				for (arr_tfs_entry* dst = _base; dst < end; dst++)
				{
					int ient = dst->mark + min_coref_in_mark;
					int c_cur;
					if ((c_cur = nie_feat_counts[ient]++) == 0)
						c_in_mark_usage++;

					NodeIndexEntry* _pnie = nie_base + (ient * fpt) + c_cur;
					//_pnie->arr_ix = (short)i;
					_pnie->i_feat = (ushort)dst->i_feat;
					_pnie->out_mark = (short)dst->e_Mark;
					_pnie->f = dst->e_FlagsId;
				}
			}

			int c_ni_alloc = NodeIndex.NodeSize(c_actual, c_mark_range, c_in_mark_usage);
			p_node_index = (NodeIndex*)ctrl.Alloc(c_ni_alloc);

			NodeIndex* pni = p_node_index;
			pni->add_to_mark = min_coref_in_mark;
			pni->c_limit = c_mark_range;
			pni->c_entries = c_actual;
			pni->c_in_mark_usage = c_in_mark_usage;
			pni->c_mark_range = c_mark_range;
			NodeIndexEntry** ppnie = (NodeIndexEntry**)(pni + 1);
			NodeIndexEntry* pnie_dst = (NodeIndexEntry*)&ppnie[c_mark_range];

			*(long*)pnie_dst = -1;
			pnie_dst++;

			for (int i = 0; i < c_mark_range; i++)
			{
				int c = nie_feat_counts[i];
				if (c == 0)
					*ppnie = pnie_dst - 1;
				else
				{
					*ppnie = pnie_dst;
					NodeIndexEntry* pnie_src = nie_base + (i * fpt);

					if (f_sort)
					{
						if (c == 2)
						{
							NodeIndexEntry nie0, nie1;
							if ((nie0 = pnie_src[0]).i_feat > (nie1 = pnie_src[1]).i_feat)
							{
								*pnie_dst++ = nie1;
								*pnie_dst++ = nie0;
							}
							else
							{
								*pnie_dst++ = nie0;
								*pnie_dst++ = nie1;
							}
						}
						else if (c == 1)
						{
							*pnie_dst++ = *pnie_src;
						}
						else if (c > 1)
						{
							NodeIndexEntry._quick_sort(pnie_src, 0, c - 1);

							for (int j = 0; j < c; j++)
								*pnie_dst++ = *pnie_src++;
						}
					}
					else
					{
						for (int j = 0; j < c; j++)
							*pnie_dst++ = *pnie_src++;
					}
					*(long*)pnie_dst = -1;
					pnie_dst++;
				}
				ppnie++;
			}
		}
	};
}
#endif