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

using glue.Tasks;

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 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.m_flags |= Flags.Expanded | Flags.LoadedDefinition;
			t._definition = default(TfsEdge);
			t._expand_task = Tasks.FromResult(default(TfsEdge));
			return t;
		}

		/// <summary>
		/// returns 'true' for detached top
		/// </summary>
		public bool IsTop { get { return this == mgr.TopType; } }

		/// <summary>
		/// </summary>
		public bool HasLocalFeatures { get { return (m_flags & Flags.HasLocalFeatures) > 0; } }

		/// <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; } }

		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.");

			appropriate_feat = appropriate_feat ?? new HashSet<int>();
			appropriate_feat.Add(i_feat);
			m_flags |= Flags.HasLocalFeatures;
		}


		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		HashSet<int> appropriate_feat = null;
		public FeatureConfig fc = FeatureConfig.Empty;

		public int[] _deprec_feat_indexes;
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <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)
					{
						appropriate_feat = appropriate_feat ?? new HashSet<int>();
						appropriate_feat.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 = mgr.fcm.Get(appropriate_feat);
			}

			_deprec_feat_indexes = fc.rg_fix;
#if DEBUG
			foreach (int i in _deprec_feat_indexes)
				mgr.feat_arr[i].AppropriateFor.Add(this);
#endif
			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)
		{
			for (int i = 0; i < _deprec_feat_indexes.Length; i++)
				if (_deprec_feat_indexes[i] == i_feat)
					return true;
			return false;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected /*volatile*/ Task<TfsEdge> _expand_task = null;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]	// side effects
		public override TfsEdge Expanded
		{
			get
			{
				if (!HasAnyFeatures)
					return _definition;

				if (_expand_task == null)
				{
					// If we're in a task already, do the job synchronously. In either case, only the race winner proceeds with the job.
					if (glue.Debugging.Nop.single_threaded || Task.CurrentId.HasValue)
					{
						TaskCompletionSource<TfsEdge> tcs = new TaskCompletionSource<TfsEdge>();
						if (Interlocked.CompareExchange(ref _expand_task, tcs.Task, null) == null)
							tcs.SetResult(_do_expand());
					}
					else
					{
						Task<TfsEdge> t = new Task<TfsEdge>(_do_expand);

						if (Interlocked.CompareExchange(ref _expand_task, t, null) == null)
							t.Start(TaskScheduler.Default);
					}
					// at this point, the value of _expand_task has been written by the race winner
				}
				else
				{
					//Task t_cur;
					//if (Task.CurrentId.HasValue && (t_cur = Tasks.Self) != null && !t_cur.IsCompleted && t_cur.HasAsAncestor(_expand_task))
					//throw new TfsException("Error expanding type '{0}': constraint refers to itself.", this.Name);
				}
				return _expand_task.Result;
			}
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// 
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		TfsEdge _do_expand()
		{
			Tray tr = mgr.g.loadtray;
			Stopwatch stopw = Stopwatch.StartNew();

			TfsEdge result;
			if (!new Unification.Expander(tr).ExpandType(this, out result))
				throw new TfsException("Error expanding type '{0}': could not unify with its constraint or parents.", this.Name);

			result.RegisterName(Name);

			m_flags |= Flags.Expanded;

			Interlocked.Add(ref mgr.ms_expand, (int)stopw.ElapsedMilliseconds);
			return result;
		}

		internal void _set_expanded(TfsEdge e)
		{
			_expand_task = Tasks.FromResult(e);
		}

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