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

namespace Babebi4
{
	public partial class Form__WordFromPattern__of__Current__Pattern__CreateUpdate : Form
	{

		private MainForm							mainForm;
		private bool								trueForCreate__falseForUpdate;

		#region		short-cuts

		private BabebiDataSet						dataSet;
		private BabebiDataSet.PatternRow			current__Pattern;
		private BabebiDataSet.Pattern_LanguageRow	current__Pattern_Language;

		private BabebiDataSet.WordOrPatternRow		current__WordOrPattern		
		{
			get { return	this.mainForm.Current__WordOrPattern__of__WordFromPattern__of__Current__Pattern; }
			set {			this.mainForm.Current__WordOrPattern__of__WordFromPattern__of__Current__Pattern = value; }
		}
		private BabebiDataSet.WordRow				current__Word				
		{
			get { return	this.mainForm.Current__Word__of__WordFromPattern__of__Current__Pattern; }
			set {			this.mainForm.Current__Word__of__WordFromPattern__of__Current__Pattern = value; }
		}
		private BabebiDataSet.Word_LanguageRow		current__Word_Language		
		{
			get { return	this.mainForm.Current__Word_Language__of__WordFromPattern__of__Current__Pattern; }
			set {			this.mainForm.Current__Word_Language__of__WordFromPattern__of__Current__Pattern = value; }
		}
		private BabebiDataSet.WordFromPatternRow	current__WordFromPattern	
		{
			get { return	this.mainForm.Current__WordFromPattern__of__Current__Pattern; }
			set {			this.mainForm.Current__WordFromPattern__of__Current__Pattern = value; }
		}
		private BabebiDataSet.FromPatternRow		current__FromPattern		
		{
			get { return	this.mainForm.Current__FromPattern__of__WordFromPattern__of__Current__Pattern; }
			set {			this.mainForm.Current__FromPattern__of__WordFromPattern__of__Current__Pattern = value; }
		}

		#endregion	short-cuts
		#region		gui table : FromPatternLetter

		private DataSet guiDataSet;

		private DataTable guiTable__FromPatternLetter;

		private DataColumn guiColumn__FromPatternLetter__Rank;
		private DataColumn guiColumn__FromPatternLetter__LetterTypeCode;
		private DataColumn guiColumn__FromPatternLetter__LetterString;

		private BindingSource bindingSource__FromPatternLetter;

		#endregion	gui table : FromPatternLetter
		#region		variables

		private int word__Id = -1;

		private short selected__GuiHorizontalShift = -1; 	/// means 'undefined'

		private BabebiDataSet.WordType_LanguageRow selected__WordType_Language = null;

		private Dictionary<short, BabebiDataSet.SymbolRow>	ranks__symbols;	/// n'a pas besoin d'être 'Sorted'Dictionary car construit dans le bon ordre. 
		private List<string>								pattern_Remaining_SubStrings;

		/// doit être 'sorted' car construit à partir des rows du DataSet qui pourraient être mal triées.
		private SortedDictionary<short, Letter> ranks__letters;

		#endregion	variables
		#region		error management

		private TextBox_WEM		textBox_WEM__Id					;
		private TextBox_WEM		textBox_WEM__UniqueKeyString	;
		private TextBox_WEM		textBox_WEM__Meaning			;
		private ComboBox_WEM	comboBox_WEM__WordType			;
		private	ComboBox_WEM	comboBox_WEM__GuiHorizontalShift;

		#endregion	error management

		#region		ctors

