using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Serialization;
using System.Reflection.Emit;
using System.Reflection;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// All constraints are recorded in ConstraintPool instances. Each feature has exactly one of these, which globally
	/// records all of its constraints, regardless of the hosting type or whether the feature is introduced by--versus 
	/// inherited by--that type.
	/// 
	/// Each type is responsible for assembling its own collection of references to the ConstraintPools for all of its 
	/// appropriate features.
	/// 
	/// The design of this system is meant to capitalize on the fact that neither the membership of the appropriate feature
	/// collections, nor the global set of all ConstraintPools, change during grammar processing.
	///
	/// Assignment to *top*, that is, no constraint, is the default condition which prevails iff there is no Edge 
	/// (constraint) recorded for a particular Mark in a ConstraintPool, a condition which is (intended to be) actively
	/// maintained.
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{Feature.ToUpper(),nq} ({IntroducedBy.Name,nq})")]
	abstract public class ConstraintPool
	{
		public ConstraintPool(Tray tray, int i_feat)
		{
			this.tr = tray;
			this.tm = tray.tm;
			this.i_feat = i_feat;
		}

		readonly public int i_feat;
		readonly public TypeMgr tm;
		readonly public Tray tr;

		public String Feature
		{
			get { return tm.feat_arr[i_feat].feature; }
		}

		public Type IntroducedBy
		{
			get { return tm.feat_arr[i_feat].maximal_type; }
		}

		abstract public Edge GetEdge(Int32 m);
		abstract public bool TryGetEdge(Int32 m, out Edge e);
		abstract public void SetEdge(Int32 in_mark, Edge e);
		abstract public void RemoveEdge(Int32 in_mark);
		abstract public bool TryRemoveEdge(Int32 in_mark, out Edge e);
		abstract public bool ContainsInMark(Int32 m);
		abstract public int Count { get; }
		abstract public IEnumerable<Int32> Marks { get; }
		abstract public IEnumerable<Edge> Edges { get; }
		abstract public IEnumerable<KeyValuePair<int, Edge>> PoolEdges { get; }

		/// <summary>
		/// </summary>
		virtual public void emit_TryGetEdge(
			ILGenerator il, 
			Action<ILGenerator> ld_this, 
			Action<ILGenerator> ld_m, 
			Action<ILGenerator> ld_addr_edge)
		{
			ld_this(il);
			ld_m(il);
			ld_addr_edge(il);
			il.Emit(OpCodes.Call, this.GetType().GetMethod("TryGetEdge"));
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public IEnumerable<PoolMark> PoolMarks
		{
			get { return Marks.Select(m => new PoolMark(this, m)); }
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerDisplay("{ToString(),nq}")]
		public class _DbgConstraintPool
		{
			TypeMgr.FeatureInfo m_fi;
			Type m_host;
			public _DbgConstraintPool(TypeMgr.FeatureInfo fi, Type host)
			{
				m_fi = fi;
				m_host = host;
			}

			public override String ToString()
			{
				return String.Format("{0} ({1} '{2}')",
					m_fi.feature.ToUpper(),
					m_host == m_fi.maximal_type ? "introduced by this type" : "inherited from",
					m_fi.maximal_type.Name);
			}
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// Allows a ConstratintRef to record which Tray it will be concerned with without having to select a feature
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	sealed public class UnknownPoolClass : ConstraintPool
	{
		public UnknownPoolClass(Tray tray, int i_feat) : base(tray, i_feat) { }
		public override Edge GetEdge(int m) { throw new NotImplementedException(); }
		public override bool TryGetEdge(int m, out Edge e) { throw new NotImplementedException(); }
		public override void SetEdge(int in_mark, Edge e) { throw new NotImplementedException(); }
		public override void RemoveEdge(int in_mark) { throw new NotImplementedException(); }
		public override bool TryRemoveEdge(int in_mark, out Edge e) { throw new NotImplementedException(); }
		public override bool ContainsInMark(int m) { throw new NotImplementedException(); }
		public override int Count { get { throw new NotImplementedException(); } }
		public override IEnumerable<int> Marks { get { throw new NotImplementedException(); } }
		public override IEnumerable<Edge> Edges { get { throw new NotImplementedException(); } }
		public override IEnumerable<KeyValuePair<int, Edge>> PoolEdges { get { throw new NotImplementedException(); } }
	};
}