using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Runtime.Serialization;

using glue.Extensions.String;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// This is an immutable value type which indicates a node position within the shared TFS pools. Such edges are
	/// themselves typically stored in the pools, but this is not a requirement. As the edge is not aware of its owning
	/// pool, there is no ability to change the Type or Mark on this edge itself in a way that is persistent in the
	/// pools. For this, the ConstraintRef object is used.
	/// </summary>
	/// <remarks>
	/// This file generally contains functionality related to a single Edge in isolation. Functions which treat edges as
	/// participants in feature structure instances, though currently members of this same class, are generally located
	/// in pool-edge-tfs.cs
	/// </remarks>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public partial struct Edge : IEquatable<Edge>
	{
		public const Int32 MarkBare = 0;

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Edge consists of only two fields:
		/// 1. FlagsId (32-bits)
		/// 2. Mark (32-bits)
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerDisplay("{Edge._FlagsReport(this.FlagsId, true),nq}")]
		readonly public Flag FlagsId;

		[DebuggerDisplay("{Edge.FormatMark(this),nq}")]
		readonly public Int32 Mark;

		public static Edge PrunedEdge = new Edge(Edge.Flag.PrunedDuringParsing, MarkBare);

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Regarding the FlagsId field:
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[Flags]
		[DebuggerDisplay("{Edge._FlagsReport(this, true),nq}")]
		public enum Flag : uint
		{
			Coreference			= 0x80000000,
			PrunedDuringParsing = 0x40000000,

			/// mutually exclusive edge type modes:
			EtmMask				= 0x30000000,
			EtmConfigMapped		= 0x30000000,
			EtmString			= 0x20000000,
			EtmNonBareType		= 0x10000000,

			LowestFlagValue		= EtmNonBareType,

			//Readonly			= 0x08000000,
			//FlagMask			= 0xFF000000,

			CachedBottom = 0xFFFFFFFF
		};

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public bool IsCoreferenced { get { return (FlagsId & Flag.Coreference) != 0; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public bool IsConstrained { get { return Mark != MarkBare; } }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// C O N S T R U C T O R
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public Edge(Flag force_flagsid, int force_mark)
		{
			this.FlagsId = force_flagsid;
			this.Mark = force_mark;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Edges can be compared for equality, hashed, and (explicitly) cast to and from UInt64
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public bool Equals(Edge other)
		{
			return Mark == other.Mark && FlagsId == other.FlagsId;
		}
		public static bool operator ==(Edge x, Edge y) { throw new NotImplementedException(); }
		public static bool operator !=(Edge x, Edge y) { throw new NotImplementedException(); }
		public override bool Equals(object obj)
		{
			throw new NotImplementedException("--> use strongly typed IEquatable<T> instead");
		}

		public override int GetHashCode()
		{
			return Mark + (int)FlagsId;
		}

		public static explicit operator UInt64(Edge e)
		{
			return ((UInt64)e.FlagsId << 32) | (uint)e.Mark;
		}

		public static explicit operator Edge(UInt64 ui64)
		{
			return new Edge((Flag)(UInt32)(ui64 >> 32), (Int32)ui64);
		}
	};
}