#region Copyright and GPL
/*************************************************************************
 * dataMagus - The open source database learning software
 * Copyright (C) 2007 by all authors
 * Authors: Monika Schwitter (monsy@gmx.ch)
 *          Jürg Zgraggen (j.zgraggen@ejaz.net)
 *          HSLU (Hochschule Luzern - Technik+Architektur)
 * 
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 * 
 *     You should have received a copy of the GNU General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *************************************************************************/
#endregion
#region Using
using System;
using System.IO;
using System.Globalization;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
using System.Reflection;
using System.Resources;
using dataMagus.Forms;
using dataMagus.Forms.Controls;
using dataMagus.Data;
using dataMagus.Common;
using dataMagus.Common.Physical;
using dataMagus.Common.Physical.Objectoriented;
using dataMagus.Common.Physical.Relational;
using dataMagus.Common.Logical;
using dataMagus.Common.PlugIn;
using dataMagus.Common.PlugIn.Generator;
using dataMagus.Common.PlugIn.Notation;
#endregion

namespace dataMagus.Main
{
	/// <summary>
	/// Main form.
	/// </summary>
	public partial class MainForm : Form
	{
		#region Structures
		/// <summary>
		/// Helper structure to bind the data to the ToolStripDropDown control.
		/// </summary>
		private struct PlugInData
		{
			/// <summary>
			/// Name of the plugin.
			/// </summary>
			public string Name;
			
			/// <summary>
			/// Type of the plugin.
			/// </summary>
			public Type Type;
		}
		#endregion

		#region Members
		/// <summary>
		/// All schema data.
		/// </summary>
		private PlugInData[] _schemaData = null;
		
		/// <summary>
		/// All notation data.
		/// </summary>
		private PlugInData[] _notationData = null;
		
		/// <summary>
		/// Resource manager for loading resources.
		/// </summary>
		private ResourceManager _otherResManager = new ResourceManager("dataMagus.Main.OtherResources", Assembly.GetExecutingAssembly());
		
		/// <summary>
		/// Current open file path.
		/// </summary>
		private string _currentFilePath = string.Empty;
		#endregion
		
		#region Constructors / Destructors
		/// <summary>
		/// Default constructor.
		/// </summary>
		public MainForm()
		{
			InitializeComponent();
		}
		#endregion
		
		#region Properties
		/// <summary>
		/// Current open file path.
		/// </summary>
		private string CurrentFilePath 
		{
			get { return this._currentFilePath; }
			set 
			{
				this._currentFilePath = value; 
				this.currentOpenFilePathStatusLabel.Text = value;
			}
		}
		#endregion
		
		#region Methodes
		/// <summary>
		/// Set a new schema.
		/// </summary>
		/// <param name="schema">Schema to be set.</param>
		private void SetSchema(ISchema schema)
		{
			// clear data control and set new schema
			this.schemaElementDataControl.SelectElement(null);
			this.schemaNavigationControl.Schema = schema;
			this.designerSchemaControl.Schema = schema;
			this.SetNotation(schema.NotationType);
		}
		
