#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.Collections;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using dataMagus.Common.Physical;
using dataMagus.Common;
using dataMagus.Common.PlugIn.Generator;
using dataMagus.Common.Physical.Relational;
using dataMagus.Common.Physical.Objectoriented;
#endregion

[assembly: InternalsVisibleTo("dataMagus.Forms")]
[assembly: InternalsVisibleTo("dataMagus")]
namespace dataMagus.Common.Physical
{
	/// <summary>
	/// This singelton class manages all physical schemas in the application.
	/// </summary>
    internal sealed class PhysicalSchemaManager
    {
		#region Members
		/// <summary>
		/// Singelton instance object.
		/// </summary>
        private static PhysicalSchemaManager _instance = null;
        
        /// <summary>
        /// Lock object for thread safe access.
        /// </summary>
        private static readonly object _padlock = new object();
		
        /// <summary>
        /// Holds all loaded schemas implementing the IPhysicalSchema interface.
        /// </summary>
        private InterfaceLoadManager<IPhysicalSchema> _loadedSchemas = new InterfaceLoadManager<IPhysicalSchema>();
		#endregion
		
		#region Constructors / Destructors
		/// <summary>
		/// Default constructor.
		/// </summary>
        private PhysicalSchemaManager()
        {
        }
		#endregion
		
		#region Properties
		/// <summary>
		/// Get the singelton instance of the object.
		/// </summary>
        public static PhysicalSchemaManager Instance
        {
            get
            {
                if (null == PhysicalSchemaManager._instance)
                {
                    lock (PhysicalSchemaManager._padlock)
                    {
                        if (null == PhysicalSchemaManager._instance)
                        {
                            PhysicalSchemaManager._instance = new PhysicalSchemaManager();
                        }
                    }
                }
                return PhysicalSchemaManager._instance;
            }
        }

        /// <summary>
        /// Count all loaded physical schemas.
        /// </summary>
        public int CountLoadedSchemas
        {
            get { return this._loadedSchemas.LoadedInterfaces.Count; }
        }
        #endregion
		
		#region Methodes
		/// <summary>
		/// Get all loaded schemas.
		/// </summary>
		/// <returns>Enumerator to parse.</returns>
        public IEnumerator GetLoadedSchemas()
        {
            return this._loadedSchemas.LoadedInterfaces.GetEnumerator();
        }

        /// <summary>
        /// Unload all schemas.
        /// </summary>
		public void UnLoadSchemas()
		{
			this._loadedSchemas.LoadedInterfaces.Clear();
		}
		
		/// <summary>
		/// Load all schemas implementing IPhysicalSchema from a specified path.
		/// </summary>
		/// <param name="path">Path where the schemas are located.</param>
		public void LoadSchemas(string path)
		{
			this._loadedSchemas.LoadInterfaces(path, "*.dll", 
				new Type[] { typeof(string) },
				new object[] { DataContainer.Instance.LogicalSchema.Id },
				true);
		}
        
		/// <summary>
		/// Find all loaded schemas implementing a specified interface.
		/// </summary>
		/// <param name="type">Interface to search for.</param>
		/// <returns>List of found items.</returns>
		public List<IPhysicalSchema> FindInterfaceType(Type type)
		{
			return this._loadedSchemas.FindInterfaceType(type);
		}

		/// <summary>
		/// Find all loaded schemas implementing a specified type.
		/// </summary>
		/// <param name="type">Type to search for.</param>
		/// <returns>List of found items.</returns>
		public List<IPhysicalSchema> FindType(Type type)
		{
			return this._loadedSchemas.FindType(type);
		}

		/// <summary>
		/// Find all loaded schemas implementing a specified interface by its FQN.
		/// </summary>
		/// <param name="fullname">Full qualified name of the interface.</param>
		/// <returns>List of found items.</returns>
        public List<IPhysicalSchema> FindInterfaceType(string fullname)
        {
            return this._loadedSchemas.FindInterfaceType(fullname);
        }

        /// <summary>
		/// Find all loaded schemas implementing a specified type by its FQN.
        /// </summary>
		/// <param name="fullname">Full qualified name of the type.</param>
		/// <returns>List of found items.</returns>
        public List<IPhysicalSchema> FindType(string fullname)
        {
            return this._loadedSchemas.FindType(fullname);
        }

        /// <summary>
        /// Generate the physical schema out of the logical one.
        /// </summary>
        /// <param name="schemaType">Type of the physical schema to be created.</param>
		/// <param name="userInteraction">Instance for potential user interactions.</param>
        /// <returns>An instance of the generated schema.</returns>
        public IPhysicalSchema Generate(Type schemaType, IUserInteraction userInteraction)
        {
            List<IPhysicalSchema> foundSchema = this.FindType(schemaType);
            if (foundSchema.Count > 0)
            {
                foundSchema[0].Generate(userInteraction);

                // inform all generators about the new schema and give them the chance to keep their
                // internal data up to date
                // --> therefore we create a clone and give them it
                IPhysicalSchema cloneSchema = (IPhysicalSchema)foundSchema[0].Clone();
                List<IGenerator> generators = GeneratorManager.Instance.FindInterfaceTypeForSchema(foundSchema[0]);
                if (null != generators)
                {
                    foreach (IGenerator generator in generators)
                    {
                        try
                        {
                        	generator.CleanExtendedData(cloneSchema);
                        }
                        catch (Exception ex)
                        {
			            	// ignore when generator creates an exception
			            	Trace.TraceError(string.Format("Exception: {0}\r\nStackTrace: {1}", ex.Message, ex.StackTrace));
                        }
                    }
                }
                return foundSchema[0];
            }
			return null;
        }
		#endregion
    }
}
