using System;
using System.Reflection;
using System.Collections;
using System.Collections.Specialized;

namespace Icarus.Logic.Controllers
{
	/// <summary>
	/// This guy provides us with controllers.
	/// </summary>
	public sealed class ControllerFactory
	{
		#region Singleton Stuff
		/// <summary>
		/// Singleton Implementation.
		/// </summary>
		private static ControllerFactory mgInstance = new ControllerFactory();

		/// <summary>
		/// Singleton Implementation.
		/// </summary>
		public static ControllerFactory Instance
		{
			get { return ControllerFactory.mgInstance; }
		}
		#endregion

		#region Private Parts
		/// <summary>
		/// The default controller.
		/// </summary>
		private IController mDefaultController = new DefaultController();

		/// <summary>
		/// The dictionary of registered controllers. This is so that we don't have to look up the type through reflection every time.
		/// </summary>
		private IDictionary mControllerTypes;

		/// <summary>
		/// Initializes the controller factory.
		/// </summary>
		private ControllerFactory()
		{
			// Initialize the dictinary of controllers:
			mControllerTypes = new ListDictionary();

			// Register the default controller:
			this.RegisterControllerType(mDefaultController.GetType().FullName);
		}

		/// <summary>
		/// Registers the controller type with the dictionary.
		/// </summary>
		/// <param name="fullName"></param>
		private void RegisterControllerType(string fullName)
		{
			// First, we need to actually find this type they speak of:
			Type controllerType = GetTypeForName(fullName);

			//TODO: Check that controllerType implements IController.

			// Add this to the list:
			mControllerTypes.Add(controllerType.FullName, controllerType);
		}

		/// <summary>
		/// Returns the type corresponding to the name passed in, or null if the type is undefined.
		/// </summary>
		/// <param name="fullName"></param>
		/// <returns></returns>
		private Type GetTypeForName(string fullName)
		{
			// Get the main executable assembly:
			Assembly entryAssembly = Assembly.GetEntryAssembly();

			// See if the type is defined in that assembly:
			Type result = entryAssembly.GetType(fullName, false, false);
			if (result != null) { return result; }

			// Get the list of referenced assemblies:
			AssemblyName[] referencedAssemblies = entryAssembly.GetReferencedAssemblies();

			// Check each referenced assembly for the type:
			foreach (AssemblyName aName in referencedAssemblies)
			{
				Assembly a = Assembly.Load(aName);
				result = a.GetType(fullName, false, false);

				if (result != null) { return result; }
			}

			return null;
		}

		/// <summary>
		/// Returns a controller for the specified type.
		/// </summary>
		/// <param name="t"></param>
		/// <returns></returns>
		private IController GetControllerForType(Type t)
		{
			return (IController) t.GetConstructor(Type.EmptyTypes).Invoke(null);
		}
		#endregion

		/// <summary>
		/// Returns the default controller for objects.
		/// </summary>
		public IController DefaultController
		{
			get { return this.mDefaultController; }
		}

		/// <summary>
		/// Returns a controller for the type whose fully qualified name is supplied. Of course, note that if the specified type is not actually a controller, then bad things will happen (exceptions).
		/// </summary>
		/// <param name="type"></param>
		/// <returns></returns>
		public IController GetControllerForType(string type)
		{
			if (!mControllerTypes.Contains(type))
			{
				this.RegisterControllerType(type);
			}

			return this.GetControllerForType(mControllerTypes[type] as Type);
		}
	}
}
