using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Windows.Forms;

namespace Babebi4
{
	internal abstract class WordOrPattern
	{
		protected BabebiDataSet.WordOrPatternRow wordOrPatternRow;

		protected BabebiDataSet dataSet 
		{
			get { return (BabebiDataSet)(this.wordOrPatternRow.Table.DataSet); }	
		}

		public WordOrPattern(BabebiDataSet.WordOrPatternRow wordOrPatternRow)
		{
			this.wordOrPatternRow = wordOrPatternRow;
		}

		protected abstract string computeUniqueKeyString();

		internal bool ComputeAndTryToSetUniqueKeyString()
		{
			string computedUniqueKeyString = this.computeUniqueKeyString();

			bool isUniqueKeyStringAlreadyExisting = false;

			#region		where it is checked that the computed UKS does not exist yet.

			DataTable	dataTable	= this.getDataTable();

			DataRow[] foundDataRows 
				= this.getDataTable()
				.Select("UniqueKeyString = '" + computedUniqueKeyString + "'");

			if (foundDataRows.Length > 0)
			{
				int foundId = (int)(foundDataRows[0]["Id"]);
				int thisId = this.getId();

				if (foundId != thisId)
				{
					string objectName = dataTable.TableName;

					MessageBox.Show
					(
						"While propagating this modification, a generated 'Unique Key String' computing " +
						"got a conflict with the 'Unique Key String' of an already existing " + objectName + " :\n\n" +
						"- computed UKS	= '" + computedUniqueKeyString + "'\n" +
						"- existing " + objectName + " Id = '" + foundId.ToString() + "'\n" +
						"- error-trigger " + objectName + " Id = '" + thisId.ToString() + "'\n\n" +
						"The Unique Key String has not been performed and the further-children propagating has not performed.",
						"UniqueKeyString conflict",
						MessageBoxButtons.OK,
						MessageBoxIcon.Information
					);

					isUniqueKeyStringAlreadyExisting = true;
				}
			}

			#endregion	where it is checked that the computed UKS does not exist yet.

			if (isUniqueKeyStringAlreadyExisting)
			{
				return false;
			}
			else
			{ 
				this.setUniqueKeyString(computedUniqueKeyString);

				return true;
			}
		}

		internal virtual void PropagateToChildren()
		{
			#region		on PatternByInsertions

			BabebiDataSet.PatternByInsertionRow[] patternByInsertionRows = this.wordOrPatternRow.GetPatternByInsertionRows();

			foreach (BabebiDataSet.PatternByInsertionRow patternByInsertionRow in patternByInsertionRows)
			{
				PatternByInsertion patternByInsertion = new PatternByInsertion(patternByInsertionRow);

				if (patternByInsertion.ComputeAndTryToSetUniqueKeyString())
				{
					patternByInsertion.PropagateToChildren();
				}
			}

			#endregion	on PatternByInsertions
		}

		internal static void SetUniqueKeyString(bool trueForWord__falseForPattern, DataRow dataRow, string uniqueKeyString)
		{
			if (trueForWord__falseForPattern)
			{
				Word.SetUniqueKeyString((BabebiDataSet.WordRow)dataRow, uniqueKeyString);
			}
			else
			{ 
				BabebiDataSet.PatternRow pattern = (BabebiDataSet.PatternRow)dataRow;

				Pattern.SetUniqueKeyString(pattern, uniqueKeyString);
			}
		}

		#region		getters & setters 

		protected abstract void			setUniqueKeyString(string computedUniqueKeyString);
		protected abstract int			getId();
		protected abstract DataTable	getDataTable();

		#endregion	getters & setters 
	}

	internal abstract class Word : WordOrPattern	
	{
		protected BabebiDataSet.WordRow wordRow;

		internal Word(BabebiDataSet.WordRow wordRow) : base(wordRow.WordOrPatternRowParent)
		{
			this.wordRow = wordRow;
		}

		internal override void PropagateToChildren()
		{ 
			base.PropagateToChildren();

			#region		on WordDerivateds if hasDerivating

			if (this.wordRow.HasDerivating)
			{
				BabebiDataSet.WordDerivatingRow wordDerivatingRow
					= this.dataSet.WordDerivating.FindByWordId(this.wordRow.Id);

				foreach (BabebiDataSet.WordDerivatedRow wordDerivatedRow in wordDerivatingRow.GetWordDerivatedRows())
				{ 
					WordDerivated wordDerivated = new WordDerivated(wordDerivatedRow);

					if (wordDerivated.ComputeAndTryToSetUniqueKeyString())
					{
						wordDerivated.PropagateToChildren();
					}
				}
			}

			#endregion	on WordDerivateds if hasDerivating
			#region		on WordByInsertions if any

			BabebiDataSet.WordByInsertionRow[] wordByInsertionRows
				= this.wordRow.GetWordByInsertionRowsByWordByInsertion__Word__Origin();

			foreach (BabebiDataSet.WordByInsertionRow wordByInsertionRow in wordByInsertionRows)
			{
				WordByInsertion wordByInsertion = new WordByInsertion(wordByInsertionRow);

				if (wordByInsertion.ComputeAndTryToSetUniqueKeyString())
				{
					wordByInsertion.PropagateToChildren();
				}
			}

			#endregion	on WordByInsertions if any
			#region		on WordConcatenateds if any

			BabebiDataSet.WordConcatenatedComponentRow[] wordConcatenatedComponentRows
				= this.wordRow.GetWordConcatenatedComponentRows();

			foreach (BabebiDataSet.WordConcatenatedComponentRow wordConcatenatedComponentRow in wordConcatenatedComponentRows)
			{
				WordConcatenated wordConcatenated = new WordConcatenated(wordConcatenatedComponentRow.WordConcatenatedRow);

				if (wordConcatenated.ComputeAndTryToSetUniqueKeyString())
				{
					wordConcatenated.PropagateToChildren();
				}
			}

			#endregion	on WordConcatenateds if any

			#region		on SentenceWords if any

			foreach (BabebiDataSet.SentenceWordRow sentenceWord in this.wordRow.GetSentenceWordRows())
			{ 
				Word.Compute_Sentence__String(sentenceWord.SentenceRow);
			}

			#endregion	on SentenceWords if any
		}