		public Form__WordFromPattern__of__Current__Pattern__CreateUpdate(MainForm mainForm, bool trueForCreate__falseForUpdate) : this()
		{
			this.mainForm						= mainForm;
			this.trueForCreate__falseForUpdate	= trueForCreate__falseForUpdate;

			#region		short-cuts

			this.dataSet						= this.mainForm.DataSet;
			this.current__Pattern				= this.mainForm.Current__Pattern;
			this.current__Pattern_Language		= this.mainForm.Current__Pattern_Language;

			#endregion	short-cuts
			#region		guiTable__FromPatternLetter

			this.guiDataSet = new DataSet("GuiDataSet");

			this.guiTable__FromPatternLetter = this.guiDataSet.Tables.Add("FromPatternLetter");

			this.guiColumn__FromPatternLetter__Rank				= this.guiTable__FromPatternLetter.Columns.Add("Rank", typeof(short));
			this.guiColumn__FromPatternLetter__LetterTypeCode	= this.guiTable__FromPatternLetter.Columns.Add("LetterTypeCode", typeof(string));
			this.guiColumn__FromPatternLetter__LetterString		= this.guiTable__FromPatternLetter.Columns.Add("LetterString", typeof(string));

			this.guiTable__FromPatternLetter.PrimaryKey = new DataColumn[] { this.guiColumn__FromPatternLetter__Rank };

			this.bindingSource__FromPatternLetter = new BindingSource(this.guiDataSet, this.guiTable__FromPatternLetter.TableName);

			this.dataGridView__FromPatternLetter.DataSource = this.bindingSource__FromPatternLetter;

			this.bindingSource__FromPatternLetter.CurrentChanged += bindingSource__FromPatternLetter__CurrentChanged;

			#endregion	guiTable__FromPatternLetter	
			#region		(base)

			this.mainForm.Ctor__CreateUpdate__Word
			(
				this,
				this.button__CreateUpdate,
				this.button__Clone,
				MainForm.ModeName_FromPattern,
				this.label__Word__Id,
				this.label__Word__ComputedString,
				this.label__Word_Language__Meaning,
				this.label__WordType_LanguageName,
				this.textBox__Word__Id				,
				this.textBox__Word__ComputedString	,
				this.textBox__Word_Language__Meaning,
				this.comboBox__WordType_LanguageName,
				true,
			out	TextBox_WEM		textBox__Word__Id_				,
			out	TextBox_WEM		textBox__Word__ComputedString_	,
			out	TextBox_WEM		textBox__Word_Language__Meaning_,
			out	ComboBox_WEM	comboBox__WordType_LanguageName_,
				this.trueForCreate__falseForUpdate,
			ref this.word__Id,
			ref	this.selected__WordType_Language,
				this.current__Word,					
				this.current__Word_Language
			);
			this.textBox_WEM__Id				= textBox__Word__Id_				;
			this.textBox_WEM__UniqueKeyString	= textBox__Word__ComputedString_	;
			this.textBox_WEM__Meaning			= textBox__Word_Language__Meaning_	;
			this.comboBox_WEM__WordType			= comboBox__WordType_LanguageName_	;

			/// on clone d'office le WordType car les children sont en général homogène de ce point de vue.
			if (this.trueForCreate__falseForUpdate && this.mainForm.Current__Word__of__WordFromPattern__of__Current__Pattern != null)
			{ 
				string wordTypeString 
					= this.mainForm
					.Current__Word_Language__of__WordFromPattern__of__Current__Pattern
					.WordType_LanguageRowParent
					.Name;

				this.comboBox__WordType_LanguageName.SelectedItem 
					= new WordTypeComboBoxItem
					(
						this.mainForm
						.Current__Word_Language__of__WordFromPattern__of__Current__Pattern	/// le cloné...
						.WordType_LanguageRowParent					
					);
			}

			#endregion	(base)
			#region		(mode)

			#region		error management

			this.comboBox_WEM__GuiHorizontalShift
				= new ComboBox_WEM
				(	
					this.label__GuiHorizontalShift,
					this.comboBox__GuiHorizontalShift,
					( this.trueForCreate__falseForUpdate ? null : this.current__WordFromPattern.FromPatternRowParent.GuiHorizontalShift.ToString() )
				);

			#endregion	error management

			Pattern.Analyse__Pattern__UniqueKeyString
			(
				this.current__Pattern,
			out List<BabebiDataSet.LetterOrSymbolRow>		letterOrSymbols_,			/// not used in this Form/case.
			out Dictionary<short, BabebiDataSet.SymbolRow>	ranks__symbols_,
			out List<string>								pattern_Remaining_SubStrings_	
			);

			if (ranks__symbols_.Count == 0)
			{
				MessageBox.Show
				(
					"You are trying to create a 'Word (From Pattern)' from the Pattern ; '" + this.current__Pattern.UniqueKeyString + "' that does not include any Symbol !",
					"No Symbol in Pattern",
					MessageBoxButtons.OK,
					MessageBoxIcon.Hand
				);

				return;
			}

			this.ranks__symbols					= ranks__symbols_;
			this.pattern_Remaining_SubStrings	= pattern_Remaining_SubStrings_;

			if (this.trueForCreate__falseForUpdate)
			{
				#region		GuiHorizontalShift

				this.selected__GuiHorizontalShift				= 0;	/// la valeur par défaut
				this.comboBox__GuiHorizontalShift.SelectedIndex = 0;	/// c'est bien Select[INDEX] qui pointe sur "0" d'ailleurs.

				#endregion	GuiHorizontalShift

				this.ranks__letters = new SortedDictionary<short, Letter>();    /// pas d'existant.

				#region		initialization en mode CREATE

				#region		try to find the next letter combination for the new WordFromPattern

				List<string> letterCombinationStringList = new List<string>();

				BabebiDataSet.FromPatternRow[] fromPatterns 
					= this.current__Pattern.GetFromPatternRows();

				foreach (BabebiDataSet.FromPatternRow fromPattern in fromPatterns)
				{
					if (fromPattern.SubscriberCode != MainForm.SubscriberCode_Word)
					{ 
						continue;
					}

					string letterCombinationString = "";

					BabebiDataSet.FromPatternLetterRow[] fromPatternLetters 
						= fromPattern.GetFromPatternLetterRows();

					foreach (BabebiDataSet.FromPatternLetterRow fromPatternLetter in fromPatternLetters)
					{
						letterCombinationString += (letterCombinationString == "" ? "" : "#") + fromPatternLetter.LetterString;
					}

					letterCombinationStringList.Add(letterCombinationString);
				}

				#endregion	try to find the next letter combination for the new WordFromPattern

				#region		letterTypeCodes

				List<string> letterTypeCodeList = new List<string>();
				foreach (BabebiDataSet.SymbolRow symbol in this.ranks__symbols.Values)
				{
					letterTypeCodeList.Add(symbol.TypeCode);
				}
				string[] letterTypeCodes = letterTypeCodeList.ToArray();

				#endregion	letterTypeCodes

				RussianLetterIndexer russianLetterIndexer 
					= new RussianLetterIndexer
					(
						0, 
						letterTypeCodes, 
						this.mainForm.VowelStrings, 
						this.mainForm.ConsonantStrings
					);
				
				string theLetterCombinationString = null;

				int i = letterCombinationStringList.Count;
				do
				{
					string candidateLetterCombinationString = russianLetterIndexer.GetString_of_sharp_separated_letters();

					if (letterCombinationStringList.Contains(candidateLetterCombinationString))
					{
						i--;
					}
					else
					{
						if (i == 0)
						{ 
							theLetterCombinationString = candidateLetterCombinationString;

							break;
						}
					}
				}
				while (russianLetterIndexer.Next()) ;

				if (theLetterCombinationString == null)
				{
					i = letterCombinationStringList.Count;
					do
					{
						string candidateLetterCombinationString = russianLetterIndexer.GetString_of_sharp_separated_letters();

						if (!letterCombinationStringList.Contains(candidateLetterCombinationString))
						{
							theLetterCombinationString = candidateLetterCombinationString;

							break;
						}
					}
					while (russianLetterIndexer.Next()) ;

					if (theLetterCombinationString == null)
					{
						this.textBox_WEM__UniqueKeyString.SetError("All the possible children have already been created.", "Full");

						return;
					}
				}

				string[] theLetterCombination = theLetterCombinationString.Split('#');

				int letterIndex = -1;
				foreach (KeyValuePair<short, BabebiDataSet.SymbolRow> keyValuePair in this.ranks__symbols)
				{
					short					rank	= keyValuePair.Key;
					BabebiDataSet.SymbolRow	symbol	= keyValuePair.Value;

					letterIndex++;

					Letter letter = new Letter(symbol.TypeCode, theLetterCombination[letterIndex]);

					this.ranks__letters.Add(rank, letter);
				}

				#endregion	initialization en mode CREATE
			}
			else
			{
				#region		GuiHorizontalShift

				this.selected__GuiHorizontalShift				= this.current__FromPattern.GuiHorizontalShift;
				this.comboBox__GuiHorizontalShift.SelectedItem	= this.selected__GuiHorizontalShift.ToString();

				#endregion	GuiHorizontalShift

				this.ranks__letters = FromPattern_.Build__ranks__letters__from__FromPatternLetters(this.current__FromPattern); /// initialize ranks__letters from current FromPatternLetters
			}

			MainForm.FromPatternLetter__fill__dataGridView(this.ranks__letters, this.guiTable__FromPatternLetter);

			/// en effet il y a une tentative de calcul sur la 1ère row 
			/// grâce au binding mais plus sur les autres rows.
			if (this.ranks__letters.Count > 1)
			{ 
				this.compute__ComputedString();	
			}

			#endregion	(mode)
		}

