#define THAI
using System.Reflection;
using System;
using System.IO;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Data;
using System.Diagnostics;
using System.Globalization;
using System.Windows.Controls;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;

using agree;
using agree.itsdb;
using agree.Wpf.Util;

using SysFile = System.IO.File;
using SysType = System.Type;


using ActiproSoftware.Windows.Controls.Ribbon;


namespace agree.Wpf
{

	public class CommandResultResponder
	{
		readonly public ISysObj so;

		public CommandResultResponder(ISysObj so)
		{
			this.so = so;
		}

		//public void QueueSynchronousAction(CommandToken ct, Action a)
		//{
		//    Application.Current.Dispatcher.BeginInvoke(new Action(() =>
		//        {
		//            List<Action> la;
		//            if (!continuations.TryGetValue(ct, out la))
		//                continuations.Add(ct, la = new List<Action>());
		//            la.Add(a);
		//            CheckContinuation(ct);
		//        }), null);
		//}

		//void CheckContinuation(CommandToken ct)
		//{
		//    Application.Current.Dispatcher.BeginInvoke(new Action(() =>
		//        {
		//            if (ct.Task.IsCompleted)
		//            {
		//                List<Action> la = continuations[ct];
		//                continuations.Remove(ct);
		//                foreach (Action a in la)
		//                    a();
		//            }
		//        }), null);
		//}

		//Dictionary<CommandToken, List<Action>> continuations = new Dictionary<CommandToken, List<Action>>();

