using System;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Threading;
using System.Resources;
using System.Threading.Tasks;
using System.Collections.ObjectModel;

using Resources = agree.Properties.Resources;

using miew.ReadOnly;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public interface ISysObj
	{
		String SysObjName { get; }

		String SysObjDescription { get; }

		IReadOnlyDictionary<String, ISysObj> SysObjChildren { get; }

		ISysObj SysObjParent { get; }
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class SysObj : ISysObj
	{
		public const String build_type =
#if DEBUG
			"debug";
#else
			"release";
#endif

		public static String GetObjectTypeName(ISysObj o)
		{
			if (o is SysObj)
				return "System";
			if (o is Grammar)
				return "Grammar";
			if (o is Type)
				return "Type";
			if (o is LexicalEntry)
				return "Lexical Entry";
			if (o is GrammarRule)
				return "Grammar Rule";
			if (o is MorphologicalRule)
				return "Inflection Rule";
			if (o is LexicalRule)
				return "Lexical Rule";
			if (o is StartSymbol)
				return "Start Symbol";
			if (o is NodeLabel)
				return "Node Label";
			throw new Exception();
		}

		public String VersionInfo()
		{
			return String.Format("aɢʀee engine {0} {1} {2}-bit",
				new DateTime(2000, 1, 1).AddDays(System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Build),
				build_type,
				IntPtr.Size * 8);
		}

		SysObj()
		{
			sc = new SysCommands(this);
		}

		SysCommands sc;

		static SysObj _singleton = new SysObj();
		public static SysObj Instance { get { return _singleton; } }
		public SysCommands Commands { get { return sc; } }


		public event Action<CommandToken, String> TransactionStatusEvent = null;
		public event Action<CommandToken, CommandToken> AdditionalTransactionEvent = null;

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		internal void AddTransaction(CommandToken tx, CommandToken additional)
		{
			Action<CommandToken, CommandToken> ev = AdditionalTransactionEvent;
			if (ev != null)
				ev.Invoke(tx, additional);
		}
		internal void TransactionStatus(CommandToken tx)
		{
			TransactionStatus(tx, default(String));
		}
		internal void TransactionStatus(CommandToken tx, String fmt, params Object[] args)
		{
			Action<CommandToken, String> ev = TransactionStatusEvent;
			if (ev != null)
			{
				if (fmt != null)
					fmt = String.Format(fmt, args);
				ev.Invoke(tx, fmt);
			}
		}

		internal ResourceManager ResourceManager { get { return Resources.ResourceManager; } }

		internal SysObjHelper<ISysObj> sys_objs = new SysObjHelper<ISysObj>();

		public string SysObjName
		{
			get { return "sys"; }
		}

		public string SysObjDescription
		{
			get { return "agree grammar engineering environment system instance"; }
		}

		public IReadOnlyDictionary<String, ISysObj> SysObjChildren
		{
			get { return sys_objs; }
		}

		public ISysObj SysObjParent
		{
			get { return this; }
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class SysObjHelper<T> : KeyedCollection<String, T>, IReadOnlyDictionary<String, ISysObj> where T : class, ISysObj
	{
		public SysObjHelper()
			: base(StringComparer.OrdinalIgnoreCase)
		{
		}

		public static IEnumerable<ISysObj> GetNamePrefixMatches(ISysObj sys_obj, String s_pfx)
		{
			if (s_pfx == "*")
				s_pfx = "";
			foreach (var kvp in sys_obj.SysObjChildren)
			{
				Debug.Assert(kvp.Value.SysObjName == kvp.Key);
				if (kvp.Key.StartsWith(s_pfx))
					yield return kvp.Value;
			}
		}

		static Dictionary<String, T> _ed = null;
		static SysObjHelper<T> _empty = null;
		static public SysObjHelper<T> Empty
		{
			get { return _empty = _empty ?? new SysObjHelper<T>(); }
		}

		protected override String GetKeyForItem(T item)
		{
			return item.SysObjName;
		}

		public new IDictionary<String, T> Dictionary
		{
			get
			{
				if (base.Dictionary == null)
					return _ed = _ed ?? new Dictionary<String, T>();
				return base.Dictionary;
			}
		}

		bool IReadOnlyDictionary<String, ISysObj>.ContainsKey(String key)
		{
			if (base.Dictionary == null)
				return false;
			return base.Dictionary.ContainsKey(key);
		}

		ICollection<String> IReadOnlyDictionary<String, ISysObj>.Keys
		{
			get { return base.Dictionary.Keys; }
		}

		ICollection<ISysObj> IReadOnlyDictionary<String, ISysObj>.Values
		{
			get { return ((IEnumerable<T>)this).Cast<ISysObj>().ToArray(); }
		}

		bool IReadOnlyDictionary<String, ISysObj>.TryGetValue(String key, out ISysObj value)
		{
			T v;
			if (base.Dictionary == null || !base.Dictionary.TryGetValue(key, out v))
			{
				value = null;
				return false;
			}
			value = v;
			return true;
		}

		ISysObj IReadOnlyDictionary<String, ISysObj>.this[String key]
		{
			get
			{
				if (base.Dictionary == null)
					throw new KeyNotFoundException();
				return base.Dictionary[key];
			}
		}

		bool IReadOnlyDictionary<String, ISysObj>.IsReadOnly
		{
			get { return true; }
		}

		int IReadOnlyDictionary<String, ISysObj>.Count
		{
			get { return base.Count; }
		}

		IEnumerator<KeyValuePair<String, ISysObj>> IEnumerable<KeyValuePair<String, ISysObj>>.GetEnumerator()
		{
			if (base.Dictionary == null)
				yield break;
			foreach (var kvp in base.Dictionary)
				yield return new KeyValuePair<String, ISysObj>(kvp.Key, kvp.Value);
		}

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
	};
}