		public Form__WordFromPattern__of__Current__Pattern__CreateUpdate()
		{
			InitializeComponent();
		}

		#endregion	ctors

		#region		(base) events
 
		private void button__Clone__Click(object sender, EventArgs e)
		{
			this.mainForm.Clone__Word
			(
				null,
				this.textBox__Word_Language__Meaning,
				this.comboBox__WordType_LanguageName,
				true,
				this.current__Word,					
				this.current__Word_Language
			);
		}

		#region		Word__Id

		private void textBox__Word__Id__KeyPress(object sender, KeyPressEventArgs e)
		{
			MainForm.Integer__KeyPress(e);
		}
		private void textBox__Word__Id__TextChanged(object sender, EventArgs e)
		{
			this.mainForm.Word__Id__TextChanged(this.textBox_WEM__Id, ref this.word__Id);
		}
		private void textBox__Word__Id__Leave(object sender, EventArgs e)
		{
			if (this.textBox_WEM__Id.HasError)
			{
				this.textBox_WEM__Id.ShowMessageBox();
			}
		}

		#endregion	Word__Id
		#region		Word_Language__Meaning

		private void textBox__Word_Language__Meaning__TextChanged(object sender, EventArgs e)
		{
			this.mainForm.Word__Meaning__TextChanged(this.textBox_WEM__Meaning);
		}

