/*
	$Id: MainForm.cs 62 2010-02-01 18:06:46Z catwalk $
*/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Xml.Linq;
using System.Reflection;
using Hiyoko.Net;
using Hiyoko.Net.Twitter;
using Hiyoko.Themes;
using Hiyoko.Utilities;
using Microsoft.WindowsAPICodePack.Shell;
using Microsoft.WindowsAPICodePack.Taskbar;

namespace Hiyoko.Forms{
	using WinForms = System.Windows.Forms;
	using Gdi = System.Drawing;
	using IO = System.IO;
	
	public partial class MainForm : GlassWindow{
		private Gdi.Icon emptyIcon;
		private Gdi.Icon letterIcon;
		private ObservableCollection<OutputItem> outputs;
		private WinForms.ContextMenu notifyIconContextMenu = new WinForms.ContextMenu();
		private WinForms.NotifyIcon notifyIcon = new WinForms.NotifyIcon();
		private WindowSettings windowSettings = new WindowSettings("MainForm");
		private WindowState restoreState;
		private OutputPanelTraceListener traceListener;
		private HotKeyManager hotKeyManager;
		private List<Status> newTimelineStatusesNotShown = new List<Status>();
		private TimelineAutoRefresher timelineAutoRefresher;
		private HotKey ShowWindowHotKey;
		
		public MainForm(){
			this.InitializeComponent();
			this.IsResident = Program.Settings.IsResident;
			Program.ThemeManager.Attach(this.Resources);
			
			// ホットキー
			this.hotKeyManager = new HotKeyManager(this);
			this.RefreshHotKey();
			this.timelineAutoRefresher = new TimelineAutoRefresher(this);
			
			this.outputs = new ObservableCollection<OutputItem>();
			this.outputs.CollectionChanged += this.Outputs_CollectionChanged;
			this.outputList.DataContext = this.outputs;
			
			// 通知アイコン
			System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
			this.letterIcon = new Gdi.Icon(asm.GetManifestResourceStream("Resources.Letter.ico"));
			this.emptyIcon = new Gdi.Icon(asm.GetManifestResourceStream("Resources.Empty.ico"));
			this.notifyIcon.Icon = this.emptyIcon;
			this.notifyIcon.MouseClick += this.NotifyIcon_MouseClick;
			this.notifyIcon.Text = "Hiyoko - A Twitter Client";
			
			// 通知アイコンのコンテキストメニュー。
			this.notifyIconContextMenu.MenuItems.Add(new WinForms.MenuItem("表示(&V)", delegate{
				this.ShowWindow();
			}));
			this.notifyIconContextMenu.MenuItems.Add(new WinForms.MenuItem("-"));
			this.notifyIconContextMenu.MenuItems.Add(new WinForms.MenuItem("設定(&S)...", delegate{
				SettingsForm settings = new SettingsForm();
				settings.Owner = this;
				settings.ShowDialog();
			}));
			this.notifyIconContextMenu.MenuItems.Add(new WinForms.MenuItem("-"));
			this.notifyIconContextMenu.MenuItems.Add(new WinForms.MenuItem("終了(&V)", delegate{
				Application.Current.Shutdown();
			}));
			this.notifyIcon.ContextMenu = this.notifyIconContextMenu;
			this.notifyIcon.Visible = Program.Settings.IsResident;
			
			// ウインドウ復元
			this.windowSettings.UpgradeOnce();
			this.Left = this.windowSettings.RestoreBounds.Left;
			this.Top = this.windowSettings.RestoreBounds.Top;
			this.Width = this.windowSettings.RestoreBounds.Width;
			this.Height = this.windowSettings.RestoreBounds.Height;
			this.WindowState = this.restoreState = this.windowSettings.WindowState;
			this.outputListRow.Height = Program.Settings.OutputListRowHeight;
			
			// イベント
			Program.Settings.PropertyChanged += this.ApplicationSettings_Changed;
			((ObjectDataProvider)this.Resources["networkJobServerProvider"]).ObjectInstance = Program.NetworkJobServer;
			Program.NetworkJobServer.JobCompleted += delegate{
				if(TaskbarManager.IsPlatformSupported){
					TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.NoProgress, this);
				}
				((ObjectDataProvider)this.Resources["networkJobServerProvider"]).Refresh();
			};
			Program.NetworkJobServer.JobRunning += delegate{
				if(TaskbarManager.IsPlatformSupported){
					TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.Indeterminate, this);
				}
				((ObjectDataProvider)this.Resources["networkJobServerProvider"]).Refresh();
			};
			Program.NetworkJobServer.JobEnqueued += delegate{
				((ObjectDataProvider)this.Resources["networkJobServerProvider"]).Refresh();
			};
			this.timelinePanel.TimelineRefreshed += TimelinePanel_TimelineRefreshed;
			this.Loaded += this.LoadedListener;
			this.AeroGlassCompositionChanged += new AeroGlassCompositionChangedEvent(this.AeroGlassCompositionChangedListener);
			
