///<summary>
		/// Constructor.
		///</summary>
		///<param name="idsInFile"></param>
		///<param name="replaceIdsInFile"></param>
		///<param name="writingSystemRepository"></param>
		public static void FindOrphans(
			IEnumerable<string> idsInFile,
			IdReplacementStrategy replaceIdsInFile,
			IWritingSystemRepository writingSystemRepository
		) {
			var originalIds = new List<string>(idsInFile);
			var updatedIds = new List<string>(idsInFile);
			foreach (var wsId in originalIds)
			{
				// Check if it's in the repo
				if (writingSystemRepository.Contains(wsId))
				{
					continue;
				}
				string newId = wsId;
				if (writingSystemRepository.WritingSystemIdHasChanged(wsId))
				{
					newId = writingSystemRepository.WritingSystemIdHasChangedTo(wsId);
				}
				else
				{
					// It's an orphan
					// Check for the writing system repository compatibility mode
					if (writingSystemRepository.CompatibilityMode == WritingSystemCompatibility.Flex7V0Compatible)
					{
						if (!wsId.StartsWith("x-"))
						{
							// Clean it
							var rfcTagCleaner = new Rfc5646TagCleaner(wsId);
							rfcTagCleaner.Clean();
							newId = rfcTagCleaner.GetCompleteTag();
						}
					}
					else
					{
						// Clean it
						var rfcTagCleaner = new Rfc5646TagCleaner(wsId);
						rfcTagCleaner.Clean();
						newId = rfcTagCleaner.GetCompleteTag();
					}
				}
				var conformantWritingSystem = WritingSystemDefinition.Parse(newId);
				// If it changed, then change
				if (conformantWritingSystem.Bcp47Tag != wsId)
				{
					conformantWritingSystem = WritingSystemDefinition.CreateCopyWithUniqueId(conformantWritingSystem, updatedIds);
					replaceIdsInFile(wsId, conformantWritingSystem.Bcp47Tag);
					updatedIds.Remove(wsId);
					updatedIds.Add(conformantWritingSystem.Bcp47Tag);
				}
				// Check if it's in the repo
				if (writingSystemRepository.Contains(conformantWritingSystem.Bcp47Tag))
				{
					continue;
				}
				// It's not in the repo so set it
				writingSystemRepository.Set(conformantWritingSystem);
			}
			writingSystemRepository.Save();
		}
		public void CompleteTagConstructor_QaaWithXDashBeforeValidLanguageName_NoChange()
		{
			var cleaner = new Rfc5646TagCleaner("qaa-x-th");
			cleaner.Clean();
			Assert.That(cleaner.GetCompleteTag(), Is.EqualTo("qaa-x-th"));
			Assert.That(cleaner.Language, Is.EqualTo("qaa"));
			Assert.That(cleaner.PrivateUse, Is.EqualTo("th"));
		}
		public void CompleteTagConstructor_InvalidLanguageNameWithScript_QaaAdded()
		{
			var cleaner = new Rfc5646TagCleaner("wee-Latn");
			cleaner.Clean();
			Assert.That(cleaner.GetCompleteTag(), Is.EqualTo("qaa-Latn-x-wee"));

			// Also when initially "Latn" is properly in the Script field.
			cleaner = new Rfc5646TagCleaner("wee", "Latn", "", "", "");
			cleaner.Clean();
			Assert.That(cleaner.GetCompleteTag(), Is.EqualTo("qaa-Latn-x-wee"));
		}
		public void PrivateScriptKnownLanguageAndRegion_InsertsPrivateScriptCode()
		{
			var cleaner = new Rfc5646TagCleaner("fr", "x-script", "NO", "", "");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "fr", "Qaaa", "NO", "", "fr-Qaaa-NO-x-script");
		}
		public void ValidLanguageCodeMarkedPrivate_InsertsQaa()
		{
			var cleaner = new Rfc5646TagCleaner("x-de", "", "", "", "");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "qaa", "", "", "", "qaa-x-de");
		}
		public void CompleteTagConstructor_LanguageNameWithAudio_GetZxxxAdded()
		{
			var cleaner = new Rfc5646TagCleaner("aaa-x-audio");
			cleaner.Clean();
			Assert.That(cleaner.GetCompleteTag(), Is.EqualTo("aaa-Zxxx-x-audio"));
		}
		public void CleanMarksCustomScriptMovedToPrivateUse()
		{
			var cleaner = new Rfc5646TagCleaner("en-Zyxw");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "en", "Qaaa", "", "", "en-Qaaa-x-Zyxw");
		}
		public void NewTag_IsNotModified()
		{
			var cleaner = new Rfc5646TagCleaner("fr-Qaaa-QM-x-Mysc-YY");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "fr", "Qaaa", "QM", "", "fr-Qaaa-QM-x-Mysc-YY");
		}
		public void Pes_BecomesFa()
		{
			var cleaner = new Rfc5646TagCleaner("pes", "Latn", "", "", "");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "fa", "Latn", "", "", "fa-Latn");
		}
		public void CmnNoRegion_BecomesZhCN()
		{
			var cleaner = new Rfc5646TagCleaner("cmn", "", "", "", "");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "zh", "", "CN", "", "zh-CN");
		}
		public void ZhNoRegion_InsertsRegionCN()
		{
			var cleaner = new Rfc5646TagCleaner("zh", "", "", "", "");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "zh", "", "CN", "", "zh-CN");
		}
		public void MultiPartVariantWithoutX_InsertsX()
		{
			var cleaner = new Rfc5646TagCleaner("fr", "", "", "fonipa-etic", "");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "fr", "", "", "fonipa", "fr-fonipa-x-etic");
		}
		public void PrivateRegionMultiPartVariant_InsertsPrivateRegionCode()
		{
			var cleaner = new Rfc5646TagCleaner("fr", "", "x-ZY", "fonipa-x-etic", "");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "fr", "", "QM", "fonipa", "fr-QM-fonipa-x-ZY-etic");
		}
		public void PrivateRegionKnownLanguageAndScript_InsertsPrivateRegionCode()
		{
			var cleaner = new Rfc5646TagCleaner("fr", "Latn", "x-ZY", "", "");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "fr", "Latn", "QM", "", "fr-Latn-QM-x-ZY");
		}
		public void CompleteTagConstructor_HasInvalidLanguageName_MovedToPrivateUse()
		{
			var cleaner = new Rfc5646TagCleaner("234");
			cleaner.Clean();
			Assert.That(cleaner.GetCompleteTag(), Is.EqualTo("qaa-x-234"));
		}
		public void LanguageCodeAfterX_IsNotShortened()
		{
			var cleaner = new Rfc5646TagCleaner("qaa-x-kal");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "qaa", "", "", "", "qaa-x-kal");
		}
		public void NewTagWithPrivateLanguage_IsNotModified()
		{
			var cleaner = new Rfc5646TagCleaner("qaa-Qaaa-QM-x-kal-Mysc-YY");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "qaa", "Qaaa", "QM", "", "qaa-Qaaa-QM-x-kal-Mysc-YY");
		}
		public void Arb_BecomesAr()
		{
			var cleaner = new Rfc5646TagCleaner("arb", "", "x-ZG", "", "");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "ar", "", "QM", "", "ar-QM-x-ZG");
		}
		public void RegionCodesThatMatchLanguageCodesNotMovedToPrivateUse()
		{
			var cleaner = new Rfc5646TagCleaner("rwr-IN");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "rwr", "", "IN", "", "rwr-IN");
		}
		public void ZhRegion_NoChange()
		{
			var cleaner = new Rfc5646TagCleaner("zh", "", "NO", "", "");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "zh", "", "NO", "", "zh-NO");

			cleaner = new Rfc5646TagCleaner("zh", "", "x-ZK", "", "");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "zh", "", "QM", "", "zh-QM-x-ZK");
		}
		public void ScriptEndingWithX_IsHandledCorrectly()
		{
			var cleaner = new Rfc5646TagCleaner("zh-Phnx-CN-fonipa-x-emic");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "zh", "Phnx", "CN", "fonipa", "zh-Phnx-CN-fonipa-x-emic");
		}
		public void LanguageSubtagContainsMultipleValidLanguageSubtagsAsWellAsDataThatIsNotValidLanguageScriptRegionOrVariant_AllSubtagsButFirstValidLanguageSubtagAreMovedToPrivateUse()
		{
			var cleaner = new Rfc5646TagCleaner("bogus-en-audio-tpi-bogus2-x-", "", "", "", "");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "en", "Zxxx", "", "", "en-Zxxx-x-bogus-audio-bogus2-tpi");
		}
		void VerifyRfcCleaner(Rfc5646TagCleaner cleaner, string language, string script, string region, string variant, string completeTag)
		{
			Assert.That(cleaner.Language, Is.EqualTo(language));
			Assert.That(cleaner.Script, Is.EqualTo(script));
			Assert.That(cleaner.Region, Is.EqualTo(region));
			Assert.That(cleaner.Variant, Is.EqualTo(variant));
			Assert.That(cleaner.GetCompleteTag(), Is.EqualTo(completeTag));
		}
		public void CompleteTagConstructor_HasLanguageNameAndOtherName_OtherNameMovedToPrivateUse()
		{
			var cleaner = new Rfc5646TagCleaner("abc-123");
			cleaner.Clean();
			Assert.That(cleaner.GetCompleteTag(), Is.EqualTo("abc-x-123"));
		}
		public void CompleteTagConstructor_XDashBeforeValidLanguageNameInVariant_NoChange()
		{
			var cleaner = new Rfc5646TagCleaner("", "", "", "x-de", "");
			cleaner.Clean();
			Assert.That(cleaner.GetCompleteTag(), Is.EqualTo("x-de"));
		}
		public void CmnRegion_BecomesZh()
		{
			var cleaner = new Rfc5646TagCleaner("cmn", "", "NO", "", "");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "zh", "", "NO", "", "zh-NO");
		}
		public void Language_XDashBeforeString_AddsQaa()
		{
			var cleaner = new Rfc5646TagCleaner("x-blah");
			cleaner.Clean();
			Assert.That(cleaner.GetCompleteTag(), Is.EqualTo("qaa-x-blah"));
		}
		public void EmicWithoutFonipa_AddsFonipa()
		{
			var cleaner = new Rfc5646TagCleaner("en", "", "", "x-emic", "");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "en", "", "", "fonipa", "en-fonipa-x-emic");
		}
		public void CompleteTagConstructor_TagContainsOnlyPrivateUseWithAdditionalXDash_RedundantXDashRemoved()
		{
			var cleaner = new Rfc5646TagCleaner("x-some-x-whatever");
			cleaner.Clean();
			Assert.That(cleaner.GetCompleteTag(), Is.EqualTo("qaa-x-some-whatever"));
		}
		public void PrivateUseVariantLanguageCode_IsNotShortened()
		{
			var cleaner = new Rfc5646TagCleaner("qaa", "", "", "", "x-kal");
			cleaner.Clean();
			VerifyRfcCleaner(cleaner, "qaa", "", "", "", "qaa-x-kal");
		}