		private void textBox__Word_Language__Meaning__Leave(object sender, EventArgs e)
		{
			if (this.textBox_WEM__Meaning.HasError && !string.IsNullOrEmpty(this.textBox__Word_Language__Meaning.Text))
			{
				this.textBox_WEM__Meaning.ShowMessageBox();
			}
		}

		#endregion	Word_Language__Meaning
		#region		ComputedString

		private void textBox__Word__ComputedString__TextChanged(object sender, EventArgs e)
		{			
			this.mainForm.Word__UniqueKeyString__TextChanged(this.textBox_WEM__UniqueKeyString);
		}

		#endregion	ComputedString
		#region		WordType_LanguageName

		private void comboBox__WordType_LanguageName_SelectedIndexChanged(object sender, EventArgs e)
		{
			this.mainForm.Word__WordType__SelectedIndexChanged(this.comboBox_WEM__WordType, ref this.selected__WordType_Language);
		}

		#endregion	WordType_LanguageName

		#endregion	(base) events
		#region		(mode) events

		#region		events about FromPatternLetter

		private void bindingSource__FromPatternLetter__CurrentChanged(object sender, EventArgs e)
		{

			this.mainForm.FromPatternLetter__bindingSource__CurrentChanged
			(
				this.bindingSource__FromPatternLetter,
				this.guiColumn__FromPatternLetter__Rank,				
				this.guiColumn__FromPatternLetter__LetterTypeCode,
				this.guiColumn__FromPatternLetter__LetterString,	
				this.textBox__FocusedRank,
				this.textBox__FocusedLetter,
				this.comboBox__SelectedLetter
			);
		}

