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

using miew.Concurrency;
using miew.Debugging;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// Type in the type hierarchy. This hierarchy is a fully-connected directed-acyclic graph (DAG) where each type
	/// supports zero or more features that may be arbitrarily constrained to be any type in the hierarchy.
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public sealed partial class Type : GlbDag
	{
		public Type(TypeMgr tm, String name, List<BaseFeatConstraint> bfc)
			: base(tm, name, bfc)
		{
		}

		public static Type MakeTopType(TypeMgr tm, String name)
		{
			Type t = new Type(tm, name, null);
			t.m_id = TypeMgr.TopId;
			t._definition = tm.TopTfs;
			t._expanded = tm.TopTfs;
			return t;
		}

		public bool IsTop { get { return this == tm.TopType; } }

		/// <summary>
		/// if a type has authored constraints then, when it is the result of a unification meet
		/// and is not the same as either type, it must go through the extra step of
		/// being made well-formed. It is not sufficient to check HasLocalFeatures because
		/// a type can further constraint features that it did not introduce
		/// </summary>
		public bool HasConstraints { get { return (m_flags & Flags.HasConstraints) > 0; } }

		/// <summary>
		/// 'Atomic' types have no appropriate features in their inclusive subgraph (Copestake 2001 p.68)
		/// We term featureless types "bare." For example, *top* is not atomic, despite being bare.
		/// </summary>
		public bool IsAtomic { get { return (m_flags & Flags.Atomic) > 0; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		HashSet<int> _feat_gather = null;

		public FeatureConfig fc = FeatureConfig.Empty;

		public override Type InstanceType { get { return this; } }

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public void AddLocalFeature(int i_feat)
		{
			if ((m_flags & Flags.LoadedNonLocalFeatures) > 0)
				throw new Exception("Cannot add local features because parent features have already been calculated.");

			(_feat_gather = _feat_gather ?? new HashSet<int>()).Add(i_feat);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Internal use only. Implements lazy loading of parent features.
		/// Assumes local features have already been added to m_appropriate_features for the parent types, so this should
		/// be called for all types according to topological order.
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		internal void AddNonLocalFeatures()
		{
			if ((m_flags & Flags.HasAnyFeatures) > 0)
			{
				foreach (Type par in Parents)
				{
					if ((par.m_flags & Flags.LoadedNonLocalFeatures) == 0)
						throw new Exception("Incorrect topological order while calculating inherited features.");

					if (par.fc.Count > 0)
					{
						_feat_gather = _feat_gather ?? new HashSet<int>();
						_feat_gather.UnionWith(par.fc);
					}
				}
				/// Set the order for which features of this type are enumerated.
				/// Observation: unification failures tend to happen in more common types, as opposed to more obscure ones.
				/// Reverse this ordering and you'll see.
				/// We use the following order to unify the features of a type, i.e. according to the topological order of 
				/// their introducing type.

				fc = tm.fcm.Get(this, _feat_gather);
				_feat_gather = null;
			}
			m_flags |= Flags.LoadedNonLocalFeatures;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <returns>
		/// 'false' if the specified feature does not exist or is not appropriate for this type.
		/// </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public bool HasFeature(int i_feat)
		{
			int[] arr = fc.rg_fix;
			for (int i = 0; i < arr.Length; i++)
				if (arr[i] == i_feat)
					return true;
			return false;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public override Tfs EnsureExpanded(out bool f_did)
		{
			f_did = false;
			Tfs _tmp, _new;

			if ((_tmp = _expanded) != null)
				return _tmp;

			if (!HasAnyFeatures)
				return _expanded = Definition;

			Stopwatch stopw = Stopwatch.StartNew();

			TargetTfs tt = Unification.ExpandType(this);
			if (tt == null)
				throw new TfsException("Error expanding type '{0}': could not unify with its constraint or parents.", this.Name);

			if ((_tmp = _expanded) != null)
				return _tmp;

			int ec = tt.EdgeCount;
			if (ec == 0)
				_new = new BareTfs(tt.Type);
			//else if (ec < 12)
			//    _new = new TinyTfs(tt);
			else
				_new = tt.ToArrayTfs();
			_new.Name = Name + " - Expanded";

			if ((_tmp = _expanded) != null)
				return _tmp;

			Interlocked.Add(ref tm.ms_expand, (int)stopw.ElapsedMilliseconds);
			if ((_tmp = Interlocked.CompareExchange(ref _expanded, _new, null)) != null)
				return _tmp;
			f_did = true;
			return _new;
		}
	};
}