		public void QueueResultContinuation(CommandToken ct)
		{
			MainWindow w = MainWindow.w;

			if (ct is SysCommands.CmdTokInteractive)
				ct = (ct as SysCommands.CmdTokInteractive).Task.Result;

			if (ct.ErrorMessage != null)
			{
				Application.Current.Dispatcher.BeginInvoke(new Action(() =>
						{
							w.log_window.WriteLine(ct.ErrorMessage);
						}),
						DispatcherPriority.Normal,
						null);
				return;
			}

			if (ct is SysCommands.CmdTokLoadGrammar)
			{
				((Task<Grammar>)ct.Task).ContinueWith(
					t =>
					{
						Grammar g = t.Result;
						if (!g.IsLoaded)
							return;

						Application.Current.Dispatcher.BeginInvoke(new Action(() =>
									{
										CommandResultResponder cr = new CommandResultResponder(g);

										w.cmd_responders.Add(g, cr);
										w.object_browser.AddSysObj(g);
										w.log_window.CurrentHandler = cr;
									}),
								DispatcherPriority.Normal,
								null);
					}, TaskContinuationOptions.ExecuteSynchronously);
			}
			else if (ct is SysCommands.CmdTokClearConsole)
			{
				Application.Current.Dispatcher.BeginInvoke(new Action(() =>
				{
					w.log_window.Clear();
				}),
				DispatcherPriority.Normal,
				null);
			}
			else if (ct is SysCommands.CmdTokDisplayMessage)
			{
			}
			else if (ct is SysCommands.CmdTokQueryNamePrefix)
			{
				var z = ct as SysCommands.CmdTokQueryNamePrefix;
				z.Task.ContinueWith(
					t =>
					{
						ISysObj[] rgso = t.Result;
						Application.Current.Dispatcher.BeginInvoke(new Action(() =>
						{
							if (rgso.Length == 0)
								w.log_window.WriteLine("No names.");
							else
								foreach (ISysObj _s in rgso)
								{
									w.log_window.WriteLine("{0}", _s.SysObjName);
								}
						}),
						DispatcherPriority.Normal,
						null);
					});
			}
			else if (ct is SysCommands.CmdTokShowItem)
			{
				var z = ct as SysCommands.CmdTokShowItem;
				z.Task.ContinueWith(
					t =>
					{
						ISysObj o = t.Result;
						Application.Current.Dispatcher.BeginInvoke(new Action(() =>
						{
							//CommandBinding q = MyDockSite.g_docksite.CommandBindings.OfType<CommandBinding>().First(cb => cb.Command is ShowItemCommand);
							//q.Command.Execute(o);
							ShowItemCommand.BetterExecute(z.iso, o);
						}),
						DispatcherPriority.Normal,
						null);
					});
			}
			else if (ct is SysCommands.CmdTokShowHighlightedItem)
			{
				var z = ct as SysCommands.CmdTokShowHighlightedItem;
				z.Task.ContinueWith(
					t =>
					{
						Application.Current.Dispatcher.BeginInvoke(new Action(() =>
						{
							//new ShowHighlightedItemCommand(w, z.obj_parent, z.obj_highlight).Execute(null, w);
							MyDockSite mds = MyDockSite.g_docksite;
							var etw = new ExpandedWindow((Tfs)z.obj_parent, (Tfs)z.obj_highlight);
							mds.wksp.tmc.Items.Add(etw);
							etw.Activate();
						}),
						DispatcherPriority.Normal,
						null);
					});
			}
			else if (ct is SysCommands.CmdTokUnloadGrammar)
			{
				var z = ct as SysCommands.CmdTokUnloadGrammar;
				z.Task.ContinueWith(
					t =>
					{
						bool b = t.Result;
						if (b)
						{
							Application.Current.Dispatcher.BeginInvoke(new Action(() =>
							{
								CommandResultResponder cr;
								if (w.cmd_responders.TryGetValue(z.g, out cr))
								{
									if (w.log_window.CurrentHandler == cr)
										w.log_window.CurrentHandler = w.cmd_responders.Values.First(crx => crx.so != z.g);

									//w.object_browser.RemoveSysObj(cr.so);

									w.cmd_responders.Remove(z.g);
								}
							}),
							DispatcherPriority.Normal,
							null);
						}
					});
			}
			else if (ct is SysCommands.CmdTokParse)
			{
				var z = ct as SysCommands.CmdTokParse;
				z.Task.ContinueWith(
					t =>
					{
						ParseControl pc = t.Result;
						Application.Current.Dispatcher.BeginInvoke(new Action(() =>
						{
							var cr = new CommandResultResponder(pc);
							w.cmd_responders.Add(pc, cr);
							w.object_browser.AddSysObj(pc);

							w.log_window.WriteLine("{0}: {1} parse(s)", pc.SysObjName, pc.chart.CompletedEdges.Count());

						}),
						DispatcherPriority.Normal,
						null);
					});
			}
			else if (ct is SysCommands.CmdTokChangeDefaultObject)
			{
				((Task<ISysObj>)ct.Task).ContinueWith(
					t =>
					{
						ISysObj so_new = t.Result;
						Application.Current.Dispatcher.BeginInvoke(new Action(() =>
						{
							CommandResultResponder cr;
							if (w.cmd_responders.TryGetValue(so_new, out cr))
								w.log_window.CurrentHandler = cr;
						}),
						DispatcherPriority.Normal,
						null);
					});
			}

			//CheckContinuation(ct);
		}

	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public partial class MainWindow : RibbonWindow
	{
		const String build_type =
#if DEBUG
 "debug";
#else
			"release";
#endif

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

		public static MainWindow w;
		[STAThread()]
		static void Main(String[] args)
		{
			Application app = new Application();

			Uri pageUri = new Uri("/Dictionary1.xaml", UriKind.Relative);
			app.Resources.Source = pageUri;

			w = new MainWindow(args);
			w.Title = "aɢʀee :: another ɢʀammar enɢineerinɢ environment";

			app.Run(w);
		}

		private void DoNothingCommand(object target, ExecutedRoutedEventArgs args)
		{
		}


		//public Grammar g;

		public static OptionSetting3d optionSetting3d;
		//public static ShowCommand Show = new ShowCommand();

		String[] args;

		public MainWindow(String[] args)
		{
			this.args = args;

			this.Loaded += new RoutedEventHandler(MainWindow_Loaded);

			this.FontSize = 13;

			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.New, DoNothingCommand));
			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Open, OpenFileCommand));
			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Save, SaveCommand));
			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.SaveAs, DoNothingCommand));
			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Print, PrintCommand));
			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Cut, DoNothingCommand));
			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Copy, DoNothingCommand));
			this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Paste, DoNothingCommand));

			//			this.CommandBindings.Add(new LoadGrammar(this).CommandBinding);
			//			this.CommandBindings.Add(new SaveGrammarBinary(this).CommandBinding);

			//this.CommandBindings.Add(Show.CommandBinding);
			optionSetting3d = new OptionSetting3d(this);
			this.CommandBindings.Add(optionSetting3d.CommandBinding);
			//CommandManager.regi

			this.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
			this.Width = 1024;
			this.Height = 800;
			this.Content = new MyDockPanel(this);
			this.ApplicationName = "aɢʀee :: another ɢʀammar enɢineerinɢ environment";

		}

		public InteractiveWindow log_window;

		public ObjectBrowserToolWindow object_browser;

		//public List<CommandResultResponder> rgso = new List<CommandResultResponder>();
		public Dictionary<ISysObj, CommandResultResponder> cmd_responders = new Dictionary<ISysObj, CommandResultResponder>();

		public SysObj so;

		void MainWindow_Loaded(object sender, RoutedEventArgs e)
		{
			so = agree.SysObj.Instance;

			CommandResultResponder cr = new CommandResultResponder(so);
			cmd_responders.Add(so, cr);

			new SysCommands.CmdTokDisplayVersionInfo(so);

			/// Create a status log window
			log_window = new InteractiveWindow(this, so);
			MyDockSite.g_docksite.wksp.tmc.Items.Add(log_window);
			log_window.Activate();

			so.TransactionStatusEvent += (x, msg) => Application.Current.Dispatcher.BeginInvoke(
						new Action(() => log_window.WriteLine(x, msg)),
						DispatcherPriority.Normal,
						null);
			so.AdditionalTransactionEvent += (x, tx_add) => Application.Current.Dispatcher.BeginInvoke(
						new Action(() => cmd_responders[x.iso].QueueResultContinuation(tx_add)),
						DispatcherPriority.Normal,
						null);

			log_window.WriteLine("Application started.");
			log_window.WriteLine(ClientVersionInfo());

			Config cfg = new Config();

			int arg_ix;
			if ((arg_ix = Array.IndexOf<String>(args, "-singlethreaded")) != -1)
			{
				log_window.WriteLine("disabling multi-threading");
				args = args.Where((_e, x) => x != arg_ix).ToArray();
				cfg.system.MultiThreading = false;
			}

			try
			{
				SysCommands.CmdTokLoadGrammar lg_cmd = null;
				for (int i = 0; i < args.Length; i++)
				{
					String arg = args[i];
					if (arg[0] != '-' && SysFile.Exists(arg))
					{
						//ExecuteCommand<LoadGrammar>(Path.GetFullPath(arg));
						lg_cmd = new SysCommands.CmdTokLoadGrammar(so, Path.GetFullPath(arg), null, cfg);
						cr.QueueResultContinuation(lg_cmd);
					}

					if (i < args.Length - 1 && arg.StartsWith("-c"))
					{
						String s_cmd = args[i + 1].Trim('\"');
						if (lg_cmd != null)
							lg_cmd.QueueResultInteraction(s_cmd).ContinueWith(q => cr.QueueResultContinuation(q.Result));
						else
						{
							var x = new SysCommands.CmdTokInteractive(cr.so, s_cmd);
							cr.QueueResultContinuation(x);
						};
					}
				}
			}
			catch (DebuggingTfsDisplayException ex)
			{
				foreach (var te in ex.Tfs)
					ShowItemCommand.BetterExecute(null, te);
			}

			log_window.Focus();
		}

		public void ExecuteCommand<T>(Object param) where T : CommandBase
		{
			ExecuteCommand<T>(this, param);
		}
		public void ExecuteCommand<T>(IInputElement source, Object param) where T : CommandBase
		{
			CommandBindings
					.OfType<CommandBinding>()
					.Select(cb => cb.Command as T)
					.First(c => c != null)
					.Execute(param, source);
		}

	};

