/*
 *  TLV - Trace Log Visualizer
 *
 *  Copyright (C) 2008-2010 by Nagoya Univ., JAPAN
 *
 *  上記著作権者は，以下の(1)〜(4)の条件を満たす場合に限り，本ソフトウェ
 *  ア（本ソフトウェアを改変したものを含む．以下同じ）を使用・複製・改
 *  変・再配布（以下，利用と呼ぶ）することを無償で許諾する．
 *  (1) 本ソフトウェアをソースコードの形で利用する場合には，上記の著作
 *      権表示，この利用条件および下記の無保証規定が，そのままの形でソー
 *      スコード中に含まれていること．
 *  (2) 本ソフトウェアを，ライブラリ形式など，他のソフトウェア開発に使
 *      用できる形で再配布する場合には，再配布に伴うドキュメント（利用
 *      者マニュアルなど）に，上記の著作権表示，この利用条件および下記
 *      の無保証規定を掲載すること．
 *  (3) 本ソフトウェアを，機器に組み込むなど，他のソフトウェア開発に使
 *      用できない形で再配布する場合には，次のいずれかの条件を満たすこ
 *      と．
 *    (a) 再配布に伴うドキュメント（利用者マニュアルなど）に，上記の著
 *        作権表示，この利用条件および下記の無保証規定を掲載すること．
 *    (b) 再配布の形態を，別に定める方法によって，TOPPERSプロジェクトに
 *        報告すること．
 *  (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
 *      害からも，上記著作権者およびTOPPERSプロジェクトを免責すること．
 *      また，本ソフトウェアのユーザまたはエンドユーザからのいかなる理
 *      由に基づく請求からも，上記著作権者およびTOPPERSプロジェクトを
 *      免責すること．
 *
 *  本ソフトウェアは，無保証で提供されているものである．上記著作権者お
 *  よびTOPPERSプロジェクトは，本ソフトウェアに関して，特定の使用目的
 *  に対する適合性も含めて，いかなる保証も行わない．また，本ソフトウェ
 *  アの利用により直接的または間接的に生じたいかなる損害に関しても，そ
 *  の責任を負わない．
 *
 *  @(#) $Id$
 */
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Diagnostics;
using NU.OJL.MPRTOS.TLV.Base;
using WeifenLuo.WinFormsUI.Docking;
using System.IO;

namespace NU.OJL.MPRTOS.TLV.Third
{
	/// <summary>
	/// DockPanel Suiteのドッキングウィンドウを
	/// IWindowManagerで使うためのハンドラ
	/// WeifenLuo.WinFormsUI.Docking.dllを必要とする
	/// </summary>
	public class WeifenLuoWindowManager : IWindowManager
	{
		private readonly string settingFileName = "windowManager.setting";
		private SubWindowCollection _subWindows = new SubWindowCollection();
		private Form _mainPanel = null;
		private Control _parent;
		private DockPanel _dockPanel = null;

		public WeifenLuoWindowManager()
		{
			_dockPanel = new DockPanel();
			_dockPanel.DocumentStyle = DocumentStyle.DockingMdi;
			_dockPanel.Dock = DockStyle.Fill;
			_dockPanel.ActiveDocumentChanged += DockPanel_ActiveDocumentChanged;
		}

		public WeifenLuoWindowManager(DockPanel dockPanel)
		{
			_dockPanel = dockPanel;
			_dockPanel.ActiveDocumentChanged += DockPanel_ActiveDocumentChanged;
			_parent = dockPanel.Parent;
		}

		/// <summary>
		/// <c>ISubWindow</c>を追加したときに発生するイベント
		/// </summary>
		public event EventHandler<GeneralEventArgs<ISubWindow>> SubWindowAdded = null;
		/// <summary>
		/// 管理下のISubWindowのDockStateが変ったときに発生するイベント
		/// </summary>
		public event EventHandler<GeneralChangedEventArgs<NU.OJL.MPRTOS.TLV.Base.DockState>> SubWindowDockStateChanged = null;
		/// <summary>
		/// 管理下のISubWindowのVisibleが変ったときに発生するイベント
		/// </summary>
		public event EventHandler<GeneralChangedEventArgs<bool>> SubWindowVisibleChanged = null;
		/// <summary>
		/// 有効なドキュメントが変ったときに発生するイベント
		/// </summary>
		public event EventHandler<GeneralEventArgs<Control>> ActiveDocumentChanged = null;

