// /home/epsilon/Projects/Asteroid/ABot/Framework1/Framework1/Main.cs created with MonoDevelop
// User: epsilon at 16:14 19.04.2008
//
// project created on 19.04.2008 at 16:14
using System;
using System.Collections.Generic;

namespace Framework1
{
	class MainClass : IPluginHost
	{
		// consts
		//--------
		private const string theUseBotName = "Bot1Delta";
		

		// fields
		//--------
		private NetworkInterface theNetworkInterface;
		private System.Collections.Generic.List<IPlugin> thePlugins = new List<IPlugin>();
		private Playfield thePlayfield = new Playfield();
		private DateTime theStartTime = DateTime.Now;
		private GameCalibration theCalibration = new GameCalibration();
		
		
		// constructor
		private MainClass(string ipAddress)
		{
			// message thresholds
			LogOutput.Threshold = LogOutput.Categories.Warning;
			/*
			LogOutput.SetModuleThreshold("Network", LogOutput.Categories.Info);
			LogOutput.SetModuleThreshold("KeySet", LogOutput.Categories.Info);
			LogOutput.SetModuleThreshold("Bot1Alpha", LogOutput.Categories.Info);
			LogOutput.SetModuleThreshold("Bot1Delta", LogOutput.Categories.Info);
			LogOutput.SetModuleThreshold("Playfield", LogOutput.Categories.Warning);
			LogOutput.SetModuleThreshold("FramePacket", LogOutput.Categories.Info);
			LogOutput.SetModuleThreshold("GLVisualizer", LogOutput.Categories.Info);
			LogOutput.SetModuleThreshold("GLScreen", LogOutput.Categories.Info);
			LogOutput.SetModuleThreshold("DataCollector", LogOutput.Categories.Info);
			LogOutput.SetModuleThreshold("Sprite", LogOutput.Categories.Info);
			LogOutput.SetModuleThreshold("Main", LogOutput.Categories.Info);
			LogOutput.SetModuleThreshold("Main.Plugin", LogOutput.Categories.Info);
			*/
			// start
			LogOutput.LogInfo("Main", "Starting Framework 1");

			this.SearchPlugins();
			
			theNetworkInterface = new NetworkInterface(ipAddress);
			theNetworkInterface.FrameReceived += new FrameReceivedEventHandler(this.NetworkInterface_FrameReceived);
			theNetworkInterface.Start();
			
			// wait for game to end
			while (!thePlayfield.GameEnded)
			{
				System.Threading.Thread.Sleep(100);
				LogOutput.LogDebug("Main", "Screen text: " + thePlayfield.ScreenText);
				LogOutput.LogDebug("Main", "Running time: " + (DateTime.Now - theStartTime));
			}
			
			// stop
			theNetworkInterface.Stop();
			StopPlugins();
			LogOutput.LogInfo("Main", "Total running time: " + (DateTime.Now - theStartTime));
		}

		// Entry point
		public static void Main(string[] args)
		{
            if (args.Length != 1)
            {
                Console.WriteLine("Need parameter <IP-Address>\n");
                Environment.Exit(1);
            }

			//MainClass program = new MainClass(args[0]);
			new MainClass(args[0]);			
		}
		
		// methods
		private void SearchPlugins()
		{
			LogOutput.LogInfo("Main.Plugin", "Searching for plugins");
			thePlugins.Clear();

			// application directory
            string[] files = System.IO.Directory.GetFiles(System.Threading.Thread.GetDomain().BaseDirectory);

			foreach (string file in files)
            {
                // pick only DLL files
                if (file.EndsWith(".dll")
				    && System.IO.Path.GetFileNameWithoutExtension(file) != "GLUtils" // do not our utility libraries
				    && System.IO.Path.GetFileNameWithoutExtension(file) != "OutputUtils" // do not our utility libraries
				    && System.IO.Path.GetFileNameWithoutExtension(file) != "Tao.Sdl" // do not load tao libraries
				    && System.IO.Path.GetFileNameWithoutExtension(file) != "Tao.OpenGl") // do not load tao libraries
                {
                    try
                    {
                        string filename = System.IO.Path.GetFileNameWithoutExtension(file);
						LogOutput.LogDebug("Main.Plugin", "Checking file: " + filename);

                        // try loading the assembly
                        System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(filename);
                        if (assembly != null)
                        {
							LogOutput.LogDebug("Main.Plugin", "Checking assembly: " + filename);

							// search assembly for a MovePlugin class
							System.Type[] types = assembly.GetTypes();
							foreach (System.Type type in types)
							{
								LogOutput.LogDebug("Main.Plugin", "Checking type: " + type.Name);
								if (!type.IsAbstract)// && type.IsInterface)
								{
									// create instance of the plugin and register as host
									try 
									{
										IPlugin plugin = (IPlugin)Activator.CreateInstance(type);

										if (plugin != null)
										{
											LogOutput.LogDebug("Main.Plugin", "Found plugin: "
											                   + plugin.ToString() + " (" + type.Name + ")");
											plugin.Host = this;
										}
									}
									catch(System.InvalidCastException)
									{
										LogOutput.LogDebug("Main.Plugin", "Cannot convert '" + type.Name
										                   + "' into plugin. Skipping type");
									}
									catch(System.MissingMethodException)
									{
										LogOutput.LogDebug("Main.Plugin", "No standard constructor of '" + type.Name
										                   + "' found. Skipping type");
									}
								}
							}
                        }
                    }
                    catch(Exception e)
                    {
						LogOutput.LogError("Main.Plugin", "received unhandled error message during plugin search: " + e.ToString());
                    }
                }
            }
		}
		
