/*
	$Id: Hiyoko.cs 69 2010-02-06 06:03:13Z catwalk $
*/
using System;
using System.Linq;
using System.IO;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using System.Configuration;
using System.ComponentModel;
using System.Threading;
using System.Diagnostics;
using System.Net;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using IronPython.Hosting;
using IronPython.Modules;
using Hiyoko.Forms;
using Hiyoko.Utilities;
using Hiyoko.Net;
using Hiyoko.Net.Twitter;
using Hiyoko.Themes;
using Microsoft.WindowsAPICodePack.Taskbar;

namespace Hiyoko{
	using IO = System.IO;
	
	public partial class Program : Application{
		private PythonEngine pythonEngine = null;
		private ApplicationSettings settings;
		private CommandLine commandLine;
		private NetworkJobServer networkJobServer;
		private string executablePath;
		private UrlShorter urlShorter;
		private ThemeManager themeManager;
		private MainForm mainForm;
		private Twitpic twitpic;
		
		protected override void OnStartup(StartupEventArgs e){
			if(!ApplicationProcess.IsFirst){
				ApplicationProcess.InvokeRemote("Show");
				this.Shutdown();
			}else{
				ApplicationProcess.Actions.Add("Show", new Action(delegate{
					this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate{
						if(this.MainWindow != null){
							((MainForm)this.MainWindow).ShowWindow();
						}
					}));
				}));
				//System.Windows.Forms.Application.EnableVisualStyles();
				this.DispatcherUnhandledException += this.HandleUnhandleExceptions;
				
				// 初期化
				this.commandLine = new CommandLine();
				this.networkJobServer = new NetworkJobServer();
				this.executablePath = System.Windows.Forms.Application.ExecutablePath;
				AvailableUpdates = new ReadOnlyCollection<UpdatePackage>(new UpdatePackage[0]);
				
				// 設定読み込み
				this.settings = new ApplicationSettings("Application");
				this.settings.UpgradeOnce();
				this.settings.PropertyChanged += this.ApplicationSettings_Changed;
				if(this.settings.StartupScriptPath == null){
					this.settings.StartupScriptPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\HiyokoStartup.py";
				}
				this.InitalizeWebProxy();
				this.ApplyShutdownMode();
				this.networkJobServer.Timeout = Program.Settings.ConnectionTimeout;
				this.InitalizeUrlShorter();
				this.InitalizeTwitpic();
				
				
				// 初期アカウント設定
				if(this.settings.AccessToken == null){
					SettingsForm settings = new SettingsForm();
					settings.ShowInTaskbar = true;
					settings.authTab.IsSelected = true;
					settings.ShowDialog();
				}
				
				// ThemeManager
				string path = IO.Path.GetDirectoryName(Program.ExecutablePath) + @"\Themes";
				this.themeManager = new ThemeManager();
				try{
					this.themeManager.LoadThemes(path);
					this.RefreshTheme();
				}catch{
				}
				
				// MainForm初期化
				this.mainForm = this.LoadMainForm();
				if(!(this.settings.IsResident && (this.commandLine.Options.ContainsKey("hide")))){
					this.mainForm.Show();
				}
				