#if false
	public class LoadGrammar : CommandBase
	{
		public LoadGrammar(MainWindow w)
			: base(w, "Load Grammar", "LoadGrammar", typeof(LoadGrammar))
		{

		}

		protected override void OnExecute(object target, ExecutedRoutedEventArgs args)
		{
			String filename = args.Parameter as String;
			if (filename == null)
			{
				MessageBox.Show("Incorrect argument 'null' for LoadGrammar command.", "Command Error", MessageBoxButton.OK, MessageBoxImage.Error);
			}
			else if (!System.IO.File.Exists(filename))
			{
				String msg = String.Format("The file '{0}' could not be found.", filename);
				MessageBox.Show(msg, "File not found", MessageBoxButton.OK, MessageBoxImage.Error);
			}
			else
			{
				String file_base = Path.GetFileNameWithoutExtension(filename);

				 w.so.Commands.LoadGrammar(file_base, filename).Task.ContinueWith(t =>
					{
						Grammar g = t.Result;
						Application.Current.Dispatcher.BeginInvoke(new Action(() => 
									{
										w.object_browser.AddSysObj(g);
										w.log_window.Default = g;
									}),
								DispatcherPriority.Normal,
								null);
					});
			}
			args.Handled = true;
		}

	};


	public class SaveGrammarBinary : CommandBase
	{
		public SaveGrammarBinary(MainWindow w)
			: base(w, "Save grammar in binary format", "SaveGrammarBinary", typeof(SaveGrammarBinary))
		{
		}

		protected override void OnExecute(object target, ExecutedRoutedEventArgs args)
		{
			Grammar g = args.Parameter as Grammar;
			if (g == null)
				return;
			//String filename = args.Parameter as String;
			//if (filename == null)
			//{
			//    MessageBox.Show("Incorrect argument 'null' for LoadGrammar command.", "Command Error", MessageBoxButton.OK, MessageBoxImage.Error);
			//}
			//else if (!System.IO.File.Exists(filename))
			//{
			//    String msg = String.Format("The file '{0}' could not be found.", filename);
			//    MessageBox.Show(msg, "File not found", MessageBoxButton.OK, MessageBoxImage.Error);
			//}
			//else
			//Grammar g = args. as Grammar;

			String filename = Path.GetFullPath(g.Name + ".bg");

			//			Task.Factory.StartNew(() => g.Save(filename));

			args.Handled = true;
		}

	};