		private void comboBox__SelectedLetter__SelectedIndexChanged(object sender, EventArgs e)
		{
			MainForm.FromPatternLetter__comboBox__SelectedLetter__SelectedIndexChanged
			(
				this.textBox__FocusedRank,
				this.textBox__FocusedLetter,
				this.comboBox__SelectedLetter,
				this.ranks__letters,
				this.guiTable__FromPatternLetter,
				this.guiColumn__FromPatternLetter__LetterString
			);

			this.compute__ComputedString();
		}
		
		#endregion	events about FromPatternLetter

		private void comboBox__GuiHorizontalShift_SelectedIndexChanged(object sender, EventArgs e)
		{
			MainForm.FromPattern__comboBox__GuiHorizontalShift__SelectedIndexChanged
			(
				this.comboBox_WEM__GuiHorizontalShift,
			ref	this.selected__GuiHorizontalShift
			);
		}

		#endregion	(mode) events
		#region		ComputedString

		private void compute__ComputedString()
		{
			/// Comme on est dans Word, on doit avoir : 
			/// 
			/// 'ranks__symbols.Count == FromPatternLetter.Rows.Count'.
			/// 
			/// => On ne tente aucun recalcul inutile tant que l'on n'a pas le bon nombre
			/// de row dans FromPatternLetter.

			if (this.guiTable__FromPatternLetter.Rows.Count != this.ranks__letters.Count)
			{
				return;
			}

			string computedString 
				= WordFromPattern
				.Compute_UniqueKeyString
				(
					this.pattern_Remaining_SubStrings,
					this.ranks__letters
				);

			this.textBox__Word__ComputedString.Text = computedString;

			this.mainForm.Word__UniqueKeyString__TextChanged(this.textBox_WEM__UniqueKeyString );
		}

		#endregion	ComputedString

