using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Threading;

namespace MultiDispatcherTest
{
	public sealed class UIEntrance : IDisposable
	{
		private string _name;

		private Thread _uiThread;

		private Dispatcher _executeDispatcher;

		/// <summary>
		/// 새 작업 DispatcherSynchronizationContext.
		/// </summary>
		private DispatcherSynchronizationContext _executeDispatcherSynchronization;

		private Dispatcher _callerDispatcher;

		/// <summary>
		/// _callerDispatcher 가 종료 될 때 함께 수행해야할 종료 처리.
		/// </summary>
		private Action _exitProcedure;

		private bool _isDisposed;

		public UIEntrance(string name)
		{
			this._name = name;
			this._uiThread = new Thread(() =>
			{
				try
				{
					Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
					Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;

					Dispatcher.Run();
				}
				catch (Exception ex)
				{
					Debug.WriteLine(ex);

					this._executeDispatcher.InvokeShutdown();
				}
			});
			this._uiThread.Name = name;
			this._uiThread.IsBackground = true;
			this._uiThread.SetApartmentState(ApartmentState.STA);
			this._uiThread.Start();

			while ((this._executeDispatcher = Dispatcher.FromThread(this._uiThread)) == null)
			{
				Thread.Sleep(10);
			}

			this._executeDispatcher.ShutdownStarted += Dispatcher_ShutdownStarted;

			this._executeDispatcherSynchronization = new DispatcherSynchronizationContext(this._executeDispatcher);
		}

		~UIEntrance()
		{
			DoDispose(false);
		}

		void Dispatcher_ShutdownStarted(object sender, EventArgs e)
		{
			// executed dispatcher.
			var dispatcher = sender as Dispatcher;
			if (dispatcher == null)
			{
				return;
			}

			dispatcher.ShutdownStarted -= Dispatcher_ShutdownStarted;

			// 상위 Dispatcher가 등록되어 있으면 종료처리한다.
			if (this._callerDispatcher != null)
			{
				if (this._exitProcedure != null)
				{
					this._exitProcedure();
				}

				this._callerDispatcher.InvokeShutdown();
			}
		}

		public void Post(SendOrPostCallback d, object state, Action exitAction = null, bool isSetCallerDispatcher = false)
		{
			if (this._isDisposed)
			{
				throw new ObjectDisposedException(this.ToString());
			}

			if (isSetCallerDispatcher)
			{
				Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
				Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;

				this._callerDispatcher = Dispatcher.CurrentDispatcher;
			}

			this._exitProcedure = exitAction;
			try
			{
				this._executeDispatcherSynchronization.Post(d, state);
				//this._executeDispatcher.Invoke(new Action(() => d(state)));
				Debug.WriteLine("[shwlee] ###### seq : " + this._name);
			}
			catch (Exception ex)
			{
				Debug.WriteLine("[shwlee] " + ex);
			}
		}

		public void Dispose()
		{
			GC.SuppressFinalize(this);
			DoDispose(true);
		}

		private void DoDispose(bool isDisposing)
		{
			if (this._isDisposed)
				return;

			if (isDisposing)
			{
				this._executeDispatcher.InvokeShutdown();
			}

			this._isDisposed = true;
		}
	}
}