		internal static Word New(BabebiDataSet.WordRow wordRow)
		{ 
			BabebiDataSet dataSet = (BabebiDataSet)(wordRow.Table.DataSet);

			Word word;

			switch (wordRow.ModeCode)
			{
				case MainForm.ModeCode_Derivated:
				{
					BabebiDataSet.WordDerivatedRow wordDerivatedRow = dataSet.WordDerivated.FindById(wordRow.Id);

					word = new WordDerivated(wordDerivatedRow);

					break;
				}
				case MainForm.ModeCode_FromPattern:
				{
					BabebiDataSet.WordFromPatternRow wordFromPatternRow = dataSet.WordFromPattern.FindById(wordRow.Id);

					Pattern parent__Pattern = Pattern.New(wordFromPatternRow.FromPatternRowParent.PatternRow);

					word = new WordFromPattern(wordFromPatternRow, parent__Pattern);

					break;
				}
				case MainForm.ModeCode_ByInsertion:
				{
					BabebiDataSet.WordByInsertionRow wordByInsertionRow = dataSet.WordByInsertion.FindById(wordRow.Id);

					word = new WordByInsertion(wordByInsertionRow);

					break;
				}
				case MainForm.ModeCode_Concatenated:
				{
					BabebiDataSet.WordConcatenatedRow wordConcatenatedRow = dataSet.WordConcatenated.FindById(wordRow.Id);

					word = new WordConcatenated(wordConcatenatedRow);

					break;
				}
				case MainForm.ModeCode_NotDefined:
				{
					word = new WordWithoutMode(wordRow);

					break;
				}
				default: throw new Exception(wordRow.ModeCode);
			}

			return word;
		}

		internal static void SetUniqueKeyString(BabebiDataSet.WordRow wordRow, string uniqueKeyString)
		{
			wordRow.UniqueKeyString = uniqueKeyString;

			Word word = Word.New(wordRow);

			word.PropagateToChildren();
		}

		#region		static functions

		private		static string					compute_UniqueKeyString__(string priorString, string nextString, string echoingString = "")
		{
			return priorString + echoingString + nextString;
		}
		internal	static BabebiDataSet.LetterRow	Compute_UniqueKeyString__find_consonant_at_last_place(BabebiDataSet dataSet, string priorString, out int dueToDoubleCharConsonant)
		{
			if (priorString.Length >= 3)  /// 
			{
				string lastDoubleConsonantString = priorString.Substring(priorString.Length - 2, 2);

				BabebiDataSet.LetterRow lastDoubleConsonant = dataSet.Letter.FindByString(lastDoubleConsonantString);

				if (lastDoubleConsonant != null && lastDoubleConsonant.TypeCode == MainForm.TypeCode_Consonant)
				{
					dueToDoubleCharConsonant = 1;

					return lastDoubleConsonant;
				}
			}

			dueToDoubleCharConsonant = 0;

			string lastConsonantString = priorString.Substring(priorString.Length - 1, 1);

			BabebiDataSet.LetterRow lastConsonant = dataSet.Letter.FindByString(lastConsonantString);

			if (lastConsonant.TypeCode == MainForm.TypeCode_Consonant)
			{
				return lastConsonant;
			}
			else
			{ 
				return null;
			}
		}
		internal	static string					Compute_UniqueKeyString__echoing
		(
			BabebiDataSet	dataSet, 
			string			priorString, 
			string			nextString 
		)
		{ 
			BabebiDataSet.LetterRow letter;

			string first_consonant_string_of_next_String = nextString.Substring(0, 1);

			letter = dataSet.Letter.FindByString(first_consonant_string_of_next_String);

			if(letter != null && letter.TypeCode == MainForm.TypeCode_Consonant)
			{
				if (priorString.Length >= 2)		/// 
				{
					BabebiDataSet.LetterRow lastConsonant 
						= Compute_UniqueKeyString__find_consonant_at_last_place
						(
							dataSet, 
							priorString, 
					out int dueToDoubleCharConsonant
						);

					#region		(ancienne version)
					/*
					if (priorString.Length >= 3)  /// 
					{
						string lastDoubleConsonantString = priorString.Substring(priorString.Length - 2, 2);

						BabebiDataSet.LetterRow lastDoubleConsonant = dataSet.Letter.FindByString(lastDoubleConsonantString);

						if (lastDoubleConsonant != null && lastDoubleConsonant.TypeCode == MainForm.TypeCode_Consonant)
						{
							lastConsonant = lastDoubleConsonant;

							dueToDoubleCharConsonant = 1;
						}
					}
					if (lastConsonant == null)
					{
						string lastConsonantString = priorString.Substring(priorString.Length - 1, 1);

						lastConsonant = dataSet.Letter.FindByString(lastConsonantString);

						if (lastConsonant.TypeCode != MainForm.TypeCode_Consonant)
						{ 
							lastConsonant = null;
						}

						dueToDoubleCharConsonant = 0;
					}
					*/
					#endregion	(ancienne version)

					if (lastConsonant != null)
					{
						string lastVowel__2Chars__String = priorString.Substring(priorString.Length - 3  - dueToDoubleCharConsonant, 2);

						BabebiDataSet.LetterRow lastVowel__2Chars = dataSet.Letter.FindByString(lastVowel__2Chars__String);
						if (lastVowel__2Chars != null) 
						{
							if (lastVowel__2Chars.TypeCode == MainForm.TypeCode_Vowel)     /// pas 'nn' donc.)
							{
								return compute_UniqueKeyString__(priorString, nextString, lastVowel__2Chars__String);
							}
							else
							{
								return compute_UniqueKeyString__(priorString, nextString);
							}
						}

						string lastVowel__1Char__String = priorString.Substring(priorString.Length - 2 - dueToDoubleCharConsonant, 1);

						BabebiDataSet.LetterRow lastVowel__1Char = dataSet.Letter.FindByString(lastVowel__1Char__String);

						if (lastVowel__1Char != null && lastVowel__1Char.TypeCode == MainForm.TypeCode_Vowel)   
						{
							return compute_UniqueKeyString__(priorString, nextString, lastVowel__1Char__String);
						}
					}
				}
			}
			return compute_UniqueKeyString__(priorString, nextString);
		}

