using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Windows.Forms;
namespace Trigger.Tasks
{
	/// 
	/// This  takes care of all available s and automatically loads them on startup
	/// 
	public class Manager
	{
		#region Properties
		/// Our  
		private Main Main;
		
		/// A List of available s
		public List TaskPluginsAvailable;
		/// A List of actually loaded (=active) s
		public List TaskPluginsLoaded = new List();
		/// A List with the instances of the s
		private List TaskPluginInstances = new List();
		/// A flag that indicates whether the loading process should be aborted
		private bool abort = false;
		#endregion
		#region Constructors
		/// 
		/// 
		public Manager(Main Main)
		{
			this.Main = Main;
			List types = this.getAvailableTasks();
			this.TaskPluginsAvailable = types;
			this.Refresh();
		}
		#endregion
		#region Methods
		/// 
		/// Returns the current status of this 
		/// This includes the lists with available and with loaded s
		/// 
		/// 
		public TreeNode GetStatus(TreeView tv)
		{
			TreeNode tnMain = new TreeNode("Task Manager");
			TreeNode tnAvailable = tnMain.Nodes.Add("Available Tasks (" + this.TaskPluginsAvailable.Count + ")");
			this.TaskPluginsAvailable.ForEach(new Action((item) => tnAvailable.Nodes.Add(new TreeNode(item.Name))));
			TreeNode tnLoaded = tnMain.Nodes.Add("Loaded Tasks (" + this.TaskPluginInstances.Count + ")");
			this.TaskPluginInstances.ForEach(new Action((tp) => tnLoaded.Nodes.Add(tp.GetStatus(tv))));
			return tnMain;
		}
		/// 
		/// Gets a list of all available s using Reflection
		/// 
		/// 
		private List getAvailableTasks()
		{
			List types = new List(System.Reflection.Assembly.GetExecutingAssembly().GetTypes());
			types = types.FindAll(new Predicate(item => { return item.BaseType == typeof(TaskPlugin) && item.Namespace.StartsWith("Trigger.Tasks"); }));
			return types;
		}
		/// 
		/// Loads all s that are not found or enabled in settings
		/// 
		private void Refresh()
		{
			ObservableDictionary plugins = TaskOptions.GetPluginsSetting();
			List types = new List(this.TaskPluginsAvailable);
			types = types.FindAll(new Predicate(item => { return !plugins.ContainsKey(item.Name) || plugins[item.Name]; }));
			foreach (Type type in types)
			{
				if (abort)
					break;
				loadTask(type);
			};
			abort = false;
		}
		/// 
		/// Loads all s that are enabled in  and not yet loaded and
		/// unloads all s that are disabled in  and but still loaded
		/// This method is used by  to notify us about changes
		/// The list of plugins and whether they shall be enabled
		/// 
		public void Refresh(ObservableDictionary plugins)
		{
			foreach (KeyValuePair plugin in plugins)
			{
				if (abort)
					break;
				Type type = this.TaskPluginsLoaded.Find(new Predicate(item => { return item.Name == plugin.Key; }));
				if (plugin.Value && type == null)		// task is enabled and not loaded
					this.loadTask(plugin.Key);
				if (!plugin.Value && type != null)
					this.unloadTask(type);
			}
			abort = false;
		}
		/// 
		/// Creates an instance of the specified 
		/// 
		/// 
		private void loadTask(string type)
		{
			this.loadTask(this.TaskPluginsAvailable.Find(new Predicate(item => { return item.Name == type; })));
		}
		/// 
		/// Creates an instance of the specified 
		/// 
		/// 
		private void loadTask(Type type)
		{
			try
			{
#if DEBUG
			System.Diagnostics.Stopwatch swInitEvent = new System.Diagnostics.Stopwatch();
			swInitEvent.Start();
#endif
				TaskPlugin task = (TaskPlugin)Activator.CreateInstance(type);
#if DEBUG
				if (new List(type.GetMethods()).Find(new Predicate(m => { return m.Name == "Init" && m.GetParameters().Length == 2; })) != null)
					task.Init(this.Main, swInitEvent);
				else
					task.Init(this.Main);
#else
				if (task.Init(this.Main))
#endif
				{
					this.TaskPluginInstances.Add(task);
					this.TaskPluginsLoaded.Add(type);
#if DEBUG
					swInitEvent.Stop();
					this.Main.Log.LogLine("Loaded Task  plugin \"" + type.Name + "\" in " + swInitEvent.ElapsedMilliseconds + "ms", Log.Type.Other);
#else
			this.Main.Log.LogLine("Loaded Task  plugin \"" + type.Name + "\"", Log.Type.Other);
#endif
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message + "\n\n" + e.StackTrace, "Error when loading Task \"" + type.Name + "\"!", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}
		/// 
		/// Destructs the instance of the specified 
		/// 
		/// 
		private void unloadTask(Type type)
		{
			try
			{
				TaskPlugin task = this.TaskPluginInstances.Find(new Predicate(item => { return item.GetType() == type; }));				
				task.Dispose();
				this.TaskPluginInstances.Remove(task);
				this.TaskPluginsLoaded.Remove(type);
				this.Main.Log.LogLine("Unloaded Task  plugin \"" + type.Name + "\"", Log.Type.Other);
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message + "\n\n" + e.StackTrace, "Error when unloading Task \"" + type.Name + "\"!", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}
		internal void Abort()
		{
			this.abort = true;
		}
		#endregion
	}
}