using System;
using System.Collections;
using System.Diagnostics;
using System.Collections.Generic;
using System.Threading;

using miew.ReadOnly;
using miew.Tokenization;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// interface for objects which can be placed into the parse chart. currently, only LexicalAnalysis and
	/// (instances which inherit from ParseTfs) DerivedPassiveEdge/CompletedPassiveEdge
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public interface IParseObj : IAtomicSequence, ITokenSpan
	{
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		Entry License { get; }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		Tfs Tfs { get; }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		IList<IDerivation> Derivations { get; }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		IParseObj Next { get; }

		bool TrySetTail(IParseObj po);
		bool TrySetNext(IParseObj po_to_set, IParseObj po_expected);
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// a specific derivation, more than one of which might be packed into (some implementors of) IParseObj
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public interface IDerivation
	{
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		Tfs UnpackedTfs { get; }

		String TreeDisplay();

		IParseObj Source { get; }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		ulong DerivationHash { get; }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// for objects that participate in a global atomic sequence
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public interface IAtomicSequence
	{
		int SequenceId { get; }
		void SetSequenceId(int id);

		int State { get; }
		void SetStateRaw(int s);
		bool BeginTransact();
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// Extension methods for IAtomicSequence objects
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public static class SequenceControl
	{
		public const int stb_Pending	= 0x00;
		public const int stb_Active		= 0x01;
		public const int stb_Hold		= 0x02;
		public const int stb_Remove		= 0x04;

		public const int stb_Blocked = unchecked((int)0x80000000);
		public const int stb_IdValid = 0x20;

		public const int PENDING	= stb_Pending	| stb_Blocked;
		public const int NEW		= stb_IdValid	| stb_Blocked;
		public const int ACTIVE		= stb_Active	| stb_IdValid;
		public const int TRANSACT	= stb_Active	| stb_IdValid | stb_Blocked;
		public const int HOLD		= stb_Hold		| stb_IdValid;
		public const int REMOVE		= stb_Remove	| stb_IdValid;

		public static void SetStateNew(this IAtomicSequence ias_obj)
		{
			ias_obj.SetStateRaw(NEW);
		}

		public static void SetStateActive(this IAtomicSequence ias_obj)
		{
			Debug.Assert((ias_obj.State & (stb_Hold | stb_Remove)) == 0);	// cannot re-activate objects
			ias_obj.SetStateRaw(ACTIVE);
		}

		public static bool SeqIdBelow(this IAtomicSequence ias_obj, int upper_id)
		{
			if ((ias_obj.State & stb_IdValid) == 0)
			{
				SpinWait sw = new SpinWait();
				do
					sw.SpinOnce();
				while ((ias_obj.State & stb_IdValid) == 0);
			}
			return ias_obj.SequenceId < upper_id;
		}

		public static bool SeqIdAbove(this IAtomicSequence ias_obj, int lower_id)
		{
			if ((ias_obj.State & stb_IdValid) == 0)
			{
				SpinWait sw = new SpinWait();
				do
					sw.SpinOnce();
				while ((ias_obj.State & stb_IdValid) == 0);
			}
			return ias_obj.SequenceId > lower_id;
		}

		public static bool IsActive(this IAtomicSequence ias_obj)
		{
			int s;
			if ((s = ias_obj.State) < 0)
			{
				SpinWait sw = new SpinWait();
				do
					sw.SpinOnce();
				while ((s = ias_obj.State) < 0);
			}
			return s == ACTIVE;
		}

		public static bool IsBlocked(this IAtomicSequence ias_obj)
		{
			return ias_obj.State < 0;
		}

		public static void EndTransact(this IAtomicSequence ias_obj, int state)
		{
			Debug.Assert(ias_obj.State == TRANSACT);
			ias_obj.SetStateRaw(state);
		}

		public static bool IsHold(this IAtomicSequence ias_obj)
		{
			int s;
			if ((s = ias_obj.State) < 0)
			{
				SpinWait sw = new SpinWait();
				do
					sw.SpinOnce();
				while ((s = ias_obj.State) < 0);
			}
			return (s & stb_Hold) > 0;
		}

		public static bool IsHoldOrRemove(this IAtomicSequence ias_obj)
		{
			int s;
			if ((s = ias_obj.State) < 0)
			{
				SpinWait sw = new SpinWait();
				do
					sw.SpinOnce();
				while ((s = ias_obj.State) < 0);
			}
			return (s & (stb_Hold | stb_Remove)) > 0;
		}

		public static bool IsRemove(this IAtomicSequence ias_obj)
		{
			int s;
			if ((s = ias_obj.State) < 0)
			{
				SpinWait sw = new SpinWait();
				do
					sw.SpinOnce();
				while ((s = ias_obj.State) < 0);
			}
			return (s & stb_Remove) > 0;
		}

		public static String ToString(this IAtomicSequence ias_obj)
		{
			String s = ias_obj.SequenceId.ToString("X");
			int b = ias_obj.State;
			if ((b & stb_IdValid) > 0)
				s += " ID";
			if ((b & stb_Blocked) > 0)
				s += " BLK";
			if ((b & stb_Active) > 0)
				s += " ACT";
			if ((b & stb_Hold) > 0)
				s += " HOLD";
			if ((b & stb_Remove) > 0)
				s += " REMV";
			return s;
		}
	};
}