		/// <summary>
		/// この<c>WindowManager</c>を格納する<c>Control</c>
		/// </summary>
		public Control Parent
		{
			get { return _parent; }
			set
			{
				_parent = value;
				Parent.Controls.Add(_dockPanel);
			}
		}

		/// <summary>
		/// <c>MainPanel</c>にFillされる<c>Contorl</c>
		/// </summary>
		public Form MainPanel
		{
			get { return _mainPanel; }
			set
			{
				_mainPanel = value;
				_mainPanel.Dock = DockStyle.Fill;
				ISubWindow sw = GetSubWindow(value);
				if (sw == null)
				{
					AddSubWindow(new SubWindow(value, NU.OJL.MPRTOS.TLV.Base.DockState.Document));
				}
			}
		}

		/// <summary>
		/// この<c>WindowManager</c>で管理しているISubWindowを返すイテレータ
		/// </summary>
		public IEnumerable<ISubWindow> SubWindows
		{
			get
			{
				foreach (ISubWindow sw in _subWindows)
				{
					yield return sw;
				}
			}
		}

		public double DockLeftPortion
		{
			get { return _dockPanel.DockLeftPortion; }
			set { _dockPanel.DockLeftPortion = value; }
		}

		public double DockTopPortion
		{
			get { return _dockPanel.DockTopPortion; }
			set { _dockPanel.DockTopPortion = value; }
		}

		public double DockRightPortion
		{
			get { return _dockPanel.DockRightPortion; }
			set { _dockPanel.DockRightPortion = value; }
		}

		public double DockBottomPortion
		{
			get { return _dockPanel.DockBottomPortion; }
			set { _dockPanel.DockBottomPortion = value; }
		}

		/// <summary>
		/// 指定した<c>ISubWindow</c>を追加します
		/// </summary>
		/// <remarks><paramref name="subWindows"/>パラメータで追加する<c>ISubWindow</c>を指定する。<paramref name="subWindows"/>パラメータは可変長引数である。</remarks>
		/// <param name="subWindows">追加する<c>ISubWindow</c></param>
		public void AddSubWindow(params ISubWindow[] subWindows)
		{
			foreach (ISubWindow sw in subWindows)
			{
				Form form = sw.Form;
				DockContentHandler dch = (DockContentHandler)sw.Handler;
				dch.DockPanel = _dockPanel;
				//dch.DockAreas = DockAreas.DockBottom | DockAreas.DockLeft | DockAreas.DockRight | DockAreas.DockTop | DockAreas.Float;
				if (sw.DockState == NU.OJL.MPRTOS.TLV.Base.DockState.Document)
				{
					form.MdiParent = (Form)_parent;
					if (_mainPanel == null)
						_mainPanel = sw.Form;
				}
				WeifenLuo.WinFormsUI.Docking.DockState dockState = DockStateExtensions.Specialize(sw.DockState);
				foreach (DockPane pane in _dockPanel.Panes)
				{
					if (pane.DockState == dockState)
					{
						dch.DockTo(pane, DockStyle.Fill, pane.Contents.Count);
						break;
					}
				}
				dch.DockState = dockState;
				dch.HideOnClose = true;
				dch.DockStateChanged += DockContent_DockStateChanged;
			}

			foreach (ISubWindow sw in subWindows)
			{
				_subWindows.Add(sw);

				sw.DockStateChanged += OnSubWindowDockStateChanged;
				sw.VisibleChanged += OnSubWindowVisibleChanged;

				if (sw.Visible)
				{
					ShowSubWindow(sw.Name);
				}
				else
				{
					HideSubWindow(sw.Name);
				}

				if (SubWindowAdded != null)
					SubWindowAdded(this, new GeneralEventArgs<ISubWindow>(sw));
			}
		}

