#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.Xml;
using System.Xml.Schema;
using dataMagus.Common.Physical;
using dataMagus.Common.PlugIn;
using System.Collections.Generic;
using System.Collections;
#endregion

namespace dataMagus.PlugIns.Generators.Odl
{
	/// <summary>
	/// This class is used to hold generator specific data.
	/// </summary>
	public class GeneratorData : IExtendedData
	{
		#region Members
		/// <summary>
		/// Singelton instance object.
		/// </summary>
        private static GeneratorData _instance = null;

        /// <summary>
        /// Lock object for thread safe access.
        /// </summary>
        private static readonly object _padlock = new object();

        /// <summary>
        /// Holds the mapping of the datatypes.
        /// </summary>
        private Dictionary<string, string> _dataTypeMapping = new Dictionary<string, string>();
        
        /// <summary>
        /// Indicator if the inverse relationships should also be generated.
        /// </summary>
        private bool _inverse = true;
        
        /// <summary>
        /// Indicator if the key (or keys) statement should also be generated.
        /// </summary>
        private bool _key = false;
        
        /// <summary>
        /// Indicator if the extend statement should also be generated.
        /// </summary>
        private bool _extent = false;
		#endregion

		#region Constructors / Destructors
        /// <summary>
        /// Default constructor.
        /// </summary>
        private GeneratorData()
		{
            // define the mapping for the base datatypes
            this._dataTypeMapping.Add(DataType.String.Name, "string");
            this._dataTypeMapping.Add(DataType.Number.Name, "short");
            this._dataTypeMapping.Add(DataType.Float.Name, "float");
            this._dataTypeMapping.Add(DataType.DateTime.Name, "Date");
            this._dataTypeMapping.Add(DataType.Boolean.Name, "boolean");
            this._dataTypeMapping.Add(DataType.Binary.Name, "octet");
            this._dataTypeMapping.Add(DataType.Unknown.Name, "unknown");
		}
		#endregion

		#region Properties
		/// <summary>
		/// Singelton instance object.
		/// </summary>
        public static GeneratorData Instance 
		{
			get
			{
                if (null == GeneratorData._instance)
				{
                    lock (GeneratorData._padlock)
					{
                        if (null == GeneratorData._instance)
						{
                            GeneratorData._instance = new GeneratorData();
						}
					}
				}
                return GeneratorData._instance;
			}			
		}

        /// <summary>
        /// Indicator if the inverse relationships should also be generated.
        /// </summary>
		public bool Inverse 
		{
			get { return this._inverse; }
			set { this._inverse = value; }
		}
		
        /// <summary>
        /// Indicator if the key (or keys) statement should also be generated.
        /// </summary>
		public bool Key
		{
			get { return this._key; }
			set { this._key = value; }
		}
	
        /// <summary>
        /// Indicator if the extend statement should also be generated.
        /// </summary>
		public bool Extent
		{
			get { return this._extent; }
			set { this._extent = value; }
		}
		#endregion

		#region Methodes
		/// <summary>
		/// Update the internal stored datatype if the an existing one has changed or a new 
		/// one has been added.
		/// </summary>
		/// <param name="dataType">Datatype to update.</param>
		/// <param name="value">Value to update.</param>
        public void UpdateDataTypeMapping(string dataType, string value)
        {
            if (String.IsNullOrEmpty(dataType) || null == value)
            {
                return;
            }
            if (!this._dataTypeMapping.ContainsKey(dataType))
            {
                this._dataTypeMapping.Add(dataType, String.Empty);
            }
            this._dataTypeMapping[dataType] = value;
        }

        /// <summary>
        /// Get the stored mapping of a datatype.
        /// </summary>
        /// <param name="dataType">Datatype to get the mapping for.</param>
        /// <returns>The found mapping or null if nothing has been found.</returns>
        public string GetDataTypeMapping(string dataType)
        {
            if (!this._dataTypeMapping.ContainsKey(dataType))
            {
                return null;
            }
            return this._dataTypeMapping[dataType];
        }

        /// <summary>
        /// Remove the stored mapping of a datatype.
        /// </summary>
        /// <param name="dataType">Datatype to remove the mapping for.</param>
        public void RemoveDataTypeMapping(string dataType)
        {
            if (this._dataTypeMapping.ContainsKey(dataType))
            {
                this._dataTypeMapping.Remove(dataType);
            }
        }