		internal static void Compute_Sentence__String(BabebiDataSet.SentenceRow sentence)
		{ 
			SortedDictionary<short, string> sortedDictionary = new SortedDictionary<short, string>();

			foreach (BabebiDataSet.SentenceWordRow sentenceWord in sentence.GetSentenceWordRows())
			{
				sortedDictionary.Add(sentenceWord.Index, sentenceWord.WordRow.UniqueKeyString);				
			}

			string sentence__string = "";

			foreach (string uniqueKeyString in sortedDictionary.Values)
			{
				sentence__string += (sentence__string == "" ? "" : " ") + uniqueKeyString;
			}

			sentence.String = sentence__string;
		}

		#endregion	static functions


		#region		getters & setters 

		protected override void			setUniqueKeyString(string computedUniqueKeyString)
		{
			this.wordRow.UniqueKeyString = computedUniqueKeyString;
		}
		protected override int			getId()
		{
			return this.wordRow.Id;
		}
		protected override DataTable	getDataTable()
		{
			return this.dataSet.Word;
		}

		#endregion	getters & setters 
	}

	internal class			WordDerivated : Word	
	{
		private BabebiDataSet.WordDerivatedRow wordDerivatedRow;

		public WordDerivated(BabebiDataSet.WordDerivatedRow wordDerivatedRow) : base(wordDerivatedRow.WordRowParent)
		{
			this.wordDerivatedRow = wordDerivatedRow;
		}

		protected	override string	computeUniqueKeyString()
		{
			return Compute_UniqueKeyString
				(
					wordDerivatedRow.WordDerivatingRowParent, 
					wordDerivatedRow.DerivatingRowParent
				);
		}
		internal	override void	PropagateToChildren()
		{ 
			base.PropagateToChildren();

			/// no child.
		}


		#region		(ancienne version)
		/*
		private static string compute_UniqueKeyString__simple(BabebiDataSet.WordDerivatingRow wordDerivating, BabebiDataSet.DerivatingRow derivating)
		{
			return wordDerivating.WordUniqueKeyString + derivating.LetterClusterString;
		}
		private static string compute_UniqueKeyString__echoing(BabebiDataSet.WordDerivatingRow wordDerivating, BabebiDataSet.DerivatingRow derivating, string echoedVowel)
		{
			return wordDerivating.WordUniqueKeyString + echoedVowel + derivating.LetterClusterString;
		}
		*/
		#endregion	(ancienne version)
		internal static string	Compute_UniqueKeyString(BabebiDataSet.WordDerivatingRow wordDerivating, BabebiDataSet.DerivatingRow derivating)
		{
			if (derivating.AppendStringAtLast)
			{
				return Compute_UniqueKeyString__echoing
				(
					(BabebiDataSet)(wordDerivating.Table.DataSet), 
					wordDerivating.WordUniqueKeyString, 
					derivating.LetterClusterString 
				);

				#region	(ancienne version)
				/*
				BabebiDataSet dataSet = (BabebiDataSet)(wordDerivating.Table.DataSet);

				BabebiDataSet.LetterRow letter;

				string first_consonant_string_of_Derivating__LetterClusterString = derivating.LetterClusterString.Substring(0, 1);
				letter = dataSet.Letter.FindByString(first_consonant_string_of_Derivating__LetterClusterString);
				if(letter != null && letter.TypeCode == MainForm.TypeCode_Consonant)
				{
					if (wordDerivating.WordUniqueKeyString.Length > 2)		/// je ne comprends pas pourquoi 2 et pas 1
					{
						//BabebiDataSet.LetterRow lastConsonant 
						//	= compute_UniqueKeyString__last_consonant_of_prior_string
						//	(
						//		dataSet, 
						//		wordDerivating.WordUniqueKeyString, 
						//	out int dueToDoubleCharConsonant
						//	);

						BabebiDataSet.LetterRow lastConsonant = null;
						if (wordDerivating.WordUniqueKeyString.Length > 3)  /// je ne comprends pas pourquoi 3 et pas 2
						{
							string lastDoubleConsonantString = wordDerivating.WordUniqueKeyString.Substring(wordDerivating.WordUniqueKeyString.Length - 2, 2);
							BabebiDataSet.LetterRow lastDoubleConsonant = dataSet.Letter.FindByString(lastDoubleConsonantString);
							if (lastDoubleConsonant != null && lastDoubleConsonant.TypeCode == MainForm.TypeCode_Consonant)
							{
								lastConsonant = lastDoubleConsonant;

								dueToDoubleCharConsonant = 1;
							}
						}
						if (lastConsonant == null)
						{
							string lastConsonantString 
								= wordDerivating.WordUniqueKeyString
								.Substring(wordDerivating.WordUniqueKeyString.Length - 1, 1);

							lastConsonant = dataSet.Letter.FindByString(lastConsonantString);
						}

						if (lastConsonant != null)
						{
							string lastVowel__2Chars__String 
								= wordDerivating.WordUniqueKeyString
								.Substring(wordDerivating.WordUniqueKeyString.Length - 3  - dueToDoubleCharConsonant, 2);

							BabebiDataSet.LetterRow lastVowel__2Chars = dataSet.Letter.FindByString(lastVowel__2Chars__String);
							if (lastVowel__2Chars != null) 
							{
								if (lastVowel__2Chars.TypeCode == MainForm.TypeCode_Vowel)     /// pas 'nn' donc.)
								{
									return compute_UniqueKeyString__(wordDerivating.WordUniqueKeyString, derivating.LetterClusterString, lastVowel__2Chars__String);
								///	return compute_UniqueKeyString__echoing( wordDerivating, derivating, lastVowel__2Chars__String);
								}
								else
								{
									return compute_UniqueKeyString__(wordDerivating.WordUniqueKeyString, derivating.LetterClusterString);
								///	return compute_UniqueKeyString__simple( wordDerivating, derivating);
								}
							}

							string lastVowel__1Char__String 
								= wordDerivating.WordUniqueKeyString
								.Substring(wordDerivating.WordUniqueKeyString.Length - 2 - dueToDoubleCharConsonant, 1);

							BabebiDataSet.LetterRow lastVowel__1Char = dataSet.Letter.FindByString(lastVowel__1Char__String);
							if (lastVowel__1Char != null && lastVowel__1Char.TypeCode == MainForm.TypeCode_Vowel)   
							{
								return compute_UniqueKeyString__(wordDerivating.WordUniqueKeyString, derivating.LetterClusterString, lastVowel__1Char__String);
							///	return compute_UniqueKeyString__echoing(wordDerivating, derivating, lastVowel__1Char__String);
							}
						}
					}
				}
				return compute_UniqueKeyString__(wordDerivating.WordUniqueKeyString, derivating.LetterClusterString);
			///	return compute_UniqueKeyString__simple( wordDerivating, derivating );
				*/
				#endregion	(ancienne version)
			}
			else /// if(AppendStringAtLast == false)
			{ 
				return derivating.LetterClusterString + wordDerivating.WordUniqueKeyString;	
			}
		}