#endif


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if false
	class My3dGrid : Grid
	{
		public My3dGrid(Grammar g, Edge ce)
		{
#if true
			Zoom3D m3d = new Zoom3D(g, ce, this);
#else
			Main3D m3d = new Main3D(g, ce, this);
#endif

#if true
			this.ColumnDefinitions.Add(new ColumnDefinition());
			m3d.SetValue(Grid.ColumnProperty, 0);
			this.Children.Add(m3d);
#else
			this.Children.Add(m3d);
#endif
		}
	};
#endif

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	abstract class MainGrid : Grid
	{
		public MainGrid()
		{
			this.HorizontalAlignment = HorizontalAlignment.Left;
			this.VerticalAlignment = VerticalAlignment.Top;
			this.ShowGridLines = true;
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	class HorizontalTfsGrid : MainGrid
	{
		Random rnd = new Random();

		public HorizontalTfsGrid(Grammar g, IEnumerable<Tfs> ce)
		{
			int j = 0;
			foreach (Tfs te in ce)
			{
				TfsControl tc = new TfsControl();
				tc._TfsEdge = te;
				tc.FontFamily = new FontFamily("Arial");
				tc.Margin = new Thickness(10);
				this.ColumnDefinitions.Add(new ColumnDefinition());
				tc.SetValue(Grid.ColumnProperty, j);
				this.Children.Add(tc);

				j++;

				//tc.Background = new SolidColorBrush(Color.FromArgb((byte)rnd.Next(), (byte)rnd.Next(), (byte)rnd.Next(), (byte)rnd.Next()));
			}

			this.ShowGridLines = true;
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	class SingleTfsGrid : Grid
	{
		Random rnd = new Random();

		public SingleTfsGrid(Grammar g, Tfs ce)
		{
			TfsControl tc = new TfsControl();
			tc._TfsEdge = ce;
			tc.FontFamily = new FontFamily("Arial");
			tc.Margin = new Thickness(10);
			this.Children.Add(tc);
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	class TwoColumnDefinitionExpandedGrid : MainGrid
	{
		public TwoColumnDefinitionExpandedGrid(Grammar g, IEnumerable<Tfs> ce)
		{
			this.Margin = new Thickness(20);

			this.ColumnDefinitions.Add(new ColumnDefinition());
			this.ColumnDefinitions.Add(new ColumnDefinition());
			this.ColumnDefinitions.Add(new ColumnDefinition());
			this.ColumnDefinitions.Add(new ColumnDefinition());
			this.RowDefinitions.Add(new RowDefinition());

			int j = 1;
			foreach (Tfs e in ce)
			{
				this.RowDefinitions.Add(new RowDefinition());

				TfsControl tc0 = new TfsControl();
				tc0._TfsEdge = e;
				tc0.FontFamily = new FontFamily("Arial");
				tc0.Margin = new Thickness(10);
				tc0.SetValue(Grid.RowProperty, j);
				tc0.SetValue(Grid.ColumnProperty, 1);

				TfsControl tc1 = new TfsControl();
				tc1._TfsEdge = e.Type.Expanded;
				tc1.FontFamily = new FontFamily("Arial");
				tc1.Margin = new Thickness(10);
				tc1.SetValue(Grid.ColumnProperty, 2);
				tc1.SetValue(Grid.RowProperty, j);

				this.Children.Add(tc0);
				this.Children.Add(tc1);

				j++;
			}

			this.RowDefinitions.Add(new RowDefinition());
			this.ShowGridLines = true;
		}
	};

	/// <summary> /// Converter to use in WPF triggers that returns true when /// 'value' is less than 'parameter'. /// </summary>
	public class LessThanConverter : IValueConverter
	{
		public object Convert(object value, SysType targetType, object parameter, System.Globalization.CultureInfo culture)
		{
			int v1 = System.Convert.ToInt32(value);
			int v2 = System.Convert.ToInt32(parameter);
			return (object)(bool)(v1 < v2);
		}
		public object ConvertBack(object value, SysType targetType, object parameter, System.Globalization.CultureInfo culture)
		{
			throw new NotImplementedException();
		}
	};


}