        /// <summary>
        /// Get all stored mapping of the datatype.
        /// </summary>
        /// <returns>Enumerator to parse.</returns>
        public IEnumerator GetDataTypeMappings()
        {
            return this._dataTypeMapping.GetEnumerator();
        }

        /// <summary>
        /// Update a datatype on the schema.
        /// </summary>
        /// <param name="newDatatype">New datatype.</param>
        /// <param name="oldDatatype">Old datatype.</param>
        /// <param name="action">Action happend.</param>
        public void UpdateDataType(DataType newDatatype, DataType oldDatatype, PhysicalItemContainer.DataTypeAction action)
        {
            if (PhysicalItemContainer.DataTypeAction.Added == action)
            {
                if (null != newDatatype && null != newDatatype.Name)
                {
                    this.UpdateDataTypeMapping(newDatatype.Name, newDatatype.BaseType.ToString());
                }
            }
            else if (PhysicalItemContainer.DataTypeAction.Removed == action)
            {
                if (null != oldDatatype && null != oldDatatype.Name)
                {
                    this.RemoveDataTypeMapping(oldDatatype.Name);
                }
            }
            else if (PhysicalItemContainer.DataTypeAction.Modified == action)
            {
                if (null != newDatatype && null != newDatatype.Name &&
                    null != oldDatatype && null != oldDatatype.Name)
                {
                    string savedDatatype = this.GetDataTypeMapping(oldDatatype.Name);
                    this.RemoveDataTypeMapping(oldDatatype.Name);
                    this.UpdateDataTypeMapping(newDatatype.Name, savedDatatype);
                }
            }
        }
		
        /// <summary>
        /// Gets the xml schema object.
        /// </summary>
        /// <returns>Always null.</returns>
		public XmlSchema GetSchema()
		{
            return null;
		}
		
		/// <summary>
        /// Reads back an instance of this class from a reader.
        /// </summary>
        /// <param name="reader">Reader object to read from.</param>
		public void ReadXml(XmlReader reader)
		{
            if (reader.IsStartElement("GeneratorData"))
            {
                this._inverse = Convert.ToBoolean(reader.GetAttribute("inverse"));
                this._key = Convert.ToBoolean(reader.GetAttribute("key"));
                this._extent = Convert.ToBoolean(reader.GetAttribute("extent"));

                reader.ReadStartElement();
                if (reader.IsStartElement("DataTypeMappings"))
                {
                    if (!reader.IsEmptyElement)
                    {
                        while (!reader.EOF)
                        {
                            reader.Read();
                            if (reader.NodeType == System.Xml.XmlNodeType.EndElement && reader.Name == "DataTypeMappings")
                            {
                                break;
                            }
                            if (reader.IsStartElement("Mapping"))
                            {
                                this.UpdateDataTypeMapping(reader.GetAttribute("key"), reader.GetAttribute("value"));
                            }
                        }
                    }
                }
                if (!reader.IsEmptyElement)
                {
                    reader.ReadEndElement();
                }
            }
		}
		
		/// <summary>
        /// Writes a the instace of this class to a writer.
        /// </summary>
        /// <param name="writer">Writer object to write into.</param>
		public void WriteXml(XmlWriter writer)
		{
            writer.WriteStartElement("GeneratorData");
            writer.WriteAttributeString("inverse", this.Inverse.ToString());
            writer.WriteAttributeString("key", this.Key.ToString());
            writer.WriteAttributeString("extent", this.Extent.ToString());

            writer.WriteStartElement("DataTypeMappings");
            IEnumerator dataTypeMappingsEnumerator = this._dataTypeMapping.GetEnumerator();
            while (dataTypeMappingsEnumerator.MoveNext())
            {
                KeyValuePair<string, string> dataTypeMapping = (KeyValuePair<string, string>)dataTypeMappingsEnumerator.Current;
                writer.WriteStartElement("Mapping");
                writer.WriteAttributeString("key", dataTypeMapping.Key);
                writer.WriteAttributeString("value", dataTypeMapping.Value);
                writer.WriteEndElement();
            }
            writer.WriteEndElement();

            writer.WriteEndElement();
		}
		#endregion		
	}
}