		internal static void	DerivatingChanging(BabebiDataSet.DerivatingRow derivatingRow)
		{
			BabebiDataSet.WordDerivatedRow[] wordDerivatedRows = derivatingRow.GetWordDerivatedRows();

			foreach (BabebiDataSet.WordDerivatedRow wordDerivatedRow in wordDerivatedRows)
			{
				WordDerivated wordDerivated = new WordDerivated(wordDerivatedRow);

				if (wordDerivated.ComputeAndTryToSetUniqueKeyString())
				{
					wordDerivated.PropagateToChildren();
				}
			}
		}
	}
	internal class			WordByInsertion : Word	
	{
		private BabebiDataSet.WordByInsertionRow wordByInsertionRow;

		public WordByInsertion(BabebiDataSet.WordByInsertionRow wordByInsertionRow) : base(wordByInsertionRow.WordRowParentByWordByInsertion__Word)
		{
			this.wordByInsertionRow = wordByInsertionRow;
		}

		protected override string	computeUniqueKeyString()
		{
			return Compute_UniqueKeyString
				(
					wordByInsertionRow.WordRowByWordByInsertion__Word__Origin, 
					wordByInsertionRow.InsertionIndex,
					wordByInsertionRow.LetterClusterRow.String
				);
		}
		internal override void	PropagateToChildren()
		{ 
			base.PropagateToChildren();

			/// no child.
		}

		internal static string	Compute_UniqueKeyString(BabebiDataSet.WordRow wordRow__origin, short insertionIndex, string letterClusterString)
		{
			string uniqueKeyString;

			BabebiDataSet dataSet = (BabebiDataSet)(wordRow__origin.Table.DataSet);

			string parentWordString = wordRow__origin.UniqueKeyString;

			if (insertionIndex == 0)
			{
				uniqueKeyString
					= letterClusterString
					+ parentWordString;
			}
			else if (insertionIndex == 32)
			{
				uniqueKeyString
					= parentWordString
					+ letterClusterString;
			}
			else
			{
				/// On ne peut pas faire ce qui est écrit dans les lignes juste ci-dessous en commentaires
				/// car cela ne marcherait pas pour les double-char Letters 'aa', 'ee', 'oo', 'uu', et 'nn'.
				/// 
				/// string parentWordString_1 = parentWordString.Substring(0, insertionIndex);
				/// string parentWordString_2 = parentWordString.Substring(insertionIndex);
				/// 

				uniqueKeyString = "";

				int charIndex = -1;

				/// On compte les chars du parent Word depuis le début, ...
				for (int i = 0; i < insertionIndex; i++)
				{
					charIndex++;

					char thisChar = parentWordString[charIndex];

					if (charIndex < parentWordString.Length - 1)
					{
						char nextChar = parentWordString[charIndex + 1];

						string candidateDoubleCharLetterString = new string(new char[] { thisChar, nextChar });

						BabebiDataSet.LetterRow candidateDoubleCharLetter 
							= dataSet.Letter.FindByString(candidateDoubleCharLetterString);

						if (candidateDoubleCharLetter == null)
						{
							uniqueKeyString += thisChar;
						}
						else
						{
							uniqueKeyString += candidateDoubleCharLetterString;

							charIndex++;
						}
					}
					else
					{
						uniqueKeyString += thisChar;
					}
				}

				/// ... on rajoute le cluster, ...
				uniqueKeyString += letterClusterString;

				charIndex++;

				/// ... puis le reste du Word parent.
				uniqueKeyString += parentWordString.Substring(charIndex);
			}

			return uniqueKeyString;
		}
	}
	internal class			WordConcatenated : Word	
	{
		private BabebiDataSet.WordConcatenatedRow wordConcatenatedRow;

		public WordConcatenated(BabebiDataSet.WordConcatenatedRow wordConcatenatedRow) : base(wordConcatenatedRow.WordRowParent)
		{
			this.wordConcatenatedRow = wordConcatenatedRow;
		}

		protected override string computeUniqueKeyString()
		{
			return Compute_UniqueKeyString(wordConcatenatedRow);
		}
		internal override void	PropagateToChildren()
		{ 
			base.PropagateToChildren();

			/// no child.
		}