		void DockContent_DockStateChanged(object sender, EventArgs e)
		{
			DockContentHandler dch = (DockContentHandler)sender;
			Form form = dch.Form;
			ISubWindow subwin = GetSubWindow(form.Name);
			if (dch.DockState == WeifenLuo.WinFormsUI.Docking.DockState.Hidden)
			{
				subwin.Visible = false;
				if (dch.Pane != null)
					subwin.DockState = DockStateExtensions.Generalize(dch.Pane.DockState);
			}
			else
			{
				subwin.Visible = true;
				subwin.DockState = DockStateExtensions.Generalize(dch.DockState);
			}
		}

		public void RemoveSubWindow(ISubWindow sw)
		{
			DockContentHandler dc = (DockContentHandler)sw.Handler;

			sw.DockStateChanged -= OnSubWindowDockStateChanged;
			sw.VisibleChanged -= OnSubWindowVisibleChanged;

			dc.DockStateChanged -= DockContent_DockStateChanged;
			dc.Close();

			_subWindows.Remove(sw.Name);
		}

		/// <summary>
		/// 管理下にある<c>ISubWindow</c>のすべてを表示します
		/// </summary>
		public void ShowAllSubWindows()
		{
			foreach (ISubWindow sw in _subWindows)
			{
				ShowSubWindow(sw.Name);
			}
		}

		/// <summary>
		/// 管理下にある<c>ISubWindow</c>のすべてを非表示にします
		/// </summary>
		public void AutoHideAllSubWindows()
		{
			foreach (ISubWindow sw in _subWindows)
			{
				AutoHideSubWindow(sw.Name);
			}
		}

		/// <summary>
		/// 管理下にある<c>ISubWindow</c>のすべてをオートハイド状態にします
		/// </summary>
		public void HideAllSubWindows()
		{
			foreach (ISubWindow sw in _subWindows)
			{
				HideSubWindow(sw.Name);
			}
		}

		/// <summary>
		/// <paramref name="name"/>で指定した<c>Name</c>プロパティをもつ<c>ISubWindow</c>を表示します
		/// </summary>
		/// <param name="name">表示する<c>ISubWindow</c>の<c>Name</c>プロパティの値</param>
		public void ShowSubWindow(string name)
		{
			ISubWindow sw = _subWindows[name];
			sw.Visible = true;

			DockContentHandler dc = (DockContentHandler)sw.Handler;
			dc.Show();
		}

		/// <summary>
		/// <paramref name="name"/>で指定した<c>Name</c>プロパティをもつ<c>ISubWindow</c>を非表示にします
		/// </summary>
		/// <param name="name">非表示にする<c>ISubWindow</c>の<c>Name</c>プロパティの値</param>
		public void HideSubWindow(string name)
		{
			ISubWindow sw = _subWindows[name];
			sw.Visible = false;

			DockContentHandler dc = (DockContentHandler)sw.Handler;
			dc.DockState = WeifenLuo.WinFormsUI.Docking.DockState.Hidden;
		}