		/// Add plugin to the list of known plugins
		public void RegisterPlugin(IPlugin plugin)
		{
			LogOutput.LogInfo("Main.Plugin", "Plugin registered: " + plugin.ToString());
			thePlugins.Add(plugin);
			
			if (plugin.PluginType == PluginTypes.BotPlugin
			    && (theUseBotName == "" || plugin.ToString() == theUseBotName))
			{
				LogOutput.LogInfo("Main.Plugin", "Using bot plugin: " + plugin.ToString());

				// allow plugin to change keys
				((IBotPlugin)plugin).ChangeKeys += new ChangeKeysEventHandler(this.Plugin_ChangeKeys);

				// allow plugin to change bot status
				((IBotPlugin)plugin).ChangeBotStatus += new ChangeBotStatusEventHandler(this.Plugin_ChangeBotStatus);
			}
			
			if (plugin.PluginType == PluginTypes.VisualizationPlugin)
			{
				// start visualization
				
				LogOutput.LogInfo("Main.Plugin", "Creating new thread for plugin " + plugin.ToString());

				System.Threading.Thread thePluginThread = 
					new System.Threading.Thread(new System.Threading.ThreadStart(((IVisualizationPlugin)plugin).StartVisualization));
				thePluginThread.IsBackground=true;
				thePluginThread.Start();
			}

			if (plugin.PluginType == PluginTypes.StatisticsPlugin)
			{
				// allow plugin to change calibration
				((IStatisticsPlugin)plugin).ChangeCalibration += new ChangeCalibrationEventHandler(this.Plugin_ChangeCalibration);
			}
		}
		
		private void StopPlugins()
		{
			LogOutput.LogInfo("Main.Plugin", "Stopping all visualization plugins");
			
			foreach (IPlugin plugin in thePlugins)
			{
				// stop all visualization plugins
				if (plugin.PluginType == PluginTypes.VisualizationPlugin)
					((IVisualizationPlugin)plugin).StopVisualization();
			}
		}
		
		// events methods
		private void Plugin_ChangeKeys(object sender, ChangeKeysEventArgs e)
		{
			LogOutput.LogDebug("Main", "Request to send keys " + e.keys.ToString());
			theNetworkInterface.CurrentKeys=e.keys;
			
			foreach (IPlugin plugin in thePlugins)
			{
				plugin.UpdateCurrentKeys(e.keys);
			}
		}
		
		private void Plugin_ChangeBotStatus(object sender, ChangeBotStatusEventArgs e)
		{
			LogOutput.LogDebug("Main", "Bot status changes: " + e.status.ToString());
			
			foreach (IPlugin plugin in thePlugins)
			{
				plugin.UpdateBotStatus(e.status);
			}
		}

		private void Plugin_ChangeCalibration(object sender, ChangeCalibrationEventArgs e)
		{
			LogOutput.LogDebug("Main", "Game calibration changes: " + e.calibration.ToString());
			theCalibration = e.calibration;
			
			foreach (IPlugin plugin in thePlugins)
			{
				plugin.UpdateCalibration(theCalibration.Clone());
			}
		}

		private void NetworkInterface_FrameReceived(object sender, FrameReceivedEventArgs e)
		{
			LogOutput.LogDebug("Main", "Frame received");
			
			if (e.framePackage == null)
				LogOutput.LogError("Main", "Received frame packet is a null pointer");
			
			thePlayfield.Update(e.framePackage);
			
			foreach (IPlugin plugin in thePlugins)
			{
				plugin.UpdatePlayfield(thePlayfield);
			}
		}
	}
}