		internal static string	Compute_UniqueKeyString(BabebiDataSet.WordConcatenatedRow wordConcatenatedRow)
		{
			string uniqueKeyString = "";

			SortedDictionary<short, BabebiDataSet.WordConcatenatedComponentRow> sortedDictionary
				= new SortedDictionary<short, BabebiDataSet.WordConcatenatedComponentRow>();

			foreach
			(
				BabebiDataSet.WordConcatenatedComponentRow wordConcatenatedComponent
				in
				wordConcatenatedRow.GetWordConcatenatedComponentRows()
			)
			{
				sortedDictionary.Add(wordConcatenatedComponent.Rank, wordConcatenatedComponent);
			}

			foreach(KeyValuePair<short, BabebiDataSet.WordConcatenatedComponentRow> wordConcatenatedComponentRow in sortedDictionary)
			{ 
				BabebiDataSet.WordConcatenatedComponentRow wordConcatenatedComponent = wordConcatenatedComponentRow.Value;

				Read_SubString_Index_and_Length_from_DataRow
				(
					wordConcatenatedComponent, 
			out int index, 
			out int length
				);			

				string subString = Compute__SubString(wordConcatenatedComponent.WordRow.UniqueKeyString, index, length);

				uniqueKeyString 
					= Compute_UniqueKeyString__echoing
					(
						(BabebiDataSet)(wordConcatenatedRow.Table.DataSet), 
						uniqueKeyString, 
						subString
					);

			///	uniqueKeyString += subString;
			}

			return uniqueKeyString;
		}

		/// Tool
		/// 
		/// Cette methode parait bizarre MAIS elle est très utile pour juste extraire (index + length) pour en fabriquer 
		/// une SubStringInformation utilisée dans les 2 combos de la Forme Form__WordConcatenatedComponent__Create. 
		internal static void	Read_SubString_Index_and_Length_from_DataRow
		(
			BabebiDataSet.WordConcatenatedComponentRow wordConcatenatedComponent, 
		out int index, 
		out int length
		)
		{
			if (wordConcatenatedComponent.IsSubStringIndexNull())
			{
				index	= 0;
			}
			else
			{ 
				index	= wordConcatenatedComponent.SubStringIndex;
			}

			if (wordConcatenatedComponent.IsSubStringLengthNull())
			{
				length	= 0;
			}
			else
			{ 
				length	= wordConcatenatedComponent.SubStringLength;
			}
		}

		/// Tool
		internal static string Compute__SubString(string componantWordUniqueKeyString, int index, int length)
		{
			if(index == 0 && length == 0)
			{
				return componantWordUniqueKeyString;
			}
			else
			{
				if (length == 0)
				{
					return  componantWordUniqueKeyString.Substring(index);
				}
				else
				{ 
					return componantWordUniqueKeyString.Substring(index, length);
				}
			}
		}
	}
	internal class			WordFromPattern : Word, _FromPattern	
	{
		private BabebiDataSet.WordFromPatternRow	wordFromPatternRow;

		private BabebiDataSet.FromPatternRow					fromPatternRow;
				BabebiDataSet.FromPatternRow _FromPattern.		FromPatternRow
		{
			get { return this.fromPatternRow;}
		}

		private Pattern											pattern;
				Pattern _FromPattern.							Pattern
				{
					get { return this.pattern;}
				}

		private SortedDictionary<short, Letter>					ranks__letters;
				SortedDictionary<short, Letter> _FromPattern.	Ranks__Letters
				{
					get { return	this.ranks__letters;}
					set {			this.ranks__letters = value;}
				}

		public WordFromPattern(BabebiDataSet.WordFromPatternRow wordFromPatternRow, Pattern pattern) : base(wordFromPatternRow.WordRowParent)
		{
			this.wordFromPatternRow = wordFromPatternRow;

			this.fromPatternRow = this.wordFromPatternRow.FromPatternRowParent;

			this.pattern = pattern;

			FromPattern_.Compute__ranks__letters(this);
		}

		protected override string computeUniqueKeyString()
		{
			/// Word  et Pattern n'ont pas la même fonction Compute_UniqueKeyString car, dans un Word, on a forcément :
			/// 
			/// ranks__letters.Count = ranks__symbols.Count
			/// 
			/// Et donc, le calcul de la UKS est plus simple.
			///

			return WordFromPattern.Compute_UniqueKeyString
			(
				this.pattern.ParentPatternSubStrings,
				this.ranks__letters
			);
		}

		internal static string	Compute_UniqueKeyString
		(
			List<string>					parentPatternSubStrings,
			SortedDictionary<short, Letter>	ranks__letters
		)
		{
			string uniqueKeyString = parentPatternSubStrings[0];

			foreach (KeyValuePair<short, Letter> rank__letter in ranks__letters)
			{
				short	rank	= rank__letter.Key;
				Letter	letter	= rank__letter.Value;
			
				uniqueKeyString += ranks__letters[rank].String;

				uniqueKeyString += parentPatternSubStrings[rank];	/// lui, étant une liste, n'est pas 1-based.
			}

			return uniqueKeyString;
		}




		internal override void	PropagateToChildren()
		{ 
			base.PropagateToChildren();

			/// no child.
		}

	}
	internal class			WordWithoutMode : Word
	{
		public WordWithoutMode(BabebiDataSet.WordRow wordRow) : base(wordRow)
		{
		}

		protected	override string	computeUniqueKeyString()
		{
			return base.wordRow.UniqueKeyString;
		}
		internal	override void	PropagateToChildren()
		{ 
			base.PropagateToChildren();
		}
	}

	internal abstract class Pattern : WordOrPattern	
	{
		protected	BabebiDataSet.PatternRow patternRow;

		protected	List<BabebiDataSet.LetterOrSymbolRow>		letterOrSymbolRows;

		internal	Dictionary<short, BabebiDataSet.SymbolRow>	Ranks__Symbols;
		internal	List<string>								ParentPatternSubStrings;

		internal Pattern(BabebiDataSet.PatternRow patternRow) : base(patternRow.WordOrPatternRowParent)
		{
			this.patternRow = patternRow;

			Analyse__Pattern__UniqueKeyString
			(
															this.patternRow, 
			out List<BabebiDataSet.LetterOrSymbolRow>		letterOrSymbolRows_,
			out Dictionary<short, BabebiDataSet.SymbolRow>	ranks__symbols_,
			out List<string>								parentPatternSubStrings
			);

			this.letterOrSymbolRows			= letterOrSymbolRows_;
			this.Ranks__Symbols				= ranks__symbols_;

			this.ParentPatternSubStrings	= parentPatternSubStrings;
		}

