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

using miew.Debugging;
using miew.Enumerable;
using miew.ReadOnly;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// In accordance with DELPH-IN tradition, we distinguish "types" from "entries." (Copestake 2002 p.106)
	/// Entries include everything except Types: lexical entries, grammar rules, start symbols, and node labels.
	/// In common, both share this abstract base class, Instance, which captures the idea of something that can
	/// have constraints. Types participate in a DAG hierarchy whereas Entries instead refer to a single Type. Thus,
	/// while Types and Entries have an is-a (inheritance) relationship with Instance, every Entry additionally has
	/// a has-a (contains) relationship with exactly one Type.
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract partial class Instance : ISysObj
	{
		public Instance(TypeMgr tm, String name, List<BaseFeatConstraint> bfc)
		{
			this.tm = tm;
			this.Name = name;
			SetConstraints(bfc);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// 
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[Flags]
		public enum Flags
		{
			/// <summary>set for a type with no child types</summary>
			Leaf = 0x00000001,

			/// <summary>set if this type is a system-generated greater lowest bound (glb) type</summary>
			GlbType = 0x00000002,

			/// <summary>set if this type has any appropriate (inherited or local) features</summary>
			HasAnyFeatures = 0x00000004,

			/// <summary>set if there are no appropriate features in this type's subgraph (inclusive)</summary>
			Atomic = 0x00000010,

			/// <summary>useful for skipping well-formedness unification when  a ⊓ b ⊏ glb</summary>
			HasConstraints = 0x00000020,

			/// <summary>set when the type's inherited features have been computed</summary>
			LoadedNonLocalFeatures = 0x00010000,
		};

		public Flags m_flags;

		/// <summary>
		/// if a type has no appropriate features, it can be structure-shared
		/// </summary>
		public bool HasAnyFeatures { get { return (m_flags & Flags.HasAnyFeatures) > 0; } }

		public bool IsBare { get { return (m_flags & Flags.HasAnyFeatures) == 0; } }

		public readonly TypeMgr tm;

		public List<BaseFeatConstraint> m_bfc;

		public readonly String Name;

		public abstract Type InstanceType { get; }

		[DebuggerDisplay("{m_flags.HasFlag(Flags.LoadedDefinition)?_definition.ToString():\"(definition not loaded yet)\",nq}")]
		protected Tfs _definition;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		protected Tfs _expanded;

		public Tfs Expanded
		{
			get
			{
				bool f_dont_care;
				return _expanded ?? EnsureExpanded(out f_dont_care);
			}
		}

		public abstract Tfs EnsureExpanded(out bool f_did);

		public bool IsExpanded { get { return _expanded != null; } }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public void SetConstraints(List<BaseFeatConstraint> bfc)
		{
			if (bfc != null)
			{
				if (m_bfc == null)
					m_bfc = bfc;
				else
					m_bfc.AddRange(bfc);
				m_flags |= Type.Flags.HasConstraints;
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]	// side effects
		public Tfs Definition
		{
			get
			{
				Tfs _tmp = _definition;
				if (_tmp != null)
					return _tmp;

				if ((m_flags & Flags.HasAnyFeatures) == 0 || m_bfc == null)
					_tmp = new BareTfs(InstanceType);
				else
				{
					BootstrapTfs dtfs = BootstrapDefinition();
					int ec = dtfs.EdgeCount;
					if (ec == 0)
						_tmp = new BareTfs(InstanceType);
					else if (ec < 12)
						_tmp = new TinyTfs(dtfs);
					else
						_tmp = new ArrayTfs(dtfs);
				}
#if CONCURRENT_DEF_LOAD
				if (Ext.AtomicStoreIfNull(ref _definition, ref _tmp))
					m_bfc = null;
#else
				_definition = _tmp;
				m_bfc = null;
#endif
				return _tmp;
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public BootstrapTfs BootstrapDefinition()
		{
			Debug.Assert(_definition == null);
			Type t = InstanceType;
			BootstrapTfs dtfs = new BootstrapTfs(tm, t, false);
			ConstraintRef cref = new ConstraintRef(dtfs);
			using (TdlNavigator bn = new TdlNavigator(dtfs, t))
			{
				foreach (BaseFeatConstraint bfc in m_bfc)
					bn.AcceptBaseConstraint(cref, bfc);
			}
			return dtfs;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public IReadOnlyDictionary<String, ISysObj> SysObjChildren { get { return SysObjHelper<ISysObj>.Empty; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public ISysObj SysObjParent { get { return tm.g; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public String SysObjName { get { return Name; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public string SysObjDescription { get { return Name; } }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public override string ToString()
		{
			return Name;
		}
	};
}