			// TraceListener
			this.traceListener = new OutputPanelTraceListener(this);
			Debug.Listeners.Add(this.traceListener);
			
			// 初期処理
			this.Account = new Account(Program.Settings.AccessToken);
			this.VerifyAccount();
			
			this.TimelinePanel.StatusEdit.Focus();
		}
		
		#region 関数
		
		public void RefreshTitle(){
			if(this.newTimelineStatusesNotShown.Count > 0){
				this.Title = String.Format("Hiyoko [{0}] - {1} / {2}", this.newTimelineStatusesNotShown.Count, this.Account.User.Name, this.Account.User.ScreenName);
			}else{
				this.Title = String.Format("Hiyoko - {0} / {1}", this.Account.User.Name, this.Account.User.ScreenName);
			}
			if(Program.Settings.IsUseProfileImageAsMainFormIcon && (this.Account.IsVerified)){
				try{
					this.Icon = BitmapFrame.Create(new Uri(this.Account.User.ProfileImageUrl));
				}catch{
				}
			}else{
				this.Icon = null;
			}
		}
		
		private void VerifyAccount(){
			Account account = this.Account;
			ThreadPool.QueueUserWorkItem(new WaitCallback(delegate{
				this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate{
					this.outputs.Add(new MessageOutputItem("アカウントの認証を確認中..."));
				}));
				try{
					account.VerifyCredential();
					this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate{
						this.outputs.Add(new SuccessOutputItem("アカウントの認証に成功しました。"));
						this.accountStatuses.DataContext = account;
						this.RefreshTitle();
						this.TimelinePanel.RefreshAutoCompleteDictionary();
					}));
				}catch(WebException){
					this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate{
						this.outputs.Add(new ErrorOutputItem("アカウントの認証に失敗しました。"));
						this.RefreshTitle();
					}));
				}
			}));
		}
		
		private void RefreshHotKey(){
			if(this.ShowWindowHotKey != null){
				this.hotKeyManager.HotKeys.Remove(this.ShowWindowHotKey);
			}
			if(Program.Settings.ShowWindowHotKey != Key.None){
				this.ShowWindowHotKey = new HotKey(Program.Settings.ShowWindowHotKey, Program.Settings.ShowWindowHotKeyModifiers, delegate{
					this.ShowWindow();
				});
				this.hotKeyManager.HotKeys.Add(this.ShowWindowHotKey);
			}
		}
		
		#endregion
		
		#region イベント
		
		protected override void OnInitialized(EventArgs e){
			base.OnInitialized(e);
			this.restoreState = this.WindowState;
		}
		
		private void LoadedListener(object sender, RoutedEventArgs e){
			this.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(delegate{
				if((Program.ThemeManager.CurrentTheme != null) && (Program.ThemeManager.CurrentTheme.IsGlassWindow)){
					if(this.AeroGlassCompositionEnabled){
						this.SetAeroGlassTransparency();
					}
				}
			}));
		}
		
		private void AeroGlassCompositionChangedListener(object sender, AeroGlassCompositionChangedEvenArgs e){
			if(e.GlassAvailable){
				if(this.Resources.Contains("EnableGlassWindow")){
					this.SetAeroGlassTransparency();
					this.InvalidateVisual();
				}
			}
		}
		
		protected override void OnClosing(CancelEventArgs e){
			this.windowSettings.RestoreBounds = this.RestoreBounds;
			if(this.WindowState != WindowState.Minimized){
				this.windowSettings.WindowState = this.WindowState;
			}
			Program.Settings.OutputListRowHeight = this.outputListRow.Height;
			Program.Settings.TimelineStatusEditRowHeight = this.timelinePanel.StatusEditRowHeight;
			Program.Settings.LatestTimelineStatusDateTime = this.timelinePanel.LatestTimelineStatusDateTime;
			
			if(this.IsResident){
				e.Cancel = true;
				this.Hide();
			}
			base.OnClosing(e);
		}
		
		protected override void OnClosed(EventArgs e){
			base.OnClosed(e);
			this.windowSettings.Save();
			this.notifyIcon.Visible = false;
			this.notifyIcon.Dispose();
			this.letterIcon.Dispose();
			this.emptyIcon.Dispose();
			Program.Settings.PropertyChanged -= this.ApplicationSettings_Changed;
			Debug.Listeners.Remove(this.traceListener);
		}
		
		protected override void OnActivated(EventArgs e){
			this.notifyIcon.Icon = this.emptyIcon;
			this.newTimelineStatusesNotShown.Clear();
			this.notifyIcon.Text = "Hiyoko - A Twitter Client";
			this.RefreshTitle();
			base.OnActivated(e);
			this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate{
				CommandManager.InvalidateRequerySuggested();
			}));
		}
		
		protected override void OnStateChanged(EventArgs e){
			if(this.WindowState != WindowState.Minimized){
				this.restoreState = this.WindowState;
			}
			base.OnStateChanged(e);
			this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate{
				CommandManager.InvalidateRequerySuggested();
			}));
		}
		
		private void NotifyIcon_MouseClick(object sender, WinForms.MouseEventArgs e){
			if((e.Button & WinForms.MouseButtons.Left) > 0){
				this.ShowWindow();
			}
		}
		
		public void ShowWindow(){
			this.Show();
			this.Activate();
			this.WindowState = this.restoreState;
		}
		
		private void ApplicationSettings_Changed(object sender, PropertyChangedEventArgs e){
			ApplicationSettings settings = (ApplicationSettings)sender;
			switch(e.PropertyName){
				case "IsResident":{
					this.IsResident = settings.IsResident;
					break;
				}
				case "IsAutoRefreshTimeline":{
					this.timelineAutoRefresher.IsEnabled = settings.IsAutoRefreshTimeline;
					break;
				}
				case "AccessToken":{
					this.Account.AccessToken = settings.AccessToken;
					this.VerifyAccount();
					break;
				}
				case "ShowWindowHotKey":
				case "ShowWindowHotKeyModifiers":{
					this.RefreshHotKey();
					break;
				}
				case "IsUseProfileImageAsMainFormIcon":{
					this.RefreshTitle();
					break;
				}
				case "LastCheckedUpdatesDate":{
					if(Program.AvailableUpdates.Count > 0){
						var latest = Program.AvailableUpdates.OrderByDescending(p => p.Version).First();
						var state = (latest.State == PackageState.Stable) ? "正式版" :
						            (latest.State == PackageState.Beta)   ? "ベータ版" :
						            (latest.State == PackageState.Alpha)  ? "アルファ版" :
						            (latest.State == PackageState.ReleaseCandidate) ? "リリース候補" :
						            "その他";
						this.availableUpdatesMessageBlock.Text = String.Format("最新バージョン: {0} {1}が利用可能です。", latest.Version, state);
						this.availableUpdatesPanel.Visibility = Visibility.Visible;
					}
					break;
				}
			}
		}
		
		private void Outputs_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e){
			this.outputList.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(delegate{
				if(this.outputs.Count > Program.Settings.OutputItemLimitCount){
					this.outputs.CollectionChanged -= this.Outputs_CollectionChanged;
					for(int i = Program.Settings.OutputItemLimitCount; i <= this.outputs.Count; i++){
						this.outputs.RemoveAt(0);
					}
					this.outputs.CollectionChanged += this.Outputs_CollectionChanged;
				}
				if(this.outputList.HasItems){
					this.outputList.ScrollIntoView(this.outputList.Items[this.outputList.Items.Count - 1]);
				}
			}));
		}
		
		private void TimelinePanel_TimelineRefreshed(object sender, TimelineRefreshedEventArgs e){
			// バルーンチップ
			if((e.NewStatuses.Length > 0) && this.notifyIcon.Visible && !this.IsActive){
				StringBuilder sb = new StringBuilder();
				foreach(Status status in e.NewStatuses){
					sb.AppendFormat("{0} / {1}\n", status.User.Name, status.User.ScreenName);
					sb.AppendFormat("{0}\n", status.Text);
				}
				this.newTimelineStatusesNotShown.AddRange(e.NewStatuses);
				this.notifyIcon.ShowBalloonTip(
					10000,
					String.Format("新着ステータス {0}件 / 未読 {1}件", e.NewStatuses.Length, this.newTimelineStatusesNotShown.Count),
					sb.ToString(),
					WinForms.ToolTipIcon.None);
				this.notifyIcon.Icon = this.letterIcon;
				this.notifyIcon.Text = String.Format("Hiyoko - A Twitter Client\n新着{0}件", this.newTimelineStatusesNotShown.Count);
				this.RefreshTitle();
			}
			
			// アウトプットメッセージ
			if(e.NewStatuses.Length > 0){
				Program.MainForm.Outputs.Add(new SuccessOutputItem(String.Format("タイムライン取得成功 新着{0}件", e.NewStatuses.Length)));
			}else{
				Program.MainForm.Outputs.Add(new SuccessOutputItem("タイムライン取得成功"));
			}
		}
		
		/*
		private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e){
			ListBoxItem item = (ListBoxItem)sender;
			ContentControl content = (COnitem.Content;
			ModifierKeys mods = Keyboard.Modifiers;
			MouseAction action = (e.ChangedButton == MouseButton.Left) ? MouseAction.LeftDoubleClick :
			                     (e.ChangedButton == MouseButton.Right) ? MouseAction.RightDoubleClick :
			                     (e.ChangedButton == MouseButton.Middle) ? MouseAction.MiddleDoubleClick :
			                     MouseAction.None;
			MessageBox.Show(action.ToString());
			if(action != MouseAction.None){
				foreach(MouseBinding binding in ((ContentElement)sender).InputBindings.Cast<MouseBinding>()
				                                                    .Where(bind => (((MouseGesture)bind.Gesture).MouseAction == action))
				                                                    .Where(bind => (((MouseGesture)bind.Gesture).Modifiers == mods))){
					ICommand command = binding.Command;
					RoutedCommand routedCommand = command as RoutedCommand;
					if((routedCommand != null) && (routedCommand.CanExecute(binding.CommandParameter, binding.CommandTarget))){
						routedCommand.Execute(binding.CommandParameter, binding.CommandTarget);
					}else if(command.CanExecute(binding.CommandParameter)){
						command.Execute(binding.CommandParameter);
					}
				}
			}
		}
		*/
		
		#endregion
		
		#region コマンド
		
		private void About_CanExecute(object target, CanExecuteRoutedEventArgs e){
			e.CanExecute = true;
		}
		
		private void About_Executed(object target, ExecutedRoutedEventArgs e){
			AboutForm about = new AboutForm();
			about.Owner = this;
			about.ShowDialog();
		}
		
		private void AbortCommunication_CanExecute(object target, CanExecuteRoutedEventArgs e){
			e.CanExecute = Program.NetworkJobServer.IsBusy;
		}
		
		private void AbortCommunication_Executed(object target, ExecutedRoutedEventArgs e){
			Program.NetworkJobServer.AbortJob();
		}
		
		private void Settings_CanExecute(object target, CanExecuteRoutedEventArgs e){
			e.CanExecute = true;
		}
		
		private void Settings_Executed(object target, ExecutedRoutedEventArgs e){
			if(this.ShowWindowHotKey != null){
				this.hotKeyManager.HotKeys.Remove(this.ShowWindowHotKey);
			}
			this.ShowWindowHotKey = null;
			
			//Uri oldThemeUri = Program.Settings.ThemeUri;
			SettingsForm settings = new SettingsForm();
			settings.Owner = this;
			settings.ShowDialog();
			/*
			if(oldThemeUri != Program.Settings.ThemeUri){
				Program.ReloadMainForm();
			}else{*/
				this.RefreshHotKey();
			//}
		}
		
		private void Exit_CanExecute(object target, CanExecuteRoutedEventArgs e){
			e.CanExecute = true;
		}
		
		private void Exit_Executed(object target, ExecutedRoutedEventArgs e){
			Application.Current.Shutdown();
		}
		
		private void Close_CanExecute(object target, CanExecuteRoutedEventArgs e){
			e.CanExecute = true;
		}
		
		private void Close_Executed(object target, ExecutedRoutedEventArgs e){
			this.Close();
		}
		/*
		private void SaveTimeline_CanExecute(object target, CanExecuteRoutedEventArgs e){
			e.CanExecute = (this.timelineXml != null);
		}
		
		private void SaveTimeline_Executed(object target, ExecutedRoutedEventArgs e){
			WinForms.SaveFileDialog dlg = new WinForms.SaveFileDialog();
			dlg.Filter = "Xmlファイル|*.xml";
			dlg.DefaultExt = "xml";
			if(dlg.ShowDialog() == WinForms.DialogResult.OK){
				this.timelineXml.Save(dlg.FileName);
			}
		}
		*/
		
		private void OpenTwitterCom_CanExecute(object target, CanExecuteRoutedEventArgs e){
			e.CanExecute = true;
		}
		
		private void OpenTwitterCom_Executed(object target, ExecutedRoutedEventArgs e){
			Process.Start("http://twitter.com");
		}
		
		private void OpenMyHome_CanExecute(object target, CanExecuteRoutedEventArgs e){
			e.CanExecute = true;
		}
		
		private void OpenMyHome_Executed(object target, ExecutedRoutedEventArgs e){
			Process.Start("http://twitter.com/home");
		}
		
		private void CurrentTabItem_CanExecute(object target, CanExecuteRoutedEventArgs e){
			if(e.Parameter == null){
				var panel = this.mainTabControl.SelectedContent as IInputElement;
				e.CanExecute = (panel != null) ? ((RoutedCommand)e.Command).CanExecute(e.Command, panel) : false;
			}
		}
		
		private void CurrentTabItem_Executed(object target, ExecutedRoutedEventArgs e){
			if(e.Parameter == null){
				var panel = this.mainTabControl.SelectedContent as IInputElement;
				if(panel != null){
					((RoutedCommand)e.Command).Execute(e.Command, panel);
				}
			}
		}
		
		private void RefreshAccountInfo_CanExecute(object target, CanExecuteRoutedEventArgs e){
			e.CanExecute = (this.Account != null);
		}
		
		private void RefreshAccountInfo_Executed(object target, ExecutedRoutedEventArgs e){
			this.VerifyAccount();
		}
		
		private void CheckUpdates_CanExecute(object target, CanExecuteRoutedEventArgs e){
			e.CanExecute = true;
		}
		
		private void CheckUpdates_Executed(object target, ExecutedRoutedEventArgs e){
			try{
				var updates = Program.GetUpdates();
				if(updates.Length > 0){
					var latest = updates.OrderByDescending(p => p.Version).First();
					
					var state = (latest.State == PackageState.Stable) ? "正式版" :
					            (latest.State == PackageState.Beta)   ? "ベータ版" :
					            (latest.State == PackageState.Alpha)  ? "アルファ版" :
					            (latest.State == PackageState.ReleaseCandidate) ? "リリース候補" :
					            "その他";
					if(MessageBox.Show(
						String.Format(
							"下記のバージョンが見つかりました。アップデートしますか？\n\n最新バージョン: {0} {1}\nリリース日時: {2}\n\n変更履歴:\n{3}",
							latest.Version,
							state,
							latest.Date.ToString("yyyy-MM-dd"),
							latest.ChangeLog),
						"アップデート",
						MessageBoxButton.YesNo) == MessageBoxResult.Yes){
						Program.Update(latest);
					}
				}else{
					MessageBox.Show("利用可能なアップデートはありませんでした。");
				}
			}catch(WebException ex){
				MessageBox.Show(ex.Message);
			}
		}
		
		private void UpdateClick(object sender, RoutedEventArgs e){
			var latest = Program.AvailableUpdates.OrderByDescending(p => p.Version).First();
			Program.Update(latest);
		}
		
		private void DontUpdateClick(object sender, RoutedEventArgs e){
			this.availableUpdatesPanel.Visibility = Visibility.Collapsed;
		}
		
		#endregion
		
		#region Debug
		
		private class OutputPanelTraceListener : TraceListener{
			private MainForm mainForm;
			
			public OutputPanelTraceListener(MainForm mainForm) : base(){
				this.mainForm = mainForm;
			}
			
			private void _Write(string message){
				if(this.NeedIndent){
					this.WriteIndent();
				}
				if(this.mainForm.Outputs.Count > 0){
					OutputItem item = this.mainForm.Outputs[this.mainForm.Outputs.Count - 1];
					item.Text += message;
					item.DateTime = DateTime.Now;
				}else{
					this.mainForm.Outputs.Add(new MessageOutputItem(message));
				}
			}
			
			private void _WriteLine(string message){
				if(this.NeedIndent){
					this.WriteIndent();
				}
				this.mainForm.Outputs.Add(new MessageOutputItem(message));
			}
			
			public override void Write(object o){
				this._Write(o.ToString());
			}
			
			public override void Write(string message){
				this._Write(message);
			}
			
			public override void Write(object o, string category){
				this._Write(category + " : " + o.ToString());
			}
			
			public override void Write(string message, string category){
				this._Write(category + " : " + message);
			}
			
			public override void WriteLine(object o){
				this._Write(o.ToString());
			}
			
			public override void WriteLine(string message){
				this._WriteLine(message);
			}
			
			public override void WriteLine(object o, string category){
				this._WriteLine(category + " : " + o.ToString());
			}
			
			public override void WriteLine(string message, string category){
				this._WriteLine(category + " : " + message);
			}
		}
		
		#endregion
		
		#region プロパティ
		
		public static readonly DependencyProperty AccountProperty = DependencyProperty.Register("Account", typeof(Account), typeof(MainForm));
		public Account Account{
			get{
				return (Account)this.GetValue(AccountProperty);
			}
			set{
				this.SetValue(AccountProperty, value);
			}
		}
		
		public TabControl MainTabControl{
			get{
				return this.mainTabControl;
			}
		}
		
		public TabItem TimelineTabItem{
			get{
				return this.timelineTabItem;
			}
		}
		
		public MainFormTimelinePanel TimelinePanel{
			get{
				return this.timelinePanel;
			}
		}
		
		public TabItem UsersTabItem{
			get{
				return this.usersTabItem;
			}
		}
		
		public MainFormUsersPanel UsersPanel{
			get{
				return this.usersPanel;
			}
		}
		
		public Collection<OutputItem> Outputs{
			get{
				return this.outputs;
			}
		}
		
		public static readonly DependencyProperty BackgroundImageBrushProperty = DependencyProperty.Register("BackgroundImageBrush", typeof(ImageBrush), typeof(MainForm));
		public ImageBrush BackgroundImageBrush{
			get{
				return (ImageBrush)this.GetValue(BackgroundImageBrushProperty);
			}
			set{
				this.SetValue(BackgroundImageBrushProperty, value);
			}
		}
		
		public HotKeyManager HotKeyManager{
			get{
				return this.hotKeyManager;
			}
		}
		
		public static readonly DependencyProperty IsResidentProperty = DependencyProperty.Register("IsResident", typeof(bool), typeof(MainForm));
		public bool IsResident{
			get{
				return (bool)this.GetValue(IsResidentProperty);
			}
			set{
				this.SetValue(IsResidentProperty, value);
				if(this.notifyIcon != null){
					this.notifyIcon.Visible = value;
				}
			}
		}
		
		#endregion
		
		#region タイムライン自動更新
		
		private class TimelineAutoRefresher : DependencyObject{
			private double autoRefreshTimelineWeight = 1;
			private DispatcherTimer autoRefreshTimelineTimer = new DispatcherTimer(DispatcherPriority.Background);
			private MainForm mainForm;
			
			public TimelineAutoRefresher(MainForm mainForm){
				if(mainForm == null){
					throw new ArgumentNullException();
				}
				this.mainForm = mainForm;
				
				// タイマー
				this.autoRefreshTimelineTimer.Tick += this.AutoRefreshTimelineTimer_Tick;
				this.autoRefreshTimelineTimer.Interval = TimeSpan.FromSeconds(60);
				this.IsEnabled = Program.Settings.IsAutoRefreshTimeline;
				this.CalculateAutoRefreshTimelineInterval();
				
				this.mainForm.TimelinePanel.TimelineRefreshed += delegate(object sender, TimelineRefreshedEventArgs e){
					// インターバル
					if(this.IsEnabled){
						if(e.NewStatuses.Length > 0){
							this.autoRefreshTimelineWeight = 1;
						}else{
							this.autoRefreshTimelineWeight *= 0.5;
						}
						this.CalculateAutoRefreshTimelineInterval();
						Program.MainForm.Outputs.Add(new MessageOutputItem(String.Format(
							"次の自動取得は {0} 分後",
							Math.Round(this.autoRefreshTimelineTimer.Interval.TotalMinutes))));
					}
				};
			}
			
			private void AutoRefreshTimelineTimer_Tick(object sender, EventArgs e){
				if(!(this.mainForm.TimelinePanel.IsRefreshingTimeline)){
					if(this.mainForm.IsActive){
						if(this.mainForm.TimelinePanel.CurrentTimelinePage == 1){
							this.mainForm.TimelinePanel.RefreshTimeline();
						}
					}else{
						this.mainForm.TimelinePanel.CurrentTimelinePage = 1;
						this.mainForm.TimelinePanel.RefreshTimeline();
					}
				}
			}
			
			/// <summery>
			/// ステータス間の投稿日時の差の加重平均からインターバルを計算する。
			/// </summery>
			private void CalculateAutoRefreshTimelineInterval(){
				TimeSpan span = TimeSpan.FromSeconds(60);
				IList<Status> timeline = this.mainForm.TimelinePanel.Timeline;
				if(timeline.Count > 1){
					double a = 1 - this.autoRefreshTimelineWeight;
					double w = 1;
					double wsum = 0;
					decimal sum = 0;
					DateTime[] times = (this.mainForm.TimelinePanel.CurrentTimelinePage == 1) ?
					                   (new DateTime[]{DateTime.Now}).Concat(timeline.Select(s => s.CreatedAt)).ToArray() :
					                   timeline.Select(s => s.CreatedAt).ToArray();
					for(int i = 1; i < times.Length; i++, w *= a){
						TimeSpan t = times[i - 1] - times[i];
						sum += (decimal)(t.TotalSeconds * w);
						wsum += w;
					}
					sum /= (decimal)wsum;
					sum /= 2;
					if(sum > 60){
						span = TimeSpan.FromSeconds((double)sum);
					}
				}
				this.autoRefreshTimelineTimer.Stop();
				this.autoRefreshTimelineTimer.Interval = span;
				if(this.IsEnabled){
					this.autoRefreshTimelineTimer.Start();
				}
			}
			
			public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.Register("IsEnabled", typeof(bool), typeof(MainForm));
			public bool IsEnabled{
				get{
					return (bool)this.GetValue(IsEnabledProperty);
				}
				set{
					this.SetValue(IsEnabledProperty, value);
					this.autoRefreshTimelineTimer.IsEnabled = value;
					this.CalculateAutoRefreshTimelineInterval();
				}
			}
		}
		
		#endregion
	}
}