		internal static Pattern New(BabebiDataSet.PatternRow patternRow)
		{
			BabebiDataSet dataSet = (BabebiDataSet)(patternRow.Table.DataSet);

			Pattern pattern;

			switch (patternRow.ModeCode)
			{
				case MainForm.ModeCode_ExNihilo:
				{
					BabebiDataSet.PatternExNihiloRow patternExNihiloRow = dataSet.PatternExNihilo.FindById(patternRow.Id);

					pattern = new PatternExNihilo(patternExNihiloRow);

					break;
				}
				case MainForm.ModeCode_FromPattern:
				{
					BabebiDataSet.PatternFromPatternRow patternFromPatternRow = dataSet.PatternFromPattern.FindById(patternRow.Id);

					Pattern parent__pattern = New(patternFromPatternRow.FromPatternRowParent.PatternRow);

					pattern = new PatternFromPattern(patternFromPatternRow, parent__pattern);

					break;
				}
				case MainForm.ModeCode_ByInsertion:
				{
					BabebiDataSet.PatternByInsertionRow patternByInsertionRow = dataSet.PatternByInsertion.FindById(patternRow.Id);

					pattern = new PatternByInsertion(patternByInsertionRow);

					break;
				}
				case MainForm.ModeCode_NotDefined:
				{
					pattern = new PatternWithoutMode(patternRow);

					break;
				}
				default: throw new Exception(patternRow.ModeCode);
			}

			return pattern;
		}

		internal override void PropagateToChildren()
		{ 
			base.PropagateToChildren();

			#region		on FromPatterns

			BabebiDataSet.FromPatternRow[] fromPatternRows =  patternRow.GetFromPatternRows();

			foreach (BabebiDataSet.FromPatternRow fromPatternRow in fromPatternRows)
			{
				WordOrPattern wordOrPattern;
				switch (fromPatternRow.SubscriberCode)
				{
					case MainForm.SubscriberCode_Word :
					{
						BabebiDataSet.WordFromPatternRow wordFromPatternRow	
							= this.dataSet.WordFromPattern
							.FindById(fromPatternRow.SubscriberId);

						wordOrPattern = new WordFromPattern(wordFromPatternRow, this);

						break;
					}
					case MainForm.SubscriberCode_Pattern :
					{
						BabebiDataSet.PatternFromPatternRow patternFromPatternRow	
							= this.dataSet.PatternFromPattern
							.FindById(fromPatternRow.SubscriberId);

						wordOrPattern = new PatternFromPattern(patternFromPatternRow, this);

						break;
					}
					default: { throw new System.Exception(fromPatternRow.SubscriberCode); }
				}


				if (wordOrPattern.ComputeAndTryToSetUniqueKeyString())
				{
					wordOrPattern.PropagateToChildren();
				}
			}

			#endregion	on FromPatterns
		}

		internal static void SetUniqueKeyString(BabebiDataSet.PatternRow patternRow, string uniqueKeyString)
		{
			patternRow.UniqueKeyString = uniqueKeyString;

			Pattern pattern = New(patternRow);

			pattern.PropagateToChildren();
		}

		/// <returns>false if a character is not a valid Letter or Symbol.</returns>
		internal static bool Analyse__Pattern__UniqueKeyString
		(
			BabebiDataSet.PatternRow						pattern,
			out List<BabebiDataSet.LetterOrSymbolRow>		letterOrSymbolRows,
			out Dictionary<short, BabebiDataSet.SymbolRow>	ranks__symbols,
			out List<string>								patternSubRemainingStrings
		)
		{ 
			BabebiDataSet							dataSet					= (BabebiDataSet)(pattern.Table.DataSet);
			BabebiDataSet.LetterOrSymbolDataTable	table__LetterOrSymbol	= dataSet.LetterOrSymbol;
			BabebiDataSet.LetterDataTable			table__Letter			= dataSet.Letter;
			BabebiDataSet.SymbolDataTable			table__Symbol			= dataSet.Symbol;

			letterOrSymbolRows			= new List<BabebiDataSet.LetterOrSymbolRow>();
			ranks__symbols				= new Dictionary<short, BabebiDataSet.SymbolRow>();
			patternSubRemainingStrings	= new List<string>();

			/// 'rank' n'est pas la position / l'index dans la string.
			/// c'est bien le quantième symbole du pattern. 
			/// le chiffre est 1-based.
			short rank = 0;	
			
			string parentPatternSubString = "";
			for (int i = 0; i < pattern.UniqueKeyString.Length; i++)
			{
				char	firstChar		= pattern.UniqueKeyString[i];
				string	firstCharString = new string(new char[]{ firstChar } );

				/// les Symboles n'ont qu'un seul caractère.
				BabebiDataSet.SymbolRow symbol 
					= table__Symbol.FindByString(firstCharString);

				if (symbol != null)
				{
					#region		symbol

					rank++;

					ranks__symbols.Add(rank, symbol);

					patternSubRemainingStrings.Add(parentPatternSubString);

					parentPatternSubString = "";

					letterOrSymbolRows.Add(symbol.LetterOrSymbolRowParent);

					#endregion	symbol
				}
				else
				{ 
					#region		Letter

					#region		2-char Letter?

					if (i < pattern.UniqueKeyString.Length - 1)
					{
						char secondChar = pattern.UniqueKeyString[i + 1];

						string doubleCharLetter = new string(new char[] { firstChar, secondChar });

						BabebiDataSet.LetterRow doubleCharLetterRow
							= table__Letter.FindByString(doubleCharLetter);

						if (doubleCharLetterRow != null)
						{
							letterOrSymbolRows.Add(doubleCharLetterRow.LetterOrSymbolRowParent);

							i++;

							parentPatternSubString += doubleCharLetter;

							continue;
						}
					}

					#endregion	2-char Letter?
					#region		1-char Letter

					BabebiDataSet.LetterRow monoCharLetterRow
						= table__Letter.FindByString(firstCharString);

					if (monoCharLetterRow == null)
					{ 
						return false;
					}

					letterOrSymbolRows.Add(monoCharLetterRow.LetterOrSymbolRowParent);

					parentPatternSubString += firstCharString;

					#endregion	1-char Letter

					#endregion	Letter
				}
			}

			patternSubRemainingStrings.Add(parentPatternSubString);

			return true;
		}