		private void button__CreateUpdate__Click(object sender, EventArgs e)
		{
			#region		checking

			if (this.textBox_WEM__Id.HasError)
			{
				this.textBox_WEM__Id.ShowMessageBox();

				return;
			}
			if (this.textBox_WEM__UniqueKeyString.HasError)
			{
				this.textBox_WEM__UniqueKeyString.ShowMessageBox();

				return;
			}
			if (this.textBox_WEM__Meaning.HasError)
			{
				this.textBox_WEM__Meaning.ShowMessageBox();

				return;
			}
			if (this.comboBox_WEM__WordType.HasError)
			{
				this.comboBox_WEM__WordType.ShowMessageBox();

				return;
			}

			#endregion	checking

			if (this.trueForCreate__falseForUpdate)
			{
				#region		(base)

				this.mainForm.Create__Word
				(
					this.word__Id,
					this.textBox__Word__ComputedString.Text,
					this.textBox__Word_Language__Meaning.Text,
					this.selected__WordType_Language.WordTypeRow,
				out BabebiDataSet.WordOrPatternRow	current__WordOrPattern_,
				out BabebiDataSet.WordRow			current__Word_,
				out BabebiDataSet.Word_LanguageRow	current__Word_Language_,
				out BabebiDataSet.LanguageWordRow	current__LanguageWord_, 
					MainForm.ModeCode_FromPattern
				);

				this.current__WordOrPattern	= current__WordOrPattern_;
				this.current__Word			= current__Word_;
				this.current__Word_Language	= current__Word_Language_;

				this.mainForm.Current__LanguageWord	= current__LanguageWord_;

				#endregion	(base)
				#region		(mode)

				/// FromPattern
				this.current__FromPattern
					= this.dataSet.FromPattern
					.AddFromPatternRow
					(
						MainForm.SubscriberCode_Word,
						this.word__Id,
						this.current__Pattern,
						this.selected__GuiHorizontalShift
					);
				
				/// FromPatternLetters
				foreach(KeyValuePair<short, Letter> rank__letter in this.ranks__letters)
				{
					short	rank	= rank__letter.Key;
					Letter	letter	= rank__letter.Value;

					this.dataSet.FromPatternLetter
						.AddFromPatternLetterRow
						(
							MainForm.SubscriberCode_Word,
							this.word__Id,
							rank,
							letter.TypeCode,
							letter.String
						);
				}

				/// WordFromPattern
				this.current__WordFromPattern
					= this.dataSet.WordFromPattern
					.AddWordFromPatternRow
					(
						this.word__Id,
						MainForm.ModeCode_FromPattern,
						MainForm.SubscriberCode_Word
					);
				
				#endregion	(mode)
			}
			else
			{
				#region		à cause de l'Update du Word Id, if any	(BEFORE)

				bool wordIdHasChanged = false;
				if (this.word__Id != this.current__FromPattern.SubscriberId)
				{ 
					wordIdHasChanged = true;

					this.current__FromPattern.BeginEdit();
					this.current__WordOrPattern.BeginEdit();
					this.current__Word.BeginEdit();
					this.current__WordFromPattern.BeginEdit();

					this.current__FromPattern.SubscriberId = this.word__Id;
				}

				#endregion	à cause de l'Update du Word Id, if any	(BEFORE)
				#region		(base)

				this.mainForm.Update__Word
				(
					this.word__Id,
					this.textBox__Word__ComputedString.Text,
					this.textBox__Word_Language__Meaning.Text,
					this.selected__WordType_Language.WordTypeRow,
					this.current__WordOrPattern,
					this.current__Word,
					this.current__Word_Language,
				out BabebiDataSet.LanguageWordRow current__LanguageWord	/// renvoie null si toujours le même.
				);

				if (current__LanguageWord != null)
				{
					this.mainForm.Current__LanguageWord = current__LanguageWord;
				}

				#endregion	(base)
				#region		à cause de l'Update du Word Id, if any	(AFTER)

				if (wordIdHasChanged)
				{ 
					this.current__FromPattern.EndEdit();
					this.current__WordOrPattern.EndEdit();
					this.current__Word.EndEdit();
					this.current__WordFromPattern.EndEdit();
				}

				#endregion	à cause de l'Update du Word Id, if any	(AFTER)
				#region		(mode)

				/// about GuiHorizontalShift
				if (this.selected__GuiHorizontalShift != this.current__FromPattern.GuiHorizontalShift)
				{
					this.current__FromPattern.GuiHorizontalShift = this.selected__GuiHorizontalShift;
				}

				#region		fromPatternLetters

				/// dans le cas de WordFromPattern__of__the__current__Pattern, c'est biblique car :
				/// - on a forcément le même Pattern parent, et donc les mêmes symboles aux mêmes endroits.
				/// - tous les symboles du Pattern doivent être remplacés par des lettres.
				/// 
				/// => Une fois crées les FromPatternLetters ne changent pas en nombre et en ranks : seules 
				///    les lettres peuvent changer au cours d'un Update.
				///     

				BabebiDataSet.FromPatternLetterRow[] fromPatternLetters
					= this.current__FromPattern.GetFromPatternLetterRows();

				foreach (BabebiDataSet.FromPatternLetterRow fromPatternLetterRow in fromPatternLetters)
				{
					Letter letter = this.ranks__letters[fromPatternLetterRow.Rank];

					if (letter.String != fromPatternLetterRow.LetterString)
					{
						fromPatternLetterRow.LetterString = letter.String;
					}
				}

				#endregion	fromPatternLetters

				#endregion	(mode)
			}

			this.DialogResult = DialogResult.OK;

			this.Close();
		}
	}
}
