using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using System.Linq;

using miew.BitArray;
using miew.Concurrency;

#pragma warning disable 0162

namespace agree
{
	public partial struct FeatureInfo
	{
		public FeatureInfo(BinaryReader br, Type[] type_arr)
		{
			this.i_feat = br.ReadInt32();
			this.feature = br.ReadString();
			this.maximal_type = type_arr[br.ReadUInt16()];
			this.c_failures = 0;
		}

		public void Write(BinaryWriter bw)
		{
			bw.Write(i_feat);
			bw.Write(feature);
			bw.Write(maximal_type.m_id);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public unsafe partial class TypeMgr
	{
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public void Save(CommandToken tx, BinaryWriter bw)
		{
			// write types
			bw.Write(code_size);
			bw.Write(c_types);
			for (int i = 0; i < c_types; i++)
				type_arr[i].Write(bw, i);

			// write features
			bw.Write(feat_arr.Length);
			for (int i = 0; i < feat_arr.Length; i++)
				feat_arr[i].Write(bw);

			// write strings
			strings.Write(bw);

			//// write trays
			//Tray[] rgtr = TrayMgr.GetTypeMgrTrays(this).ToArray();
			//bw.Write(rgtr.Length);
			//foreach (Tray _tr in rgtr)
			//    _tr.Write(bw);

			// write expanded TFSs for types 
			for (int i = 0; i < c_types; i++)
				type_arr[i].WriteTfss(bw);

			// write entries
			bw.Write(entry_dict.Count);
			foreach (Entry ent in entry_dict.Values)
				ent.Write(bw);
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		///
		///
		///
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public void LoadBinary(CommandToken tx, BinaryReader br)
		{
			// read types
			code_size = br.ReadInt32();
			c_types = br.ReadInt32();
			type_arr = new Type[c_types];
			int[][] tdp = new int[c_types][];
			int[][] tdc = new int[c_types][];

			code_dict = new Dictionary<BitArr, Type>();
			for (int i = 0; i < c_types; i++)
			{
				Type t = new Type(this, br, out tdp[i], out tdc[i]);
				type_dict[t.Name] = t;
				type_arr[i] = t;
				code_dict.Add(t.m_code, t);
			}

			for (int i = 0; i < c_types; i++)
			{
				Type t = type_arr[i];
				int[] td;

				td = tdp[i];
				if (td != null)
					foreach (int type_id in td)
						t.AddIParent(type_arr[type_id]);
				td = tdc[i];
				if (td != null)
					foreach (int type_id in td)
						t.AddIChild(type_arr[type_id]);
			}
			tdp = tdc = null;

			// read features
			int c_feat = br.ReadInt32();
			feat_arr = new FeatureInfo[c_feat];
			for (int i = 0; i < c_feat; i++)
			{
				FeatureInfo fi = new FeatureInfo(br, type_arr);
				feat_arr[i] = fi;
				feat_map.Add(fi.feature, fi);
			}

			// read strings
			strings.Read(br);

			// load trays
			//Dictionary<int, Tray> tray_map = new Dictionary<int, Tray>();
			//int c_tr = br.ReadInt32();
			//for (int i = 0; i < c_tr; i++)
			//{
			//    var kvp = Tray.Load(this, br);
			//    tray_map.Add(kvp.Value, kvp.Key);
			//}

			// read def/expand for types. They need to map tray indexes hence the delay in doing this.
			for (int i = 0; i < c_types; i++)
				type_arr[i].ReadTfss(br);

			// read entries
			int c_ent = br.ReadInt32();
			for (int i = 0; i < c_ent; i++)
			{
				Entry e = Entry.Read(br, this);
				entry_dict.Add(e.Name, e);
			}

			/// Resolve special types: attach special configuration types used in lists, etc.
			ResolveSpecialTypes();

			/// Resolve start symbols and some special rule types
			ResolveSpecialEntries();
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial class Strings
	{
		public void Write(BinaryWriter bw)
		{
			bw.Write(dict.Count);
			foreach (var kvp in dict.Enumerate())
			{
				bw.Write(kvp.Key);
				bw.Write(kvp.Value);
			}
		}

		public void Read(BinaryReader br)
		{
			next_string_id = br.ReadInt32();
			for (int i = 0; i < next_string_id; i++)
				dict.Add(br.ReadString(), br.ReadInt32());
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract partial class Instance : ISysObj
	{
		protected static BinaryFormatter bf = new BinaryFormatter();

		public Instance(TypeMgr tm, BinaryReader br)
		{
			this.tm = tm;
			this.m_flags = (Flags)br.ReadInt32();
			this.Name = br.ReadString();
		}

		public virtual void Write(BinaryWriter bw)
		{
			bw.Write((Int32)m_flags);
			bw.Write(Name);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract partial class Entry : Instance
	{
		protected Entry(Type t, BinaryReader br)
			: base(t.tm, br)
		{
			this.t = t;
			this._definition = Tfs.Read(br);
		}

		public static Entry Read(BinaryReader br, TypeMgr tm)
		{
			String s_type = br.ReadString();
			int typeid = br.ReadInt32();
			Type t = tm.type_arr[typeid];
			Entry e;
			switch (s_type)
			{
				case "GrammarRule":
					e = new GrammarRule(t, br);
					break;
				case "MorphologicalRule":
					e = new MorphologicalRule(t, br);
					break;
				case "LexicalRule":
					e = new LexicalRule(t, br);
					break;
				case "StartSymbol":
					e = new StartSymbol(t, br);
					break;
				case "NodeLabelTemplate":
					e = new NodeLabelTemplate(t, br);
					break;
				case "NodeMetaTemplate":
					e = new NodeMetaTemplate(t, br);
					break;
				case "LexicalEntry":
					e = new LexicalEntry(t, br);
					break;
				default:
					throw new Exception("unrecognized entry type: '" + s_type + "'");
			}
			return e;
		}

		public override void Write(BinaryWriter bw)
		{
			// read by static function
			String s_type = this.GetType().Name;
			bw.Write(s_type);
			bw.Write(t.m_id);

			// read by base class constructor
			base.Write(bw);

			// read by this class constructor
			_definition.Write(bw);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract partial class StaticExpandEntry : Entry
	{
		public StaticExpandEntry(Type t, BinaryReader br)
			: base(t, br)
		{
			//if ((m_flags & Flags.Expanded) > 0)
			//    _expanded = Tfs.Read(br);
		}

		public override void Write(BinaryWriter bw)
		{
			//if ((m_flags & Flags.Expanded) > 0)
			//    _expanded.Write(bw);

			//base.Write(bw);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract partial class DemandExpandEntry : Entry
	{

		public DemandExpandEntry(Type t, BinaryReader br)
			: base(t, br)
		{
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial class LexicalEntry : DemandExpandEntry
	{
		public LexicalEntry(Type t, BinaryReader br)
			: base(t, br)
		{
			words = new String[br.ReadInt32()];
			for (int i = 0; i < words.Length; i++)
				words[i] = br.ReadString();
		}

		public override void Write(BinaryWriter bw)
		{
			base.Write(bw);

			bw.Write(words.Length);
			foreach (String s in words)
				bw.Write(s);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial class LexicalRule : Rule
	{
		public LexicalRule(Type t, BinaryReader br)
			: base(t, br)
		{
		}

		public override void Write(BinaryWriter bw)
		{
			base.Write(bw);
		}
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial class MorphologicalRule : LexicalRule
	{
		public MorphologicalRule(Type t, BinaryReader br)
			: base(t, br)
		{
			morph_subrules = (MorphologySubrule[])bf.Deserialize(br.BaseStream);
			if (morph_subrules.Length == 0)
				throw new Exception("a morphological rule with no subrules should be a lexical rule");
		}

		public override void Write(BinaryWriter bw)
		{
			base.Write(bw);

			MorphologySubrule[] ms = morph_subrules ?? new MorphologySubrule[0];
			bf.Serialize(bw.BaseStream, ms);
		}
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract partial class GlbDag : Instance
	{
		public GlbDag(TypeMgr tm, BinaryReader br)
			: base(tm, br)
		{
			m_id = br.ReadUInt16();
			m_level = br.ReadUInt16();
			m_code = new BitArr(tm.code_size, br);
		}

		public override void Write(BinaryWriter bw)
		{
			base.Write(bw);

			bw.Write(m_id);
			bw.Write(m_level);
			m_code.Write(bw);
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial class Type : GlbDag
	{
		public Type(TypeMgr tm, BinaryReader br, out int[] parent_ids, out int[] child_ids)
			: base(tm, br)
		{
			int c;
			c = br.ReadInt32();
			if (c == 0)
				parent_ids = null;
			else
			{
				parent_ids = new int[c];
				for (int i = 0; i < c; i++)
					parent_ids[i] = br.ReadUInt16();
			}

			c = br.ReadInt32();
			if (c == 0)
				child_ids = null;
			else
			{
				child_ids = new int[c];
				for (int i = 0; i < c; i++)
					child_ids[i] = br.ReadUInt16();
			}

			c = br.ReadInt32();
			if (c == 0)
				fc = FeatureConfig.Empty;
			else
			{
				var tmp = new int[c];
				for (int i = 0; i < c; i++)
					tmp[i] = br.ReadInt32();
				throw new NotImplementedException();
				//fc = tm.fcm.Get(tmp);
			}
		}

		public void Write(BinaryWriter bw, int id)
		{
			if (id != m_id)
				throw new Exception();

			base.Write(bw);

			bw.Write(i_parents.Count);
			foreach (Type t in i_parents)
				bw.Write(t.m_id);

			bw.Write(i_children.Count);
			foreach (Type t in i_children)
				bw.Write(t.m_id);

			bw.Write(fc.Count);
			foreach (int i_feat in fc.rg_fix)
				bw.Write(i_feat);
		}

		public void WriteTfss(BinaryWriter bw)
		{
			//if ((m_flags & Type.Flags.LoadedDefinition) > 0)
			//    Definition.Write(bw);
			//if ((m_flags & Type.Flags.Expanded) > 0)
			//    Expanded.Write(bw);
		}

		public void ReadTfss(BinaryReader br)
		{
			//if ((m_flags & Type.Flags.LoadedDefinition) > 0)
			//    _definition = Tfs.Read(br);
			//if ((m_flags & Type.Flags.Expanded) > 0)
			//    _set_expanded(Tfs.Read(br));
		}

	};

	public partial class Tfs : ISysObj
	{
		public void Write(BinaryWriter bw)
		{
		//	bw.Write(this.id);
		//	bw.Write((UInt64)Edge);
		}

		public static Tfs Read(BinaryReader br)
		{
			int ttid = br.ReadInt32();
			int tfs_id = ttid & unchecked((int)0xFFFFFFC0);
			int tix = ttid & 0x1F;
			//Tray tr = tray_map[tix];
			//return new SharedPoolTfs(tr.tm, tfs_id | tr.tix, (Edge)br.ReadUInt64());
			return null;
		}
	};

}