				ThreadPool.QueueUserWorkItem(new WaitCallback(delegate{
					// IronPython初期化
					EngineOptions options = new EngineOptions();
					options.ShowClrExceptions = true;
					options.ClrDebuggingEnabled = true;
					options.ExceptionDetail = false;
					this.pythonEngine = new PythonEngine(options);
					this.pythonEngine.LoadAssembly(System.Reflection.Assembly.GetExecutingAssembly());
					ClrModule clr = (ClrModule)this.pythonEngine.Import("clr");
					clr.AddReferenceByPartialName("System");
					clr.AddReferenceByPartialName("System.Core");
					clr.AddReferenceByPartialName("System.Drawing");
					clr.AddReferenceByPartialName("System.Xml");
					clr.AddReferenceByPartialName("System.Xml.Linq");
					clr.AddReferenceByPartialName("System.Web");
					clr.AddReferenceByPartialName("System.Windows.Forms");
					clr.AddReferenceByPartialName("System.Runtime.Remoting");
					clr.AddReferenceByPartialName("WindowsBase");
					clr.AddReferenceByPartialName("PresentationCore");
					clr.AddReferenceByPartialName("PresentationFramework");
					
					// Startupスクリプト実行
					string[] scripts = new string[]{
						this.settings.StartupScriptPath,
						Path.GetDirectoryName(this.executablePath) + @"\DefaultStartup.py"
					}.Where(file => File.Exists(file)).Take(1).ToArray();
					this.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(delegate{
						MainForm.Outputs.Add(new MessageOutputItem(String.Format("スタートアップスクリプト実行中 - {0}", scripts[0])));
					}));
					if(scripts.Length > 0){
						try{
							string result = null;
							using(MemoryStream standardOutput = new MemoryStream()){
								this.pythonEngine.SetStandardOutput(standardOutput);
								this.pythonEngine.ExecuteFile(scripts[0]);
								result = System.Text.ASCIIEncoding.ASCII.GetString(standardOutput.ToArray());
							}
							this.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(delegate{
								if(!String.IsNullOrEmpty(result)){
									this.mainForm.Outputs.Add(new MessageOutputItem(result));
								}
								this.mainForm.Outputs.Add(new SuccessOutputItem("スタートアップスクリプト実行完了"));
							}));
						}catch(Exception ex){
							this.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(delegate{
								this.mainForm.Outputs.Add(new ErrorOutputItem(this.pythonEngine.FormatException(ex)));
							}));
						}
					}
				}));
				
				if(this.settings.IsAutoCheckUpdates && ((DateTime.Now - this.settings.LastCheckedUpdatesDate).Days >= 1)){
					CheckUpdates();
				}
			}
			base.OnStartup(e);
		}
		
		#region 初期化
		
		private void ApplyShutdownMode(){
			this.ShutdownMode = (this.settings.IsResident) ? ShutdownMode.OnExplicitShutdown : ShutdownMode.OnLastWindowClose;
		}
		
		private void InitalizeWebProxy(){
			if(this.settings.IsUseWebProxy && !String.IsNullOrEmpty(this.settings.WebProxyUriString)){
				try{
					var uri = new Uri(this.settings.WebProxyUriString);
					var proxy = new WebProxy(uri);
					if(!String.IsNullOrEmpty(uri.UserInfo)){
						string[] strs = uri.UserInfo.Split(new char[]{':'});
						if(strs.Length >= 2){
							proxy.Credentials = new NetworkCredential(strs[0], strs[1]);
						}
					}
					this.networkJobServer.Proxy = TwitterAPI.Proxy = proxy;
				}catch(UriFormatException ex){
					if(this.mainForm != null){
						this.mainForm.Outputs.Add(new ErrorOutputItem("プロクシのUriが不正です。\n" + ex.Message));
					}
				}
			}else{
				this.networkJobServer.Proxy = TwitterAPI.Proxy = WebRequest.DefaultWebProxy;
			}
		}
		
		private void RefreshTheme(){
			this.themeManager.CurrentTheme = null;
			var guid = Program.Settings.ThemeGuid;
			if(guid != Guid.Empty){
				try{
					var theme = this.themeManager.Themes.Where(t => (t.Guid == guid)).FirstOrDefault();
					if(theme != null){
						this.themeManager.CurrentTheme = theme;
						try{
							if((this.themeManager.CurrentTheme != null) && (this.themeManager.CurrentTheme.IsGlassWindow)){
								if(this.mainForm.AeroGlassCompositionEnabled){
									this.mainForm.SetAeroGlassTransparency();
								}
							}
						}catch{
						}
					}else{
						this.mainForm.Outputs.Add(new WarningOutputItem("テーマが見つかりませんでした。"));
					}
				}catch(Exception ex){
					this.mainForm.Outputs.Add(new ErrorOutputItem("テーマの適用に失敗しました。\n" + ex.Message));
				}
			}
		}
		
		private void InitalizeUrlShorter(){
			this.urlShorter = (String.IsNullOrEmpty(settings.BitlyApiKey)) ? null : new Bitly(settings.BitlyLogin, settings.BitlyApiKey);
		}
		
		private void InitalizeTwitpic(){
			this.twitpic = new Twitpic(Program.Settings.TwitpicUsername, Program.Settings.TwitpicPassword);
		}
		
		#endregion
		
		#region 関数
		
		private MainForm LoadMainForm(){
			MainForm mainForm = new MainForm();
			this.MainWindow = mainForm;
			return mainForm;
		}
		
		private void UnloadMainForm(){
			var mode = this.ShutdownMode;
			this.ShutdownMode = ShutdownMode.OnExplicitShutdown;
			MainForm mainForm = MainForm;
			mainForm.IsResident = false;
			mainForm.Close();
			this.ShutdownMode = mode;
		}
		
		#endregion
		
		#region イベント処理
		
		protected override void OnExit(ExitEventArgs e){
			if(ApplicationProcess.IsFirst){
				this.pythonEngine.Dispose();
				this.settings.Save();
			}
			base.OnExit(e);
		}
		
		private void HandleUnhandleExceptions(object sender, DispatcherUnhandledExceptionEventArgs e){
			if(e.Exception.GetType().Namespace == "IronPython.Runtime.Exceptions"){
				e.Handled = true;
				if(MessageBox.Show(
					String.Format("IronPythonスクリプトでエラーが発生しました。続行しますか？\n\n{0}", this.pythonEngine.FormatException(e.Exception)),
					"エラー",
					MessageBoxButton.YesNo) == MessageBoxResult.Yes){
				}else{
					this.Shutdown();
				}
			}
		}
		
		private void ApplicationSettings_Changed(object sender, PropertyChangedEventArgs e){
			ApplicationSettings settings = (ApplicationSettings)sender;
			switch(e.PropertyName){
				case "ConnectionTimeout":{
					this.networkJobServer.Timeout = settings.ConnectionTimeout;
					break;
				}
				case "IsResident":{
					this.ApplyShutdownMode();
					break;
				}
				case "WebProxyUriString":
				case "IsUseWebProxy":{
					this.InitalizeWebProxy();
					break;
				}
				case "BitlyApiKey":
				case "BitlyLogin":{
					this.InitalizeUrlShorter();
					break;
				}
				case "ThemeGuid":{
					this.RefreshTheme();
					break;
				}
				case "TwitpicUsername":
				case "TwitpicPassword":{
					this.InitalizeTwitpic();
					break;
				}
				case "IsAutoCheckUpdates":{
					if(settings.IsAutoCheckUpdates && ((DateTime.Now - settings.LastCheckedUpdatesDate).Days >= 1)){
						CheckUpdates();
					}
					break;
				}
			}
		}
		
		#endregion
		
		#region アップデート
		
		public static UpdatePackage[] GetUpdates(){
			return GetUpdates(true);
		}
		
		private static UpdatePackage[] GetUpdates(bool showProgress){
			ProgressForm progress = null;
			try{
				if(showProgress){
					progress = new ProgressForm();
					progress.Message = "更新を確認しています。";
					progress.IsIndeterminate = true;
					progress.Owner = MainForm;
					progress.Show();
					if((MainForm != null) && TaskbarManager.IsPlatformSupported){
						TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.Indeterminate, MainForm);
					}
				}
				var version = new Version(System.Windows.Forms.Application.ProductVersion);
				var updater = new AutoUpdater(new Uri("http://hiyoko.sourceforge.jp/updater/packages.xml"));
				var updates = updater.CheckUpdates().Where(p => p.Version > version)
				                                    .Where(p => p.State == PackageState.Stable).ToArray();
				Program.Settings.LastCheckedUpdatesDate = DateTime.Now;
				return updates;
			}finally{
				if(showProgress){
					progress.Close();
					if((MainForm != null) && TaskbarManager.IsPlatformSupported){
						TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.NoProgress, MainForm);
					}
				}
			}
		}
		
		public static void Update(UpdatePackage package){
			var progress = new ProgressForm();
			progress.Message = "更新ファイルダウンロード中";
			progress.IsIndeterminate = false;
			progress.Owner = MainForm;
			progress.Show();
			if((MainForm != null) && TaskbarManager.IsPlatformSupported){
				TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.Normal, MainForm);
			}
			
			package.DownloadInstallerAsync(delegate(object sender, DownloadProgressChangedEventArgs e2){
				Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate{
					progress.Value = (double)e2.ProgressPercentage;
					if((MainForm != null) && TaskbarManager.IsPlatformSupported){
						TaskbarManager.Instance.SetProgressValue(e2.ProgressPercentage, 100, MainForm);
					}
				}));
			}, delegate(object sender, AsyncCompletedEventArgs e2){
				Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate{
					var file = (string)e2.UserState;
					progress.Close();
					if((MainForm != null) && TaskbarManager.IsPlatformSupported){
						TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.NoProgress, MainForm);
					}
					MessageBox.Show("インストーラーを実行します。");
					Process.Start(file);
					Application.Current.Shutdown();
				}));
			});
		}
		
		private static void CheckUpdates(){
			ThreadPool.QueueUserWorkItem(new WaitCallback(delegate{
				try{
					var updates = new ReadOnlyCollection<UpdatePackage>(GetUpdates(false));
					Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(delegate{
						AvailableUpdates = updates;
						Program.Settings.LastCheckedUpdatesDate = DateTime.Now;
					}));
				}catch(WebException){
				}
			}));
		}
		
		#endregion
		
		#region プロパティ
		
		public static ReadOnlyCollection<UpdatePackage> AvailableUpdates{get; private set;}
		
		public static PythonEngine PythonEngine{
			get{
				Program prog = Application.Current as Program;
				return (prog != null) ? prog.pythonEngine : null;
			}
		}
		
		public static MainForm MainForm{
			get{
				return Application.Current.MainWindow as MainForm;
			}
		}
		
		public static ApplicationSettings Settings{
			get{
				Program prog = Application.Current as Program;
				return (prog != null) ? prog.settings : null;
			}
		}
		
		public static NetworkJobServer NetworkJobServer{
			get{
				Program prog = Application.Current as Program;
				return (prog != null) ? prog.networkJobServer : null;
			}
		}
		
		public static string ExecutablePath{
			get{
				Program prog = Application.Current as Program;
				return (prog != null) ? prog.executablePath : null;
			}
		}
		
		public static UrlShorter UrlShorter{
			get{
				Program prog = Application.Current as Program;
				return (prog != null) ? prog.urlShorter : null;
			}
		}
		
		public static ThemeManager ThemeManager{
			get{
				Program prog = Application.Current as Program;
				return (prog != null) ? prog.themeManager : null;
			}
		}
		
		public static Twitpic Twitpic{
			get{
				Program prog = Application.Current as Program;
				return (prog != null) ? prog.twitpic : null;
			}
		}
		
		#endregion
	}
}