/// <summary>
		/// Attempt to populate a new FieldWorks text with a BIRD format interlinear text. If this fails
		/// for some reason then the new text is deleted and also return false to tell the calling method to abort the import.
		/// </summary>
		/// <param name="options"></param>
		/// <param name="newText"></param>
		/// <param name="interlineartext"></param>
		/// <param name="progress"></param>
		/// <param name="version"></param>
		/// <returns>true if operation completed, false if the import operation should be aborted</returns>
		private bool PopulateTextIfPossible(ImportInterlinearOptions options, ref FDO.IText newText, Interlineartext interlineartext,
												IThreadedProgress progress, int version)
		{
			if (!PopulateTextFromBIRDDoc(ref newText,
					new TextCreationParams
					{
						Cache = m_cache,
						InterlinText = interlineartext,
						Progress = progress,
						ImportOptions = options,
						Version = version
					})) //if the user aborted this text
			{
				newText.Delete(); //remove it from the list
				return false;
			}
			return true;
		}
		bool DummyCheckAndAddLanguagesInternal(FdoCache cache, Interlineartext interlinText, ILgWritingSystemFactory wsFactory, IThreadedProgress progress)
		{
			return true;
		}
		private static void EnsureVernacularLanguage(Interlineartext interlinText,string langName)
		{
			foreach (var lang in interlinText.languages.language)
			{
				if (lang.lang == langName)
				{
					lang.vernacularSpecified = true;
					lang.vernacular = true;
					return;
				}
			}
		}
		/// <summary>
		/// Set text metadata, create or merge media file URI's.
		/// <note>media files (ELAN initiated) need to be processed before the paragraphs, as segments could reference these parts.</note>
		/// </summary>
		/// <param name="cache"></param>
		/// <param name="interlinText">The source text</param>
		/// <param name="wsFactory"></param>
		/// <param name="newText">The target text</param>
		/// <param name="merging">True if we are merging into an existing text; False if we are creating everything new</param>
		private static void SetTextMetaAndMergeMedia(FdoCache cache, Interlineartext interlinText, ILgWritingSystemFactory wsFactory,
			FDO.IText newText, bool merging)
		{
			if (interlinText.Items != null) // apparently it is null if there are no items.
			{
				foreach (var item in interlinText.Items)
				{
					switch (item.type)
					{
						case "title":
							newText.Name.set_String(GetWsEngine(wsFactory, item.lang).Handle, item.Value);
							break;
						case "title-abbreviation":
							newText.Abbreviation.set_String(GetWsEngine(wsFactory, item.lang).Handle, item.Value);
							break;
						case "source":
							newText.Source.set_String(GetWsEngine(wsFactory, item.lang).Handle, item.Value);
							break;
						case "comment":
							newText.Description.set_String(GetWsEngine(wsFactory, item.lang).Handle, item.Value);
							break;
					}
				}
			}

			if (interlinText.mediafiles != null)
			{
				if (newText.MediaFilesOA == null)
					newText.MediaFilesOA = cache.ServiceLocator.GetInstance<ICmMediaContainerFactory>().Create();
				newText.MediaFilesOA.OffsetType = interlinText.mediafiles.offsetType;

				foreach (var mediaFile in interlinText.mediafiles.media)
				{
					ICmObject extantObject;
					cache.ServiceLocator.ObjectRepository.TryGetObject(new Guid(mediaFile.guid), out extantObject);
					var media = extantObject as ICmMediaURI;
					if (media == null)
					{
						media = cache.ServiceLocator.GetInstance<ICmMediaURIFactory>().Create(cache, new Guid(mediaFile.guid));
						newText.MediaFilesOA.MediaURIsOC.Add(media);
					}
					else if (!merging)
					{
						// If a media URI with the same GUID exists, and we are not merging, create a new media URI with a new GUID
						media = cache.ServiceLocator.GetInstance<ICmMediaURIFactory>().Create();
						newText.MediaFilesOA.MediaURIsOC.Add(media);

						// Update references to this Media URI
						foreach (var phrase in interlinText.paragraphs.SelectMany(para => para.phrases))
						{
							if (mediaFile.guid.Equals(phrase.mediaFile))
								phrase.mediaFile = media.Guid.ToString();
						}
					}
					// else, the media URI already exists and we are merging; simply update the location
					media.MediaURI = mediaFile.location;
				}
			}
		}
		private static void SetVernacularLanguagesByUsage(Interlineartext interlinText)
		{
			foreach (var para in interlinText.paragraphs)
			{
				if(para.phrases == null) // if there are no phrases, they have no languages we are interested in.
				{
					continue;
				}
				foreach (var phrase in para.phrases)
				{
					foreach (var item in phrase.Items)
					{
						if (item.type == "txt")
							EnsureVernacularLanguage(interlinText, item.lang);
					}
					if(phrase.WordsContent.Words != null)
					{
						foreach (var word in phrase.WordsContent.Words)
						{
							foreach (var item in word.Items)
							{
								if (item.type == "txt")
									EnsureVernacularLanguage(interlinText, item.lang);
							}
							// We could dig into the morphemes, but any client generating morphemes probably
							// does things right, and anyway we don't import that yet.
						}
					}
				}
			}
		}
		private static string GetPartOfPhrase(Interlineartext interlinText)
		{
			int i = 0;
			var strBldr = new StringBuilder(ITextStrings.ksImportLangMissingTextStartsWith);
			foreach (var paragraph in interlinText.paragraphs)
			{
				foreach (var phrase in paragraph.phrases)
				{
					foreach (var word in phrase.WordsContent.Words)
					{
						strBldr.Append(word.Items[0].Value);
						strBldr.Append(" ");
						i++;
						if (i > 6)
						{
							strBldr.Append(" ...");
							return strBldr.ToString();
						}
					}
				}
			}
			return strBldr.ToString();
		}
		private static string GetInstructions(Interlineartext interlinText, String wsName, String instructions)
		{
			var strBldr = new StringBuilder(wsName);
			strBldr.Append(instructions);
			strBldr.Append(Environment.NewLine); strBldr.Append(Environment.NewLine);
			strBldr.Append(GetPartOfPhrase(interlinText));
			return strBldr.ToString();
		}
		/// <summary>
		/// The imported text may be in a writing system that is not part of this project. Return false if the user
		/// rejects the text which tells the caller of this method to abort the import.
		/// </summary>
		/// <param name="cache"></param>
		/// <param name="interlinText"></param>
		/// <param name="wsFactory"></param>
		/// <param name="progress"></param>
		/// <returns>return false to abort import</returns>
		private static bool CheckAndAddLanguagesInternal(FdoCache cache, Interlineartext interlinText, ILgWritingSystemFactory wsFactory, IThreadedProgress progress)
		{
			DialogResult result;
			if (interlinText.languages != null && interlinText.languages.language != null)
			{
				if (!SomeLanguageSpecifiesVernacular(interlinText))
				{
					// Saymore file? something else that doesn't know to do this? We will confuse the user if we try to treat all as analysis.
					SetVernacularLanguagesByUsage(interlinText);
				}
				foreach (var lang in interlinText.languages.language)
				{
					bool fIsVernacular;
					var writingSystem = SafelyGetWritingSystem(cache, wsFactory, lang, out fIsVernacular);
					if (fIsVernacular)
					{
						if (!cache.LanguageProject.CurrentVernacularWritingSystems.Contains(writingSystem.Handle))
						{
							//we need to invoke the dialog on the main thread so we can use the progress dialog as the parent.
							//otherwise the message box can be displayed behind everything
							var instructions = GetInstructions(interlinText, writingSystem.LanguageName, ITextStrings.ksImportVernacLangMissing);
							IAsyncResult asyncResult = progress.SynchronizeInvoke.BeginInvoke(new ShowDialogAboveProgressbarDelegate(ShowDialogAboveProgressbar),
																		 new object[]
																			{
																				progress,
																				instructions,
																				ITextStrings.ksImportVernacLangMissingTitle,
																				MessageBoxButtons.OKCancel
																			});
							result = (DialogResult)progress.SynchronizeInvoke.EndInvoke(asyncResult);
							if (result == DialogResult.OK)
							{
								cache.LanguageProject.AddToCurrentVernacularWritingSystems((IWritingSystem)writingSystem);
							}
							else if (result == DialogResult.Cancel)
							{
								return false;
							}
						}
					}
					else
					{
						if (!cache.LanguageProject.CurrentAnalysisWritingSystems.Contains(writingSystem.Handle))
						{
							var instructions = GetInstructions(interlinText, writingSystem.LanguageName,
															   ITextStrings.ksImportAnalysisLangMissing);
							IAsyncResult asyncResult = progress.SynchronizeInvoke.BeginInvoke(new ShowDialogAboveProgressbarDelegate(ShowDialogAboveProgressbar),
																		 new object[]
																			{
																				progress,
																				instructions,
																				ITextStrings.ksImportAnalysisLangMissingTitle,
																				MessageBoxButtons.OKCancel
																			});
							result = (DialogResult)progress.SynchronizeInvoke.EndInvoke(asyncResult);
							//alert the user
							if (result == DialogResult.OK)
							{
								//alert the user
								cache.LanguageProject.AddToCurrentAnalysisWritingSystems((IWritingSystem)writingSystem);
								// We already have progress indications up.
								XmlTranslatedLists.ImportTranslatedListsForWs(writingSystem.Id, cache, FwDirectoryFinder.TemplateDirectory, null);
							}
							else if (result == DialogResult.Cancel)
							{
								return false;
							}
						}
					}
				}
			}
			return true;
		}
		private static bool SomeLanguageSpecifiesVernacular(Interlineartext interlinText)
		{
			// return true if any language in the languages section is vernacular
			return interlinText.languages.language.Any(lang => lang.vernacularSpecified);
		}