		/// <summary>
		/// Set a new notation.
		/// </summary>
		/// <param name="notationType">Type of the notation to be set.</param>
		private void SetNotation(Type notationType)
		{
			// set new notation on designer
			this.designerSchemaControl.Schema.NotationType = notationType;
			this.designerSchemaControl.ChangedNotation();
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void ExitToolStripMenuItemClick(object sender, EventArgs e)
		{
			try
			{
				this.Close();
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void GenerateToolStripMenuItemClick(object sender, EventArgs e)
		{
			try
			{
				Type generatorType = null;
				if (this.designerSchemaControl.Schema is PhysicalRelationalSchema)
				{
					generatorType = typeof(IRelationalGenerator);
				}
				else if (this.designerSchemaControl.Schema is PhysicalObjectorientedSchema)
				{
					generatorType = typeof(IObjectorientedGenerator);
				}
				else
				{
					throw new NotImplementedException("This type of schema is not implemented.");
				}
				
				GeneratorForm frm = new GeneratorForm();
				frm.GeneratorType = generatorType;
                frm.Schema = this.designerSchemaControl.Schema;
				frm.ShowDialog();
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void OptionsToolStripMenuItemClick(object sender, EventArgs e)
		{
			try
			{
				OptionsForm frm = new OptionsForm();
				DialogResult result = frm.ShowDialog();
				if (DialogResult.OK == result)
				{
					this.designerSchemaControl.ResetApplicationOptions();
					this.designerSchemaControl.Invalidate();
					this.designerSchemaControl.Update();
				}
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void AboutToolStripMenuItemClick(object sender, EventArgs e)
		{
			try
			{
				AboutForm frm = new AboutForm();
				frm.ShowDialog();
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void NotationComboBoxSelectedIndexChanged(object sender, EventArgs e)
		{
			try
			{
				this.Cursor = Cursors.WaitCursor;
				if (-1 != this.notationComboBox.SelectedIndex)
				{
					PlugInData currentNotation = this._notationData[this.notationComboBox.SelectedIndex];
					
					// set the new notation on schema
					this.SetNotation(currentNotation.Type);
				}
			}
			catch (Exception ex)
			{
				this.Cursor = Cursors.Default;
				MessageForm.ShowDialog(ex);
			}
			finally
			{
				this.Cursor = Cursors.Default;
			}
		}
		
		/// <summary>
		/// Enable/Disable menu items depending on the type of schema.
		/// </summary>
		/// <param name="schema">Schema.</param>
		private void EnableMenuItemsBySchema(ISchema schema)
		{
			// control logical items
			bool isLogical = (schema is ILogicalSchema);
			this.resetToolStripMenuItem.Enabled = isLogical;
			this.newToolStripMenuItem.Enabled = isLogical;
			this.openToolStripMenuItem.Enabled = isLogical;
			
			// control physical items
			bool isPhysical = (schema is IPhysicalSchema);
			this.generateToolStripMenuItem.Enabled = isPhysical;
		}

		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void SchemaComboBoxSelectedIndexChanged(object sender, EventArgs e)
		{
			try
			{
				this.Cursor = Cursors.WaitCursor;
				if (-1 != this.schemaComboBox.SelectedIndex)
				{
					Type schemaType = this._schemaData[this.schemaComboBox.SelectedIndex].Type;
					ISchema schema = null;
					if (null != schemaType.GetInterface(typeof(ISchema).FullName))
					{
						// check for physical schema
						if (null != schemaType.GetInterface(typeof(IPhysicalSchema).FullName))
						{
							schema = PhysicalSchemaManager.Instance.Generate(schemaType, new UserInteractionForm());
						}
						else
						{
							schema = DataContainer.Instance.LogicalSchema;
						}
					}
					else
					{
						throw new NotImplementedException("The type on SchemaComboBox does not implement ISchema.");
					}
					
					// enable menu items if needed
					this.EnableMenuItemsBySchema(schema);

					// set new schema
					this.SetSchema(schema);

					// refill notations
					this.FillNotationDropDown();
				}
			}
			catch (Exception ex)
			{
				this.Cursor = Cursors.Default;
				MessageForm.ShowDialog(ex);
			}
			finally
			{
				this.Cursor = Cursors.Default;
			}
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">ElementEventArgs</param>
		private void SchemaNavigationControlSelectElement(object sender, ElementEventArgs e)
		{
			try
			{
				// load data control
                this.schemaElementDataControl.SelectElement(e.Element);
                if (e.Element is IElement)
                {
                    // refresh specified schema control
                    ElementControl ctrl = this.designerSchemaControl.FindControl((IElement)e.Element);
                    if (null != ctrl)
                    {
                        ctrl.Invalidate();
                        ctrl.Update();
                    }
                }
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void SchemaNavigationControlSelectGroup(object sender, EventArgs e)
		{
			try
			{
				this.schemaElementDataControl.SelectElement(null);
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void RefreshToolStripMenuItemClick(object sender, EventArgs e)
		{
			try
			{
				this.schemaNavigationControl.Rebuild();
				this.schemaElementDataControl.SelectElement(null);
				
				this.designerSchemaControl.Refresh();
				this.schemaNavigationControl.Refresh();
				this.schemaElementDataControl.Refresh();
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Fill schema drop down control.
		/// </summary>
		private void FillSchemaDropDown()
		{
            this.schemaComboBox.Items.Clear();
            this.schemaComboBox.SelectedIndex = -1;
			
            // create layer source (one logical and n physical)
            this._schemaData = new PlugInData[PhysicalSchemaManager.Instance.CountLoadedSchemas + 1];

            // fill them up
            this._schemaData[0].Name = this._otherResManager.GetString(String.Format("{0}:{1}", "Name", DataContainer.Instance.LogicalSchema.GetType().FullName));
            this._schemaData[0].Type = DataContainer.Instance.LogicalSchema.GetType();

            int counter = 0;
            IEnumerator schemasEnumerator = PhysicalSchemaManager.Instance.GetLoadedSchemas();
            while (schemasEnumerator.MoveNext())
            {
                IPhysicalSchema schema = (IPhysicalSchema)schemasEnumerator.Current;
                this._schemaData[counter + 1].Name = this._otherResManager.GetString(String.Format("{0}:{1}", "Name", schema.GetType().FullName));
                this._schemaData[counter + 1].Type = schema.GetType();
                counter++;
            }

            // load schema combo
            for (int i = 0; i < this._schemaData.Length; i++)
            {
                //TODO: Get layername from resource file by _layerData[i].Type
                this.schemaComboBox.Items.Add(String.Format("{0}: {1}", i, this._schemaData[i].Name));
            }
            if (this.schemaComboBox.Items.Count != 0)
            {
                this.schemaComboBox.SelectedIndex = 0;
            }
		}
		
		/// <summary>
		/// Fill notation drop down control.
		/// </summary>
		private void FillNotationDropDown()
		{
            this.notationComboBox.Items.Clear();
            this.notationComboBox.SelectedIndex = -1;
            
            // get current selected schema type
            Type currentSchemaType = this._schemaData[this.schemaComboBox.SelectedIndex].Type;

            List<INotation> foundNotations = NotationManager.Instance.FindInterfaceTypeForSchema(currentSchemaType);
            if (null != foundNotations && foundNotations.Count == 0)
            {
            	// stop the filling here, no notations found
            	return;
            }
            
            // fill source
            this._notationData = new PlugInData[foundNotations.Count];
            for (int i = 0; i < foundNotations.Count; i++)
            {
            	INotation notation = foundNotations[i];
                this._notationData[i].Name = notation.Name;
                this._notationData[i].Type = notation.GetType();
            }
            
            // load notation combo
            for (int i = 0; i < this._notationData.Length; i++)
            {
            	if (!string.IsNullOrEmpty(this._notationData[i].Name)
					&& null != this._notationData[i].Type)
				{
                	this.notationComboBox.Items.Add(String.Format("{0}: {1}", i, this._notationData[i].Name));
				}
            }
            if (this.notationComboBox.Items.Count != 0)
            {
                this.notationComboBox.SelectedIndex = 0;
            }
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void MainFormLoad(object sender, EventArgs e)
		{
			SplashScreenForm splash = new SplashScreenForm();
			splash.Show();
			splash.Refresh();
			
			try
			{
	            // load plugins
	            splash.ProgressPercent = 10;
				splash.LoadingText = "Loading PlugIns...";
	            NotationManager.Instance.LoadPlugIns(Thread.GetDomain().BaseDirectory);
	            splash.ProgressPercent = 30;
	            PhysicalSchemaManager.Instance.LoadSchemas(Thread.GetDomain().BaseDirectory);
	            splash.ProgressPercent = 50;
	            GeneratorManager.Instance.LoadPlugIns(Thread.GetDomain().BaseDirectory);
	            splash.ProgressPercent = 70;
	            
	            // check for needed plugins
	            //TODO: check for logical and at least one for each physical schema
	            if (00 == NotationManager.Instance.CountLoadedPlugIns)
	            {
	            	throw new Exception("There are no notation plugins available. At least one for each layer is needed. The software is going to abort.");
	            }
	
				splash.LoadingText = "Setting up environment...";
				// schemas
	            this.FillSchemaDropDown();
	            splash.ProgressPercent = 90;
			}
			finally
			{
	            splash.ProgressPercent = 100;
				splash.Close();
			}
        }
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
			try
			{
        		this.Cursor = Cursors.WaitCursor;
				if (string.IsNullOrEmpty(this.CurrentFilePath))
				{
					this.SaveAsToolStripMenuItemClick(sender, e);
				}
				else
				{
		            dataMagus.Data.DataContainerSerializer dc = new DataContainerSerializer();
		            dc.Save(DataContainer.Instance, this.CurrentFilePath);
				}
			}
			catch (Exception ex)
			{
        		this.Cursor = Cursors.Default;
				MessageForm.ShowDialog(ex);
			}
			finally
			{
        		this.Cursor = Cursors.Default;
			}
        }

		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void SaveAsToolStripMenuItemClick(object sender, EventArgs e)
		{
			try
			{
				DialogResult result = this.saveFileDialog.ShowDialog();
				if (DialogResult.OK == result)
				{
		            dataMagus.Data.DataContainerSerializer dc = new DataContainerSerializer();
		            dc.Save(DataContainer.Instance, this.saveFileDialog.FileName);
					this.CurrentFilePath = this.saveFileDialog.FileName;
				}
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
        
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
        	try
        	{
        		this.Cursor = Cursors.WaitCursor;
	        	DialogResult result = this.openFileDialog.ShowDialog();
	        	if (DialogResult.OK == result)
	        	{
	        		// load new container
		            dataMagus.Data.DataContainerSerializer dc = new DataContainerSerializer();
		            dc.Load(this.openFileDialog.FileName);
		            this.CurrentFilePath = this.openFileDialog.FileName;
		            
		            // reset indexes
		            this.schemaComboBox.SelectedIndex = 0;
		            this.notationComboBox.SelectedIndex = 0;
	        	}
        	}
			catch (Exception ex)
			{
        		this.Cursor = Cursors.Default;
				MessageForm.ShowDialog(ex);
			}
			finally
			{
        		this.Cursor = Cursors.Default;
			}
        }
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void ResetChangedDataToolStripMenuItemClick(object sender, EventArgs e)
		{
			try
			{
				IEnumerator schemaEnum = PhysicalSchemaManager.Instance.GetLoadedSchemas();
				while (schemaEnum.MoveNext())
				{
					IPhysicalSchema schema = (IPhysicalSchema)schemaEnum.Current;
					DataContainer.Instance.PhysicalData.Reset(schema, true, false);
				}
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void ResetInteractionToolStripMenuItemClick(object sender, EventArgs e)
		{
			try
			{
				IEnumerator schemaEnum = PhysicalSchemaManager.Instance.GetLoadedSchemas();
				while (schemaEnum.MoveNext())
				{
					IPhysicalSchema schema = (IPhysicalSchema)schemaEnum.Current;
					DataContainer.Instance.PhysicalData.Reset(schema, false, true);
				}
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void ResetAllDataToolStripMenuItemClick(object sender, EventArgs e)
		{
			try
			{
				IEnumerator schemaEnum = PhysicalSchemaManager.Instance.GetLoadedSchemas();
				while (schemaEnum.MoveNext())
				{
					IPhysicalSchema schema = (IPhysicalSchema)schemaEnum.Current;
					DataContainer.Instance.PhysicalData.Reset(schema, true, true);
				}
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void NewToolStripMenuItemClick(object sender, EventArgs e)
		{
			try
			{
				this.CurrentFilePath = string.Empty;
				
				// create new logical schema and exchange it
				LogicalSchema newSchema = new LogicalSchema();
				PhysicalItemContainer newItemContainer = new PhysicalItemContainer();
				DataContainer.Instance.Exchange(newSchema, newItemContainer);
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		void ManualToolStripMenuItemClick(object sender, EventArgs e)
		{
			try
			{
				// open manual
				string rootDir = Thread.GetDomain().BaseDirectory;
				string manualFilepath = this._otherResManager.GetString("ManualFilename");
				Process.Start(Path.Combine(rootDir, manualFilepath));
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		void WebsiteToolStripMenuItemClick(object sender, EventArgs e)
		{
			try
			{
				// open website
				string websiteAddress = this._otherResManager.GetString("WebsiteAddress");
				Process.Start(websiteAddress);
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void EnglishToolStripMenuItemClick(object sender, EventArgs e)
		{
			try
			{
				this.CheckLanguage((ToolStripMenuItem)sender);
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void GermanToolStripMenuItemClick(object sender, EventArgs e)
		{
			try
			{
				this.CheckLanguage((ToolStripMenuItem)sender);
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Uncheck all languages.
		/// </summary>
		private void UncheckAllLanguages()
		{
			// unselect all
			foreach (ToolStripItem item in languageToolStripMenuItem.DropDownItems)
			{
				if (item is ToolStripMenuItem)
				{
					((ToolStripMenuItem)item).Checked = false;
				}
			}
		}
		
		/// <summary>
		/// Check (select) a language item.
		/// </summary>
		/// <param name="languageItem">Item to check.</param>
		private void CheckLanguage(ToolStripMenuItem languageItem)
		{
			if (null == languageItem.Tag
			    || languageItem.Tag.ToString().Trim().Length == 0)
			{
				return;
			}
			
			// uncheck all
			this.UncheckAllLanguages();
			
			// check the one
			languageItem.Checked = true;
			CultureInfo uiInfo = CultureInfo.CreateSpecificCulture(languageItem.Tag.ToString());
			Thread.CurrentThread.CurrentUICulture = uiInfo;
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void CreateImageToolStripMenuItemClick(object sender, EventArgs e)
		{
			try
			{
				// get size
				ImageSizeForm sizeForm = new ImageSizeForm();
				DialogResult result = sizeForm.ShowDialog();
				if (DialogResult.OK == result)
				{
					Size bmpSize = sizeForm.ImageSize;
					
					// get save name
					result = this.saveImageDialog.ShowDialog();
					if (DialogResult.OK == result)
					{
						// save it to image
			        	Bitmap bmp = new Bitmap(bmpSize.Width, bmpSize.Height);
			        	Graphics context = Graphics.FromImage(bmp);
						this.designerSchemaControl.DrawSchema(context);
						bmp.Save(this.saveImageDialog.FileName);
					}
				}
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Event handler.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		private void DonateToolStripMenuItemClick(object sender, EventArgs e)
		{
			try
			{
				// open website
				string websiteAddress = this._otherResManager.GetString("DonationWebsiteAddress");
				Process.Start(websiteAddress);
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		#endregion
	}
}
