context: // <file>
  //     <copyright see="prj:///doc/copyright.txt"/>
  //     <license see="prj:///doc/license.txt"/>
  //     <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
  //     <version>$Revision$</version>
  // </file>
  
  using System;
  using System.Collections.ObjectModel;
  using System.Diagnostics.CodeAnalysis;
  using System.IO;
  using System.Reflection;
  
  namespace ICSharpCode.SharpDevelop.Sda
  {
  	/// <summary>
  	/// This class can host an instance of SharpDevelop inside another
  	/// AppDomain.
  	/// </summary>
  	public sealed class SharpDevelopHost
  	{
  		#region CreateDomain
  		/// <summary>
  		/// Create an AppDomain capable of hosting SharpDevelop.
  		/// </summary>
  		public static AppDomain CreateDomain()
  		{
  			return AppDomain.CreateDomain("SharpDevelop.Sda", null, CreateDomainSetup());
  		}
  		
  		/// <summary>
  		/// Creates an AppDomainSetup specifying properties for an AppDomain capable of
  		/// hosting SharpDevelop.
  		/// </summary>
  		public static AppDomainSetup CreateDomainSetup()
  		{
  			AppDomainSetup s = new AppDomainSetup();
  			s.ApplicationBase = Path.GetDirectoryName(SdaAssembly.Location);
  			s.ConfigurationFile = SdaAssembly.Location + ".config";
  			s.ApplicationName = "SharpDevelop.Sda";
  			return s;
  		}
  		#endregion
  		
  		#region Static helpers
  		internal static Assembly SdaAssembly {
  			get {
  				return typeof(SharpDevelopHost).Assembly;
  			}
  		}
  		#endregion
  		
  		#region SDInitStatus enum
  		enum SDInitStatus
  		{
  			None,
  			CoreInitialized,
  			WorkbenchInitialized,
  			Busy,
  			AppDomainUnloaded
  		}
  		#endregion
  		
  		AppDomain appDomain;
  		CallHelper helper;
  		SDInitStatus initStatus;
  		
  		#region Constructors
  		/// <summary>
  		/// Create a new AppDomain to host SharpDevelop.
  		/// </summary>
  		public SharpDevelopHost(StartupSettings startup)
  		{
  			if (startup == null) {
  				throw new ArgumentNullException("startup");
  			}
  			this.appDomain = CreateDomain();
  			helper = (CallHelper)appDomain.CreateInstanceAndUnwrap(SdaAssembly.FullName, typeof(CallHelper).FullName);
  			helper.InitSharpDevelopCore(new CallbackHelper(this), startup);
  			initStatus = SDInitStatus.CoreInitialized;
  		}
  		
  		/// <summary>
  		/// Host SharpDevelop in the existing AppDomain.
  		/// </summary>
  		public SharpDevelopHost(AppDomain appDomain, StartupSettings startup)
  		{
  			if (appDomain == null) {
  				throw new ArgumentNullException("appDomain");
  			}
  			if (startup == null) {
  				throw new ArgumentNullException("startup");
  			}
  			this.appDomain = appDomain;
  			helper = (CallHelper)appDomain.CreateInstanceAndUnwrap(SdaAssembly.FullName, typeof(CallHelper).FullName);
  			helper.InitSharpDevelopCore(new CallbackHelper(this), startup);
  			initStatus = SDInitStatus.CoreInitialized;
  		}
  		#endregion
  		
  		#region Workbench Initialization and startup
  		/// <summary>
  		/// Initializes the workbench (create the MainForm instance, construct menu from AddInTree etc.)
  		/// and runs it using the supplied settings.
  		/// This starts a new message loop for the workbench. By default the message loop
  		/// is created on a new thread, but you can change the settings so that
  		/// it is created on the thread calling RunWorkbench.
  		/// In that case, RunWorkbench will block until SharpDevelop is shut down!
  		/// </summary>
  		public void RunWorkbench(WorkbenchSettings settings)
  		{
  			if (settings == null) {
  				throw new ArgumentNullException("settings");
  			}
  			if (initStatus == SDInitStatus.CoreInitialized) {
  				initStatus = SDInitStatus.Busy;
  				helper.RunWorkbench(settings);
  				if (settings.RunOnNewThread) {
  					initStatus = SDInitStatus.WorkbenchInitialized;
  				}
  			} else {
  				throw new InvalidOperationException();
  			}
  		}
  		#endregion
  		
  		#region Application control
  		/// <summary>
  		/// Gets the list of currently opened documents.
  		/// </summary>
  		public ReadOnlyCollection<Document> OpenDocuments {
  			get {
  				if (initStatus != SDInitStatus.WorkbenchInitialized) {
  					return new ReadOnlyCollection<Document>(new Document[0]);
  				}
  				return new ReadOnlyCollection<Document>(helper.OpenDocuments);
  			}
  		}
  		
  		/// <summary>
  		/// Opens the document with the specified file name.
  		/// Requires that the workbench is running.
  		/// </summary>
  		public Document OpenDocument(string fileName)
  		{
  			if (initStatus != SDInitStatus.WorkbenchInitialized) {
  				throw new InvalidOperationException();
  			}
  			return helper.OpenDocument(fileName);
  		}
  		
  		/// <summary>
  		/// Opens the project or solution with the specified file name.
  		/// Requires that the workbench is running.
  		/// </summary>
  		public void OpenProject(string fileName)
  		{
  			if (initStatus != SDInitStatus.WorkbenchInitialized) {
  				throw new InvalidOperationException();
  			}
  			helper.OpenProject(fileName);
  		}
  		
  		/// <summary>
  		/// Gets if the specified file is a project or solution file.
  		/// </summary>
  		public bool IsSolutionOrProject(string fileName)
  		{
  			return helper.IsSolutionOrProject(fileName);
  		}
  		
  		/// <summary>
  		/// Gets/Sets whether the workbench is visible.
  		/// Requires that the workbench is running.
  		/// </summary>
  		public bool WorkbenchVisible {
  			get {
  				if (initStatus != SDInitStatus.WorkbenchInitialized) {
  					return false;
  				} else {
  					return helper.WorkbenchVisible;
  				}
  			}
  			set {
  				if (initStatus != SDInitStatus.WorkbenchInitialized) {
  					throw new InvalidOperationException();
  				}
  				helper.WorkbenchVisible = value;
  			}
  		}
  		
  		/// <summary>
  		/// Closes and unloads the workbench. The user is asked to save his work
  		/// and can abort closing.
  		/// Requires that the workbench is running.
  		/// </summary>
  		/// <param name="force">When force is used (=true), unsaved changes to documents
  		/// are lost, but SharpDevelop still terminates correctly and saves changed
  		/// settings.</param>
  		/// <returns>True when the workbench was closed.</returns>
  		public bool CloseWorkbench(bool force)
  		{
  			if (initStatus == SDInitStatus.CoreInitialized) {
  				// Workbench not loaded/already closed: do nothing
  				return true;
  			}
  			if (initStatus != SDInitStatus.WorkbenchInitialized) {
  				throw new InvalidOperationException();
  			}
  			return helper.CloseWorkbench(force);
  		}
  		
  		/// <summary>
  		/// Unload the SharpDevelop AppDomain. This will force SharpDevelop to close
  		/// without saving open files or changed settings.
  		/// Call CloseWorkbench before UnloadDomain to prompt the user to save documents and settings.
  		/// </summary>
  		public void UnloadDomain()
  		{
  			if (initStatus != SDInitStatus.AppDomainUnloaded) {
  				if (initStatus == SDInitStatus.WorkbenchInitialized) {
  					helper.KillWorkbench();
  				}
  				AppDomain.Unload(appDomain);
  				initStatus = SDInitStatus.AppDomainUnloaded;
  			}
  		}
  		#endregion
  		
  		#region CreateInstanceInTargetDomain
  		/// <summary>
  		/// Gets the AppDomain used to host SharpDevelop.
  		/// </summary>
  		public AppDomain AppDomain {
  			get {
  				return appDomain;
  			}
  		}
  		
  		/// <summary>
  		/// Creates an instance of the specified type argument in the target AppDomain.
  		/// </summary>
  		/// <param name="arguments">Arguments to pass to the constructor of <paramref name="type"/>.</param>
  		/// <returns>The constructed object.</returns>
  		[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
  		public T CreateInstanceInTargetDomain<T>(params object[] arguments) where T : MarshalByRefObject
  		{
  			Type type = typeof(T);
  			return (T)appDomain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName, arguments);
  		}
  		
  		/// <summary>
  		/// Creates an instance of the specified type in the target AppDomain.
  		/// </summary>
  		/// <param name="type">Type to create an instance of.</param>
  		/// <param name="arguments">Arguments to pass to the constructor of <paramref name="type"/>.</param>
  		/// <returns>The constructed object.</returns>
  		[SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")]
  		public object CreateInstanceInTargetDomain(Type type, params object[] arguments)
  		{
  			if (type == null)
  
gt: 				throw new ArgumentNullException("type");