		/// <summary>
		/// <paramref name="name"/>で指定した<c>Name</c>プロパティをもつ<c>ISubWindow</c>をオートハイド状態にします
		/// </summary>
		/// <param name="name">オートハイド状態にする<c>ISubWindow</c>の<c>Name</c>プロパティの値</param>
		public void AutoHideSubWindow(string name)
		{
			ISubWindow sw = _subWindows[name];

			switch (sw.DockState)
			{
				case NU.OJL.MPRTOS.TLV.Base.DockState.DockBottom:
					sw.DockState = NU.OJL.MPRTOS.TLV.Base.DockState.DockBottomAutoHide;
					break;
				case NU.OJL.MPRTOS.TLV.Base.DockState.DockLeft:
					sw.DockState = NU.OJL.MPRTOS.TLV.Base.DockState.DockLeftAutoHide;
					break;
				case NU.OJL.MPRTOS.TLV.Base.DockState.DockRight:
					sw.DockState = NU.OJL.MPRTOS.TLV.Base.DockState.DockRightAutoHide;
					break;
				case NU.OJL.MPRTOS.TLV.Base.DockState.DockTop:
					sw.DockState = NU.OJL.MPRTOS.TLV.Base.DockState.DockTopAutoHide;
					break;
				default:
					break;
			}

			DockContentHandler dc = (DockContentHandler)sw.Handler;

			switch (dc.DockState)
			{
				case WeifenLuo.WinFormsUI.Docking.DockState.DockBottom:
					dc.DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockBottomAutoHide;
					break;
				case WeifenLuo.WinFormsUI.Docking.DockState.DockLeft:
					dc.DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockLeftAutoHide;
					break;
				case WeifenLuo.WinFormsUI.Docking.DockState.DockRight:
					dc.DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockRightAutoHide;
					break;
				case WeifenLuo.WinFormsUI.Docking.DockState.DockTop:
					dc.DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockTopAutoHide;
					break;
				default:
					break;
			}
		}

		/// <summary>
		/// 管理している<c>ISubWindow</c>の数
		/// </summary>
		public int SubWindowCount
		{
			get { return _subWindows.Count; }
		}

		public ISubWindow CreateSubWindow(Form form)
		{
			return new SubWindow(form, NU.OJL.MPRTOS.TLV.Base.DockState.Document);
		}

		/// <summary>
		/// <paramref name="name"/>で指定した<c>Name</c>プロパティをもつ<c>ISubWindow</c>を取得する
		/// </summary>
		/// <param name="name">取得したい<c>ISubWindow</c>の<c>Name</c>プロパティの値</param>
		/// <returns><paramref name="name"/>で指定した値の<c>Name</c>プロパティをもつ<c>ISubWindow</c></returns>
		public ISubWindow GetSubWindow(string name)
		{
			return _subWindows[name];
		}

		public ISubWindow GetSubWindow(Form form)
		{
			foreach (ISubWindow sw in _subWindows)
			{
				if (sw.Form == form)
					return sw;
			}
			return null;
		}

		/// <summary>
		/// <paramref name="name"/>で指定した<c>Name</c>プロパティをもつ<c>ISubWindow</c>が管理下にあるかどうか
		/// </summary>
		/// <param name="name">管理下にあるかどうか調べたい<c>ISubWindow</c>の<c>Name</c>プロパティ</param>
		/// <returns>管理下にある場合true</returns>
		/// <returns>管理下にない場合false</returns>
		public bool ContainSubWindow(string name)
		{
			return _subWindows.Contains(name);
		}

		/// <summary>
		/// 管理する<c>ISubWindow</c>のどれかの<c>DockState</c>プロパティの値が変化したときに呼び出される
		/// </summary>
		/// <param name="sender"><c>DockState</c>が変化した<c>ISubWindow</c></param>
		/// <param name="e"><c>EventArgs.Empty</c></param>
		protected void OnSubWindowDockStateChanged(object o, GeneralChangedEventArgs<NU.OJL.MPRTOS.TLV.Base.DockState> e)
		{
			if (SubWindowDockStateChanged != null)
				SubWindowDockStateChanged(o, e);

			ISubWindow sw = (ISubWindow)o;
			DockContentHandler dc = (DockContentHandler)sw.Handler;
			dc.DockState = DockStateExtensions.Specialize(sw.DockState);
		}

		/// <summary>
		/// 管理する<c>ISubWindow</c>のどれかの<c>Visible</c>プロパティの値が変化したときに呼び出される
		/// </summary>
		/// <param name="sender"><c>Visible</c>が変化した<c>ISubWindow</c></param>
		/// <param name="e"><c>EventArgs.Empty</c></param>
		protected void OnSubWindowVisibleChanged(object o, GeneralChangedEventArgs<bool> e)
		{
			if (SubWindowVisibleChanged != null)
				SubWindowVisibleChanged(o, e);

			ISubWindow sw = (ISubWindow)o;
			if (sw.Visible)
				ShowSubWindow(sw.Name);
			else
				HideSubWindow(sw.Name);
		}

