using System;
using System.Diagnostics;
using System.Threading;

using miew.Concurrency;
using miew.Debugging;

#pragma warning disable 0649

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial class Strings
	{
		FalseSharing.Padding60 fsp1;
		int next_string_id = 1;
		FalseSharing.Padding60 fsp2;

		readonly ConcurrentSymmetricDictionary<String, Int32> dict = new ConcurrentSymmetricDictionary<String, Int32>();
		readonly TypeMgr tm;
		public int string_id;
		public bool f_petrified = false;

		public Strings(TypeMgr tm)
		{
			this.tm = tm;
			Nop.X(fsp1, fsp2);
		}

		public void Petrify()
		{
			f_petrified = true;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Guaranteed to preserve the coreference flag and return an _existing_ string mark, if successful
		/// Implicitly unifies down to *string* (at least)
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public bool Unify(int m_recycle, Edge e1, Edge e2, MarkingTfs tfs_dst, out Edge result)
		{
			//Debug.Assert((e1.FlagsId & Edge.Flag.EtmMask) == Edge.Flag.EtmString || (e2.FlagsId & Edge.Flag.EtmMask) == Edge.Flag.EtmString);

			int id1 = (int)(e1.FlagsId & Edge.Flag.MultiIdMask);
			int id2 = (int)(e2.FlagsId & Edge.Flag.MultiIdMask);
			Edge.Flag f_coref = ((e1.FlagsId | e2.FlagsId) & Edge.Flag.Coreference);

			/// check for e1 not being a string type
			if ((e1.FlagsId & Edge.Flag.EtmMask) != Edge.Flag.EtmString)
			{
				if (id1 != 0 && !tm.CanUnify(string_id, id1))
					goto failed;
				id1 = 0;
			}

			/// check for e2 not being a string type
			if ((e2.FlagsId & Edge.Flag.EtmMask) != Edge.Flag.EtmString)
			{
				if (id2 != 0 && !tm.CanUnify(string_id, id2))
					goto failed;
				id2 = 0;
			}

			/// both are some kind of string
			if (id2 == 0 || id1 == id2)
			{
				result = tfs_dst.CreateRecycledEdge(f_coref | Edge.Flag.EtmString | (Edge.Flag)id1, m_recycle);
				return true;
			}
			if (id1 == 0)
			{
				result = tfs_dst.CreateRecycledEdge(f_coref | Edge.Flag.EtmString | (Edge.Flag)id2, m_recycle);
				return true;
			}
		failed:
			result = default(Edge);
			return false;
		}

		public String Get(Int32 sid)
		{
#if DEBUG
			String err = CheckId(sid);
			if (err != null)
				throw new TypeMgr.InternalConsistencyException(err);
#endif
			return dict[sid];
		}

		public String CheckId(Int32 sid)
		{
			if (sid > (int)Edge.Flag.MultiIdMask)
				return String.Format(
					"String id {0} (0x{1}) is outside the mask range (0x{2}) that was computed when the type hierarchy was frozen.",
					sid, sid.ToString("X" + (tm.c_t_bits / 4).ToString()), Edge.Flag.MultiIdMask);
			if (!dict.ContainsKey(sid))
				return String.Format("String id {0} (0x{1}) was not found",sid, sid.ToString("X" + (tm.c_t_bits / 4).ToString()));
			return null;
		}

		public int GetOrAdd(String s)
		{
			int sid;
			if (dict.TryGetKey(s, out sid))
				return sid;
			sid = Interlocked.Increment(ref next_string_id);
			dict.Add(s, sid);
			return sid;
		}

		public int Count { get { return dict.Count; } }
	};
}