/// <summary>
        /// Copies resource files from the Xcode project (back) to the MonoDevelop project directory.
        /// </summary>
        /// <param name='monitor'>
        /// A progress monitor.
        /// </param>
        /// <param name='context'>
        /// The sync context.
        /// </param>
        void CopyFilesToMD(IProgressMonitor monitor, XcodeSyncBackContext context)
        {
            if (context.FileSyncJobs.Count == 0)
            {
                return;
            }

            foreach (var file in context.FileSyncJobs)
            {
                monitor.Log.WriteLine("Copying {0} file from Xcode: {1}", file.IsFreshlyAdded ? "added" : "changed", file.SyncedRelative);

                if (!Directory.Exists(file.Original.ParentDirectory))
                {
                    Directory.CreateDirectory(file.Original.ParentDirectory);
                }

                var tempFile = file.Original.ParentDirectory.Combine(".#" + file.Original.ParentDirectory.FileName);
                File.Copy(context.ProjectDir.Combine(file.SyncedRelative), tempFile);
                FileService.SystemRename(tempFile, file.Original);
                context.SetSyncTimeToNow(file.SyncedRelative);
            }

            monitor.EndTask();
        }
        /// <summary>
        /// Updates the cli types.
        /// </summary>
        /// <returns>
        /// Returns whether or not any files were added to the project.
        /// </returns>
        /// <param name='monitor'>
        /// A progress monitor.
        /// </param>
        /// <param name='context'>
        /// A sync-back context.
        /// </param>
        /// <param name='typesAdded'>
        /// An output variable specifying whether or not any types were added to the project.
        /// </param>
        bool UpdateCliTypes(IProgressMonitor monitor, XcodeSyncBackContext context, out bool typesAdded)
        {
            var provider = dnp.LanguageBinding.GetCodeDomProvider();
            var options  = new System.CodeDom.Compiler.CodeGeneratorOptions();
            var writer   = MonoDevelop.DesignerSupport.CodeBehindWriter.CreateForProject(
                new MonoDevelop.Core.ProgressMonitoring.NullProgressMonitor(), dnp);

            monitor.BeginTask(GettextCatalog.GetString("Detecting changes made in Xcode"), 0);
            Dictionary <string, NSObjectTypeInfo> newTypes;
            Dictionary <string, ProjectFile>      newFiles;
            var updates = context.GetTypeUpdates(monitor, provider, out newTypes, out newFiles);

            if ((updates == null || updates.Count == 0) && newTypes == null && newFiles == null)
            {
                monitor.Log.WriteLine("No changes found");
                monitor.EndTask();
                typesAdded = false;
                return(false);
            }

            monitor.Log.WriteLine("Found {0} changed types", updates.Count);
            monitor.EndTask();

            int count = updates.Count + (newTypes != null ? newTypes.Count : 0);

            monitor.BeginTask(GettextCatalog.GetString("Updating types in MonoDevelop"), count);

            // First, add new types...
            if (newTypes != null && newTypes.Count > 0)
            {
                foreach (var nt in newTypes)
                {
                    if (provider is Microsoft.CSharp.CSharpCodeProvider)
                    {
                        var cs = new CSharpCodeTypeDefinition()
                        {
                            WrapperNamespace = infoService.WrapperRoot,
                            Provider         = provider,
                            Type             = nt.Value,
                        };

                        string baseDir = Path.GetDirectoryName(nt.Key);
                        if (!Directory.Exists(baseDir))
                        {
                            Directory.CreateDirectory(baseDir);
                        }

                        writer.WriteFile(nt.Key, cs.TransformText());
                    }
                    else
                    {
                        // FIXME: implement support for non-C# languages
                    }

                    monitor.Step(1);
                }

                typesAdded = true;
            }
            else
            {
                typesAdded = false;
            }

            // Next, generate the designer files for any added/changed types
            foreach (var df in updates)
            {
                monitor.Log.WriteLine("Syncing {0} types from Xcode to file '{1}'", df.Value.Count, df.Key);
                if (provider is Microsoft.CSharp.CSharpCodeProvider)
                {
                    var cs = new CSharpCodeCodebehind()
                    {
                        Types            = df.Value,
                        WrapperNamespace = infoService.WrapperRoot,
                        Provider         = provider,
                    };
                    writer.WriteFile(df.Key, cs.TransformText());
                }
                else
                {
                    var ccu = GenerateCompileUnit(provider, options, df.Key, df.Value);
                    writer.WriteFile(df.Key, ccu);
                }

                monitor.Step(1);
            }

            writer.WriteOpenFiles();

            // Update sync timestamps
            foreach (var df in updates)
            {
                foreach (var type in df.Value)
                {
                    context.SetSyncTimeToNow(type.ObjCName + ".h");
                    context.SetSyncTimeToNow(type.ObjCName + ".m");
                }
            }

            // Add new files to the DotNetProject
            if (newFiles != null)
            {
                foreach (var f in newFiles)
                {
                    monitor.Log.WriteLine("Added new designer file {0}", f.Key);
                    dnp.AddFile(f.Value);
                }
            }

            monitor.EndTask();

            return(newFiles != null && newFiles.Count > 0);
        }
		void UpdateCliTypes (IProgressMonitor monitor, XcodeSyncBackContext context)
		{
			
			var provider = dnp.LanguageBinding.GetCodeDomProvider ();
			var options = new System.CodeDom.Compiler.CodeGeneratorOptions ();
			var writer = MonoDevelop.DesignerSupport.CodeBehindWriter.CreateForProject (
				new MonoDevelop.Core.ProgressMonitoring.NullProgressMonitor (), dnp);
			
			monitor.BeginTask (GettextCatalog.GetString ("Detecting changed types from Xcode"), 0);
			Dictionary<string,ProjectFile> newFiles;
			var updates = context.GetTypeUpdates (out newFiles);
			if (updates == null || updates.Count == 0) {
				monitor.Log.WriteLine ("No changed types found");
				monitor.EndTask ();
				return;
			}
			monitor.Log.WriteLine ("Found {0} changed types", updates.Count);
			monitor.EndTask ();
			
			monitor.BeginTask (GettextCatalog.GetString ("Updating types in MonoDevelop"), updates.Count);
			foreach (var df in updates) {
				monitor.Log.WriteLine ("Syncing {0} types from Xcode to file '{1}'", df.Value.Count, df.Key);
				if (provider is Microsoft.CSharp.CSharpCodeProvider) {
					var cs = new CSharpCodeCodebehind () {
						Types = df.Value,
						WrapperNamespace = infoService.WrapperRoot,
						Provider = provider,
					};
					writer.WriteFile (df.Key, cs.TransformText ());
				} else {
					var ccu = GenerateCompileUnit (provider, options, df.Key, df.Value);
					writer.WriteFile (df.Key, ccu);
				}
				monitor.Step (1);
			}
			writer.WriteOpenFiles ();
			
			foreach (var df in updates) {
				foreach (var type in df.Value) {
					context.SetSyncTimeToNow (type.ObjCName + ".h");
					context.SetSyncTimeToNow (type.ObjCName + ".m");
				}
			}
			
			foreach (var job in context.TypeSyncJobs) {
				context.ProjectInfo.InsertUpdatedType (job.Type);
			}
			
			if (newFiles != null) {
				foreach (var f in newFiles) {
					monitor.Log.WriteLine ("Added new designer file {0}", f.Key);
					dnp.AddFile (f.Value);
				}
				monitor.Log.WriteLine ("Saving project '{0}'", dnp.Name);
				Ide.IdeApp.ProjectOperations.Save (dnp);
			}
			monitor.EndTask ();
		}
		void CopyFilesToMD (IProgressMonitor monitor, XcodeSyncBackContext context)
		{
			if (context.FileSyncJobs.Count == 0)
				return;
			foreach (var file in context.FileSyncJobs) {
				monitor.Log.WriteLine ("Copying changed file from Xcode: {0}", file.SyncedRelative);
				var tempFile = file.Original.ParentDirectory.Combine (".#" + file.Original.ParentDirectory.FileName);
				File.Copy (context.ProjectDir.Combine (file.SyncedRelative), tempFile);
				FileService.SystemRename (tempFile, file.Original);
				context.SetSyncTimeToNow (file.SyncedRelative);
			}
			Gtk.Application.Invoke (delegate {
				FileService.NotifyFilesChanged (context.FileSyncJobs.Select (f => f.Original));
			});
			monitor.EndTask ();
		}
		/// <summary>
		/// Updates the cli types.
		/// </summary>
		/// <returns>
		/// Returns whether or not any files were added to the project.
		/// </returns>
		/// <param name='monitor'>
		/// A progress monitor.
		/// </param>
		/// <param name='context'>
		/// A sync-back context.
		/// </param>
		/// <param name='typesAdded'>
		/// An output variable specifying whether or not any types were added to the project.
		/// </param>
		bool UpdateCliTypes (IProgressMonitor monitor, XcodeSyncBackContext context, out bool typesAdded)
		{
			var provider = dnp.LanguageBinding.GetCodeDomProvider ();
			var options = new System.CodeDom.Compiler.CodeGeneratorOptions ();
			var writer = MonoDevelop.DesignerSupport.CodeBehindWriter.CreateForProject (
				new MonoDevelop.Core.ProgressMonitoring.NullProgressMonitor (), dnp);
			
			monitor.BeginTask (GettextCatalog.GetString ("Detecting changes made in Xcode"), 0);
			Dictionary<string, NSObjectTypeInfo> newTypes;
			Dictionary<string, ProjectFile> newFiles;
			var updates = context.GetTypeUpdates (monitor, provider, out newTypes, out newFiles);
			if ((updates == null || updates.Count == 0) && newTypes == null && newFiles == null) {
				monitor.Log.WriteLine ("No changes found");
				monitor.EndTask ();
				typesAdded = false;
				return false;
			}
			
			monitor.Log.WriteLine ("Found {0} changed types", updates.Count);
			monitor.EndTask ();
			
			int count = updates.Count + (newTypes != null ? newTypes.Count : 0);
			monitor.BeginTask (GettextCatalog.GetString ("Updating types in MonoDevelop"), count);
			
			// First, add new types...
			if (newTypes != null && newTypes.Count > 0) {
				foreach (var nt in newTypes) {
					if (provider is Microsoft.CSharp.CSharpCodeProvider) {
						var cs = new CSharpCodeTypeDefinition () {
							WrapperNamespace = infoService.WrapperRoot,
							Provider = provider,
							Type = nt.Value,
						};
						
						string baseDir = Path.GetDirectoryName (nt.Key);
						if (!Directory.Exists (baseDir))
							Directory.CreateDirectory (baseDir);
						
						writer.WriteFile (nt.Key, cs.TransformText ());
					} else {
						// FIXME: implement support for non-C# languages
					}
					
					monitor.Step (1);
				}
				
				typesAdded = true;
			} else {
				typesAdded = false;
			}
			
			// Next, generate the designer files for any added/changed types
			foreach (var df in updates) {
				monitor.Log.WriteLine ("Syncing {0} types from Xcode to file '{1}'", df.Value.Count, df.Key);
				if (provider is Microsoft.CSharp.CSharpCodeProvider) {
					var cs = new CSharpCodeCodebehind () {
						Types = df.Value,
						WrapperNamespace = infoService.WrapperRoot,
						Provider = provider,
					};
					writer.WriteFile (df.Key, cs.TransformText ());
				} else {
					var ccu = GenerateCompileUnit (provider, options, df.Key, df.Value);
					writer.WriteFile (df.Key, ccu);
				}
				
				monitor.Step (1);
			}
			
			writer.WriteOpenFiles ();
			
			// Update sync timestamps
			foreach (var df in updates) {
				foreach (var type in df.Value) {
					context.SetSyncTimeToNow (type.ObjCName + ".h");
					context.SetSyncTimeToNow (type.ObjCName + ".m");
				}
			}
			
			// Add new files to the DotNetProject
			if (newFiles != null) {
				foreach (var f in newFiles) {
					monitor.Log.WriteLine ("Added new designer file {0}", f.Key);
					dnp.AddFile (f.Value);
				}
			}
			
			monitor.EndTask ();
			
			return newFiles != null && newFiles.Count > 0;
		}
		/// <summary>
		/// Copies resource files from the Xcode project (back) to the MonoDevelop project directory.
		/// </summary>
		/// <param name='monitor'>
		/// A progress monitor.
		/// </param>
		/// <param name='context'>
		/// The sync context.
		/// </param>
		void CopyFilesToMD (IProgressMonitor monitor, XcodeSyncBackContext context)
		{
			if (context.FileSyncJobs.Count == 0)
				return;
			
			foreach (var file in context.FileSyncJobs) {
				monitor.Log.WriteLine ("Copying {0} file from Xcode: {1}", file.IsFreshlyAdded ? "added" : "changed", file.SyncedRelative);
				
				if (!Directory.Exists (file.Original.ParentDirectory))
					Directory.CreateDirectory (file.Original.ParentDirectory);
				
				var tempFile = file.Original.ParentDirectory.Combine (".#" + file.Original.ParentDirectory.FileName);
				File.Copy (context.ProjectDir.Combine (file.SyncedRelative), tempFile);
				FileService.SystemRename (tempFile, file.Original);
				context.SetSyncTimeToNow (file.SyncedRelative);
			}
			
			monitor.EndTask ();
		}
		void CopyFilesToMD (XcodeSyncBackContext context)
		{
			foreach (var file in context.FileSyncJobs) {
				XC4Debug.Log ("Copying changed file from Xcode: {0}", file.SyncedRelative);
				var tempFile = file.Original.ParentDirectory.Combine (".#" + file.Original.ParentDirectory.FileName);
				File.Copy (context.ProjectDir.Combine (file.SyncedRelative), tempFile);
				FileService.SystemRename (tempFile, file.Original);
				context.SetSyncTimeToNow (file.SyncedRelative);
			}
			Gtk.Application.Invoke (delegate {
				FileService.NotifyFilesChanged (context.FileSyncJobs.Select (f => f.Original));
			});
		}
		public override void SyncBack (XcodeSyncBackContext context)
		{
			var hFile = context.ProjectDir.Combine (Type.ObjCName + ".h");
			var parsed = NSObjectInfoService.ParseHeader (hFile);
			
			var objcType = context.ProjectInfo.GetType (Type.ObjCName);
			if (objcType == null) {
				context.ReportError ("Missing objc type {0}", Type.ObjCName);
				return;
			}
			if (parsed.ObjCName != objcType.ObjCName) {
				context.ReportError ("Parsed type name {0} does not match original {1}",
					parsed.ObjCName, objcType.ObjCName);
				return;
			}
			if (!objcType.IsUserType) {
				context.ReportError ("Parsed type {0} is not a user type", objcType);
				return;
			}
			
			//FIXME: detect unresolved types
			parsed.MergeCliInfo (objcType);
			context.ProjectInfo.ResolveTypes (parsed);
			if (!context.ProjectInfo.ContainsErrors) {
				context.TypeSyncJobs.Add (new XcodeSyncObjcBackJob () {
					HFile = hFile,
					DesignerFile = objcType.GetDesignerFile (),
					Type = parsed,
				});
			} else {
				context.SetSyncTimeToNow (Type.ObjCName + ".h");
				LoggingService.LogWarning ("Sync back skipped because of errors in project info.");
			}
		}