		class DockPaneAdapter : IDockPane
		{
			DockPane m_DockPane;

			public DockPaneAdapter(DockPane dockPane)
			{
				m_DockPane = dockPane;
			}

			public DockPane DockPane
			{
				get { return m_DockPane; }
			}

			#region IDockPane メンバ

			public IEnumerable<Control> Contents
			{
				get
				{
					foreach (IDockContent dc in m_DockPane.Contents)
						yield return dc.DockHandler.Form;
				}
			}

			public int ContentCount
			{
				get { return m_DockPane.Contents.Count; }
			}

			#endregion
		}

		Dictionary<ISubWindow, DockPaneAdapter> m_DockPanes = new Dictionary<ISubWindow, DockPaneAdapter>();

		public void DockTo(ISubWindow subWindow, IDockPane dockPane, DockStyle dockStyle, int num)
		{
			DockContentHandler dc = (DockContentHandler)subWindow.Handler;
			dc.Pane.DockTo(((DockPaneAdapter)dockPane).DockPane, dockStyle, num);
		}

		public IDockPane GetPane(ISubWindow subWindow)
		{
			DockPaneAdapter pane;

			if (!m_DockPanes.TryGetValue(subWindow, out pane))
			{
				DockContentHandler dc = (DockContentHandler)subWindow.Handler;
				pane = new DockPaneAdapter(dc.Pane);
			}

			return pane;
		}

		public void Save()
		{
			string path = Path.Combine(Application.LocalUserAppDataPath, settingFileName);

			_dockPanel.SaveAsXml(path);
		}

		public void Load()
		{
			string path = Path.Combine(Application.LocalUserAppDataPath, settingFileName);

			if (File.Exists(path) && _subWindows.Count != 0)
			{
				Parent.Controls.Remove(_dockPanel);

				DockPanel _settingDockPanel = new DockPanel();
				Parent.Controls.Add(_settingDockPanel);
				_settingDockPanel.DocumentStyle = DocumentStyle.DockingMdi;
				_settingDockPanel.Dock = DockStyle.Fill;
				_settingDockPanel.LoadFromXml(path, delegate(string s)
					{
						foreach (ISubWindow sw in _subWindows)
						{
							DockContentHandler dc = (DockContentHandler)sw.Handler;
							if (s == sw.Name)
							{
								dc.DockPanel = _settingDockPanel;
								return dc.Content;
							}
						}
						return null;
					});
				_settingDockPanel.ActiveDocumentChanged += DockPanel_ActiveDocumentChanged;
				_dockPanel.ActiveDocumentChanged -= DockPanel_ActiveDocumentChanged;
				_dockPanel = _settingDockPanel;
				foreach (ISubWindow sw in _subWindows)
				{
					DockContentHandler dc = (DockContentHandler)sw.Handler;
					if (dc.DockState == WeifenLuo.WinFormsUI.Docking.DockState.Hidden)
					{
						sw.DockState = DockStateExtensions.Generalize(dc.Pane.DockState);
						sw.Visible = false;
					}
				}
			}
		}

		void DockPanel_ActiveDocumentChanged(object sender, EventArgs e)
		{
			if (_dockPanel.ActiveDocument == null)
				return;

			_mainPanel = _dockPanel.ActiveDocument.DockHandler.Form;
			if (ActiveDocumentChanged != null)
				ActiveDocumentChanged(this, new GeneralEventArgs<Control>(_mainPanel));
		}

		public void Show()
		{
			_dockPanel.BringToFront();
		}

		public static ISubWindow CreateSubWindow(Form form, NU.OJL.MPRTOS.TLV.Base.DockState dockState)
		{
			return new SubWindow(form, dockState);
		}
	}

