using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

using miew.Tokenization;
using miew.Reflection;
using miew.Enumerable;

namespace agree
{
	using SysType = System.Type;

	public sealed partial class Config
	{
		public sealed class Parser : INotifyPropertyChanged
		{
			/// <summary>
			/// Options for configuring which rule daughter the parser will try to unify first
			/// </summary>
			public enum KeyStrategy
			{
				/// <summary>
				/// The parser will unify the rule daughter whose key feature (configurable) has a certain key
				/// value (configurable)
				/// </summary>
				KeyDriven,	// use, e.g.  KEY-ARG +
				/// <summary>
				/// The parser will unify rule daughters left-to-right
				/// </summary>
				LeftToRight,
				/// <summary>
				/// The parser will unify rule daughters right-to-left
				/// </summary>
				RightToLeft,
				/// <summary>
				/// The parser will unify the rule daughter that is designated as the head daughter
				/// </summary>
				HeadDaughter,	// use, e.g. HD-DTR		(todo)
			};

			public enum DependencyScope
			{
				Unidirectional,
				TwoWay,
			};

			Config cfg;
			public Parser(Config cfg)
			{
				this.cfg = cfg;
			}

			public Config _cfg { get { return cfg; } }

			public HashSet<Char> punctuation_chars = new HashSet<char>();

			public bool f_irregular_forms_only = false;
			public bool f_lex_entries_can_fail = false;
			public bool f_case_sensitive = false;

			KeyStrategy key_strategy = KeyStrategy.KeyDriven;
			Double timeout_sec = Double.MaxValue;
			SysType T_tokenizer = typeof(SpaceCharTokenizer);
			DependencyScope chart_dependency_scope = DependencyScope.TwoWay;
			FsPathPair[] chart_dependency_paths = null;

			public List<String> deleted_daughters = null;
			public List<String> packing_restrictors = null;
			public byte[] deleted_daughter_map;

			public QuickCheckStrategy qcs;

			public enum UnifierType
			{
				Incremental,
				n_way,
				qd,
			}
			public UnifierType unifier = UnifierType.n_way;
			public UnifierType checker = UnifierType.n_way;

			[Flags]
			public enum PackingOpts : sbyte
			{
				None = 0,
				Proactive = 1,
				Retroactive = 2,
				Equivalence = 3,
				Full = Equivalence,

				Only = 4,
				ProactiveOnly = Proactive | Only,
				RetroactiveOnly = Retroactive | Only,
				EquivalenceOnly = Equivalence | Only,
			};

			public PackingOpts packing = PackingOpts.None;//PackingOpts.Full;
			public byte[] packing_restrictor_map;
			public bool f_variadic_unpack = false;

			public TypeMgrCompiledFsPath RuleArgsPath;
			public TypeMgrCompiledFsPath KeyDaughterPath;
			public TypeMgrCompiledFsPath OrthPath;

			public List<String> span_only_rules = null;

			public int chart_limit = 100;

			public int max_edges = 4000;

			public bool f_qc_morph = true;
			public bool f_qc_parse = true;
			public List<String> s_quick_check_paths;

			public bool f_autotune = false;

			public int[] DeletedDaughters;

			public int c_tasks_max = int.MaxValue;

			public void CompileGrammarPaths(TypeMgr tm)
			{
				Config gc = tm.config;

				RuleArgsPath = new TypeMgrCompiledFsPath(tm, gc.grammar.rule_args_path);
				KeyDaughterPath = new TypeMgrCompiledFsPath(tm, gc.grammar.key_daughter_path);
				OrthPath = new TypeMgrCompiledFsPath(tm, gc.grammar.orth_path);

				//UnificationQd.arg0_path = RuleArgsPath.Append(tm.f_ix_list_head).ToArray();
				//UnificationQd.arg1_path = RuleArgsPath.Append(tm.f_ix_list_tail).Append(tm.f_ix_list_head).ToArray();

				if (deleted_daughters != null)
				{
					int[] rgdd = deleted_daughters.Select(s => tm.feat_map[s].i_feat).ToArray();
					deleted_daughter_map = new byte[tm.feat_arr.Length];
					foreach (int i_feat in rgdd)
						deleted_daughter_map[i_feat] = 1;

					DeletedDaughters = rgdd;
				}

				if (packing_restrictors != null)
				{
					packing_restrictor_map = new byte[tm.feat_arr.Length];
					foreach (int i_feat in packing_restrictors.Select(s => tm.feat_map[s].i_feat))
						packing_restrictor_map[i_feat] = 1;
				}

				if ((f_qc_parse || f_qc_morph) && s_quick_check_paths != null && s_quick_check_paths.Count > 0)
					qcs = new QuickCheckStrategy(tm, s_quick_check_paths);

			}

			/// <summary>
			/// Strategy used by the parser to determine which of a mother rule's unifications to perform
			/// first. The available options are documented in ParseConfiguration.KeyStrategy
			/// </summary>
			public KeyStrategy ParsingStrategy
			{
				get { return key_strategy; }
				set
				{
					if (value != key_strategy)
					{
						key_strategy = value;
						NotifyPropertyChanged("ParsingStrategy");
					}
				}
			}

			/// <summary>
			/// If 'true', the presence of an irregular form in a morphological analysis hypothesis will
			/// block the proposal of any regularly inflected forms.
			/// </summary>
			public bool IrregularFormsOnly
			{
				get { return f_irregular_forms_only; }
				set
				{
					if (value != f_irregular_forms_only)
					{
						f_irregular_forms_only = value;
						NotifyPropertyChanged("IrregularFormsOnly");
					}
				}
			}