		/// <summary>
		/// return false if a character is not a valid babebi Letter.
		/// </summary>
		internal static string Split__UniqueKeyString__into__LetterOrSymbols
		(
			BabebiDataSet.PatternRow				pattern, 
			List<BabebiDataSet.LetterOrSymbolRow>	letterOrSymbolRowList
		)
		{
			string pattern__UniqueKeyString = pattern.UniqueKeyString;
			
			BabebiDataSet							dataSet					= (BabebiDataSet)(pattern.Table.DataSet);
			BabebiDataSet.LetterDataTable			table__Letter			= dataSet.Letter;
			BabebiDataSet.SymbolDataTable			table__Symbol			= dataSet.Symbol;

			for (int i = 0; i < pattern__UniqueKeyString.Length; i++)
			{
				char firstChar = pattern__UniqueKeyString[i];

				string monoCharLetter = new string(new char[] { firstChar });

				BabebiDataSet.SymbolRow symbol = table__Symbol.FindByString(monoCharLetter);

				if (symbol != null)
				{
					letterOrSymbolRowList.Add(symbol.LetterOrSymbolRowParent);
				}
				else
				{
					if (i < pattern__UniqueKeyString.Length - 1)
					{
						char secondChar = pattern__UniqueKeyString[i + 1];

						string doubleCharLetterOrSymbol = new string(new char[] { firstChar, secondChar });

						BabebiDataSet.LetterRow doubleCharLetterRow
							= table__Letter.FindByString(doubleCharLetterOrSymbol);

						if (doubleCharLetterRow != null)
						{
							letterOrSymbolRowList.Add(doubleCharLetterRow.LetterOrSymbolRowParent);

							i++;

							continue;
						}
					}

					BabebiDataSet.LetterRow monoCharLetterRow
						= table__Letter.FindByString(monoCharLetter);

					if (monoCharLetterRow == null)
					{
						return monoCharLetter;	/// invalid character.
					}

					letterOrSymbolRowList.Add(monoCharLetterRow.LetterOrSymbolRowParent);
				}
			}

			return null;	/// no invalid character.
		}

		#region		getters & setters 

		protected override void			setUniqueKeyString(string computedUniqueKeyString)
		{
			this.patternRow.UniqueKeyString = computedUniqueKeyString;

			#region		where the analyse of the Pattern UKS is performed

			Analyse__Pattern__UniqueKeyString
			(
															this.patternRow, 
			out List<BabebiDataSet.LetterOrSymbolRow>		letterOrSymbolRows_,
			out Dictionary<short, BabebiDataSet.SymbolRow>	ranks__symbols_,
			out List<string>								parentPatternSubStrings
			);

			this.letterOrSymbolRows			= letterOrSymbolRows_;
			this.Ranks__Symbols				= ranks__symbols_;
			this.ParentPatternSubStrings	= parentPatternSubStrings;

			#endregion	where the analyse of the Pattern UKS is performed
		}
		protected override int			getId()
		{
			return this.patternRow.Id;
		}
		protected override DataTable	getDataTable()
		{
			return this.dataSet.Pattern;
		}

		#endregion	getters & setters 
	}

	internal class			PatternExNihilo  : Pattern
	{
		private BabebiDataSet.PatternExNihiloRow patternExNihiloRow;

		public PatternExNihilo(BabebiDataSet.PatternExNihiloRow patternExNihiloRow) : base(patternExNihiloRow.PatternRowParent)
		{
			this.patternExNihiloRow = patternExNihiloRow;
		}

		protected	override string	computeUniqueKeyString()
		{
			return this.patternExNihiloRow.PatternRowParent.UniqueKeyString;
		}
		internal	override void	PropagateToChildren()
		{ 
			base.PropagateToChildren();

			/// no child.
		}

		/// NO : internal static string	Compute_UniqueKeyString()
	}
	internal class			PatternFromPattern : Pattern, _FromPattern	
	{
		private BabebiDataSet.PatternFromPatternRow	patternFromPatternRow;

		private BabebiDataSet.FromPatternRow					fromPatternRow;
				BabebiDataSet.FromPatternRow _FromPattern.		FromPatternRow
		{
			get { return this.fromPatternRow;}
		}

		private Pattern											pattern;
				Pattern _FromPattern.							Pattern
				{
					get { return this.pattern;}
				}

		private SortedDictionary<short, Letter>					ranks__letters;
				SortedDictionary<short, Letter> _FromPattern.	Ranks__Letters
				{
					get { return	this.ranks__letters;}
					set {			this.ranks__letters = value;}
				}

		public PatternFromPattern(BabebiDataSet.PatternFromPatternRow patternFromPatternRow, Pattern parent__pattern) : base(patternFromPatternRow.PatternRowParent)
		{
			this.patternFromPatternRow = patternFromPatternRow;

			this.fromPatternRow = this.patternFromPatternRow.FromPatternRowParent;

			this.pattern = parent__pattern;

			FromPattern_.Compute__ranks__letters(this);
		}

		protected override string computeUniqueKeyString()
		{
			return PatternFromPattern.Compute_UniqueKeyString
			(
				this.pattern.ParentPatternSubStrings,
				this.pattern.Ranks__Symbols,
				this.ranks__letters
			);
		}

		internal static string	Compute_UniqueKeyString
		(
			List<string>								parentPatternSubStrings,
			Dictionary<short, BabebiDataSet.SymbolRow>	ranks__symbols,
			SortedDictionary<short, Letter>				ranks__letters
		)
		{
			string uniqueKeyString = parentPatternSubStrings[0];

			foreach (KeyValuePair<short, BabebiDataSet.SymbolRow> rank__symbol in ranks__symbols)
			{
				short					rank	= rank__symbol.Key;
				BabebiDataSet.SymbolRow	symbol	= rank__symbol.Value;
			
				if (ranks__letters.Keys.Contains(rank))
				{
					uniqueKeyString += ranks__letters[rank].String;
				}
				else
				{
					uniqueKeyString += symbol.String;
				}

				uniqueKeyString += parentPatternSubStrings[rank];	/// lui, étant une liste, n'est pas 1-based.
			}

			return uniqueKeyString;
		}