	static class DockStateExtensions
	{
		public static WeifenLuo.WinFormsUI.Docking.DockState Specialize(NU.OJL.MPRTOS.TLV.Base.DockState dockState)
		{
			switch (dockState)
			{
				case NU.OJL.MPRTOS.TLV.Base.DockState.DockBottom:
					return WeifenLuo.WinFormsUI.Docking.DockState.DockBottom;
				case NU.OJL.MPRTOS.TLV.Base.DockState.DockBottomAutoHide:
					return WeifenLuo.WinFormsUI.Docking.DockState.DockBottomAutoHide;
				case NU.OJL.MPRTOS.TLV.Base.DockState.DockLeft:
					return WeifenLuo.WinFormsUI.Docking.DockState.DockLeft;
				case NU.OJL.MPRTOS.TLV.Base.DockState.DockLeftAutoHide:
					return WeifenLuo.WinFormsUI.Docking.DockState.DockLeftAutoHide;
				case NU.OJL.MPRTOS.TLV.Base.DockState.DockRight:
					return WeifenLuo.WinFormsUI.Docking.DockState.DockRight;
				case NU.OJL.MPRTOS.TLV.Base.DockState.DockRightAutoHide:
					return WeifenLuo.WinFormsUI.Docking.DockState.DockRightAutoHide;
				case NU.OJL.MPRTOS.TLV.Base.DockState.DockTop:
					return WeifenLuo.WinFormsUI.Docking.DockState.DockTop;
				case NU.OJL.MPRTOS.TLV.Base.DockState.DockTopAutoHide:
					return WeifenLuo.WinFormsUI.Docking.DockState.DockTopAutoHide;
				case NU.OJL.MPRTOS.TLV.Base.DockState.Float:
					return WeifenLuo.WinFormsUI.Docking.DockState.Float;
				case NU.OJL.MPRTOS.TLV.Base.DockState.Document:
					return WeifenLuo.WinFormsUI.Docking.DockState.Document;
				default:
					return WeifenLuo.WinFormsUI.Docking.DockState.Unknown;
			}
		}

		public static NU.OJL.MPRTOS.TLV.Base.DockState Generalize(WeifenLuo.WinFormsUI.Docking.DockState dockState)
		{
			switch (dockState)
			{
				case WeifenLuo.WinFormsUI.Docking.DockState.DockBottom:
					return NU.OJL.MPRTOS.TLV.Base.DockState.DockBottom;
				case WeifenLuo.WinFormsUI.Docking.DockState.DockBottomAutoHide:
					return NU.OJL.MPRTOS.TLV.Base.DockState.DockBottomAutoHide;
				case WeifenLuo.WinFormsUI.Docking.DockState.DockLeft:
					return NU.OJL.MPRTOS.TLV.Base.DockState.DockLeft;
				case WeifenLuo.WinFormsUI.Docking.DockState.DockLeftAutoHide:
					return NU.OJL.MPRTOS.TLV.Base.DockState.DockLeftAutoHide;
				case WeifenLuo.WinFormsUI.Docking.DockState.DockRight:
					return NU.OJL.MPRTOS.TLV.Base.DockState.DockRight;
				case WeifenLuo.WinFormsUI.Docking.DockState.DockRightAutoHide:
					return NU.OJL.MPRTOS.TLV.Base.DockState.DockRightAutoHide;
				case WeifenLuo.WinFormsUI.Docking.DockState.DockTop:
					return NU.OJL.MPRTOS.TLV.Base.DockState.DockTop;
				case WeifenLuo.WinFormsUI.Docking.DockState.DockTopAutoHide:
					return NU.OJL.MPRTOS.TLV.Base.DockState.DockTopAutoHide;
				case WeifenLuo.WinFormsUI.Docking.DockState.Float:
					return NU.OJL.MPRTOS.TLV.Base.DockState.Float;
				case WeifenLuo.WinFormsUI.Docking.DockState.Document:
					return NU.OJL.MPRTOS.TLV.Base.DockState.Document;
				default:
					return NU.OJL.MPRTOS.TLV.Base.DockState.Unknown;
			}
		}
	}

}