			/// <summary>
			/// Number of seconds allowed per parse. If the parse is not complete within this amount of time,
			/// the parse will be aborted with a ParseTimeoutException.
			/// </summary>
			public Double ItemTimeoutSeconds
			{
				get { return timeout_sec; }
				set
				{
					if (value != timeout_sec)
					{
						timeout_sec = value;
						NotifyPropertyChanged("TimeoutSeconds");
					}
				}
			}

			public SysType TokenizerType
			{
				get { return T_tokenizer; }
				set
				{
					if (value == null)
						throw new ArgumentNullException();
					if (!value.HasInterface(typeof(ITokenizer)))
						throw new ArgumentException("Tokenizer type must be derived from ITokenizer");
					if (value != T_tokenizer)
					{
						T_tokenizer = value;
						NotifyPropertyChanged("TokenizerType");
					}
				}
			}

			public DependencyScope ChartDependencyScope
			{
				get { return chart_dependency_scope; }
				set
				{
					if (value != chart_dependency_scope)
					{
						chart_dependency_scope = value;
						NotifyPropertyChanged("ChartDependencyScope");
					}
				}
			}

			public IEnumerable<KeyValuePair<String, String>> ChartDependencyPaths
			{
				set
				{
					chart_dependency_paths = value.Select(kvp => new FsPathPair(kvp.Key, kvp.Value)).ToArray();
					if (chart_dependency_paths.Length == 0)
						chart_dependency_paths = null;
					NotifyPropertyChanged("ChartDependencyPaths");
				}
			}
			public FsPathPair[] ChartDependencyPathsArray { get { return chart_dependency_paths; } }


			public event PropertyChangedEventHandler PropertyChanged;

			/// <summary>
			/// Notify interested parties that a configuration option has changed.
			/// </summary>
			void NotifyPropertyChanged(String s_field)
			{
				var h = PropertyChanged;
				if (h != null)
					h(this, new PropertyChangedEventArgs(s_field));
			}
		};

		public class NodeLabels : INotifyPropertyChanged
		{
			bool simple_tree_display = false;
			FsPath label_path = null;
			FsPath prefix_path = null;
			FsPath suffix_path = null;
			FsPath recursive_path = null;
			FsPath local_path = null;
			FsPath label_fs_path = null;
			String label_template_type = null;
			String meta_template_type = "meta";

			public bool SimpleTreeDisplay
			{
				get { return simple_tree_display; }
				set
				{
					if (value != simple_tree_display)
					{
						simple_tree_display = value;
						NotifyPropertyChanged("SimpleTreeDisplay");
					}
				}
			}

			public FsPath LabelPath
			{
				get { return label_path; }
				set
				{
					if (simple_tree_display)
						throw new Exception("Cannot specify 'LabelPath' when using 'SimpleTreeDisplay'");
					if (value != label_path)
					{
						label_path = value;
						NotifyPropertyChanged("LabelPath");
					}
				}
			}

			public FsPath PrefixPath
			{
				get { return prefix_path; }
				set
				{
					if (simple_tree_display)
						throw new Exception("Cannot specify 'PrefixPath' when using 'SimpleTreeDisplay'");
					if (value != prefix_path)
					{
						prefix_path = value;
						NotifyPropertyChanged("PrefixPath");
					}
				}
			}

			public FsPath SuffixPath
			{
				get { return suffix_path; }
				set
				{
					if (simple_tree_display)
						throw new Exception("Cannot specify 'SuffixPath' when using 'SimpleTreeDisplay'");
					if (value != suffix_path)
					{
						suffix_path = value;
						NotifyPropertyChanged("SuffixPath");
					}
				}
			}

			public FsPath RecursivePath
			{
				get { return recursive_path; }
				set
				{
					if (simple_tree_display)
						throw new Exception("Cannot specify 'RecursivePath' when using 'SimpleTreeDisplay'");
					if (value != recursive_path)
					{
						recursive_path = value;
						NotifyPropertyChanged("RecursivePath");
					}
				}
			}

			public FsPath LocalPath
			{
				get { return local_path; }
				set
				{
					if (simple_tree_display)
						throw new Exception("Cannot specify 'LocalPath' when using 'SimpleTreeDisplay'");
					if (value != local_path)
					{
						local_path = value;
						NotifyPropertyChanged("LocalPath");
					}
				}
			}

			public FsPath LabelFsPath
			{
				get { return label_fs_path; }
				set
				{
					if (simple_tree_display)
						throw new Exception("Cannot specify 'LabelFsPath' when using 'SimpleTreeDisplay'");
					if (value != label_fs_path)
					{
						label_fs_path = value;
						NotifyPropertyChanged("LabelFsPath");
					}
				}
			}

			public String LabelTemplateType
			{
				get { return label_template_type; }
				set
				{
					if (simple_tree_display)
						throw new Exception("Cannot specify 'LabelTemplateType' when using 'SimpleTreeDisplay'");
					if (value != label_template_type)
					{
						label_template_type = value;
						NotifyPropertyChanged("LabelTemplateType");
					}
				}
			}

			public String MetaTemplateType
			{
				get { return meta_template_type; }
				set
				{
					if (simple_tree_display)
						throw new Exception("Cannot specify 'MetaTemplateType' when using 'SimpleTreeDisplay'");
					if (value != meta_template_type)
					{
						meta_template_type = value;
						NotifyPropertyChanged("MetaTemplateType");
					}
				}
			}


			public event PropertyChangedEventHandler PropertyChanged;

			/// <summary>
			/// Notify interested parties that a configuration option has changed.
			/// </summary>
			void NotifyPropertyChanged(String s_field)
			{
				var h = PropertyChanged;
				if (h != null)
					h(this, new PropertyChangedEventArgs(s_field));
			}
		};
	};
}