		internal override void	PropagateToChildren()
		{ 
			base.PropagateToChildren();

			/// no child.
		}

	}
	internal class			PatternByInsertion : Pattern	
	{
		private BabebiDataSet.PatternByInsertionRow patternByInsertionRow;

		public PatternByInsertion(BabebiDataSet.PatternByInsertionRow patternByInsertionRow) : base(patternByInsertionRow.PatternRowParent)
		{
			this.patternByInsertionRow = patternByInsertionRow;
		}

		protected override string	computeUniqueKeyString()
		{
			return Compute_UniqueKeyString
				(
					patternByInsertionRow.WordOrPatternRowParent, 
					patternByInsertionRow.InsertionIndex,
					patternByInsertionRow.LetterOrSymbolClusterRow.String
				);
		}
		internal override void	PropagateToChildren()
		{ 
			base.PropagateToChildren();

			/// no child.
		}

		/// on ne peut pas avoir une version sans l'argument 'wordOrPatternRow' car 
		/// on ne connait pas "sa" UKS : il faut nécessairement la rechercher. 
		internal static string	Compute_UniqueKeyString(BabebiDataSet.WordOrPatternRow originWordOrPatternRow, short insertionIndex, string letterOrSymbolClusterString)
		{
			string uniqueKeyString;

			BabebiDataSet dataSet = (BabebiDataSet)(originWordOrPatternRow.Table.DataSet);

			string origin__WordOrPattern__UniqueKeyString;
			if (originWordOrPatternRow.SubscriberCode == MainForm.SubscriberCode_Word)
			{
				BabebiDataSet.WordRow wordRow
					= dataSet.Word.FindById(originWordOrPatternRow.SubscriberId);

				origin__WordOrPattern__UniqueKeyString = wordRow.UniqueKeyString;
			}
			else
			{ 
				BabebiDataSet.PatternRow patternRow
					= dataSet.Pattern.FindById(originWordOrPatternRow.SubscriberId);

				origin__WordOrPattern__UniqueKeyString = patternRow.UniqueKeyString;
			}

			if (insertionIndex == 0)
			{
				uniqueKeyString
					= letterOrSymbolClusterString
					+ origin__WordOrPattern__UniqueKeyString;
			}
			else if (insertionIndex == 32)
			{
				uniqueKeyString
					= origin__WordOrPattern__UniqueKeyString
					+ letterOrSymbolClusterString;
			}
			else
			{
				/// On ne peut pas faire ce qui est écrit dans les lignes juste ci-dessous en commentaires
				/// car cela ne marcherait pas pour les double-char Letters 'aa', 'ee', 'oo', 'uu', et 'nn'.
				/// 
				/// string parentPatternString_1 = parentPatternString.Substring(0, insertionIndex);
				/// string parentPatternString_2 = parentPatternString.Substring(insertionIndex);
				/// 

				uniqueKeyString = "";

				int charIndex = -1;

				/// On compte les chars du parent Pattern depuis le début, ...
				for (int i = 0; i < insertionIndex; i++)
				{
					charIndex++;

					char thisChar = origin__WordOrPattern__UniqueKeyString[charIndex];

					if (charIndex < origin__WordOrPattern__UniqueKeyString.Length - 1)
					{
						char nextChar = origin__WordOrPattern__UniqueKeyString[charIndex + 1];

						string candidateDoubleCharLetterOrSymbolString = new string(new char[] { thisChar, nextChar });

						BabebiDataSet.LetterOrSymbolRow candidateDoubleCharLetterOrSymbolRow 
							= dataSet.LetterOrSymbol.FindByString(candidateDoubleCharLetterOrSymbolString);

						if (candidateDoubleCharLetterOrSymbolRow == null)
						{
							uniqueKeyString += thisChar;
						}
						else
						{
							uniqueKeyString += candidateDoubleCharLetterOrSymbolString;

							charIndex++;
						}
					}
					else
					{
						uniqueKeyString += thisChar;
					}
				}

				/// ... on rajoute le cluster, ...
				uniqueKeyString += letterOrSymbolClusterString;

				charIndex++;

				/// ... puis le reste du Pattern parent.
				uniqueKeyString += origin__WordOrPattern__UniqueKeyString.Substring(charIndex);
			}

			return uniqueKeyString;
		}
	}
	internal class			PatternWithoutMode : Pattern	
	{
		public PatternWithoutMode(BabebiDataSet.PatternRow patternRow) : base(patternRow)
		{
		}

		protected override string	computeUniqueKeyString()
		{
			return base.patternRow.UniqueKeyString;
		}
		internal override void	PropagateToChildren()
		{ 
			base.PropagateToChildren();
		}
	}

	internal interface		_FromPattern
	{
		BabebiDataSet.FromPatternRow FromPatternRow { get; }

		Pattern Pattern { get; }

		SortedDictionary<short, Letter>	Ranks__Letters { get; set; }
	}
	internal static class	FromPattern_
	{
		internal static void Compute__ranks__letters(_FromPattern _fromPattern)
		{
			_fromPattern.Ranks__Letters = Build__ranks__letters__from__FromPatternLetters(_fromPattern.FromPatternRow);
		}

		internal static SortedDictionary<short, Letter>	 Build__ranks__letters__from__FromPatternLetters(BabebiDataSet.FromPatternRow fromPatternRow)
		{
			SortedDictionary<short, Letter>	ranks__letters = new SortedDictionary<short, Letter>();

			BabebiDataSet.FromPatternLetterRow[] fromPatternLetterRows = fromPatternRow.GetFromPatternLetterRows();
			foreach (BabebiDataSet.FromPatternLetterRow fromPatternLetterRow in fromPatternLetterRows)
			{
				/// Letter est une enum.
				Letter letter 
					= new Letter
					(
						fromPatternLetterRow.LetterRowParent.TypeCode,
						fromPatternLetterRow.LetterRowParent.String
					);

				ranks__letters.Add(fromPatternLetterRow.Rank, letter);
			}

			return ranks__letters;
		}

	}
}
