using Gebhardt.Shared;
using Gebhardt.Shared.Process;
using Gebhardt.StoreWare.Wcs.Common.Unity;
using Gebhardt.StoreWare.Wcs.HostBooking.InterfaceWcsWms;
using System;
using System.Collections.Generic;
using System.Configuration;
using Unity;

namespace Gebhardt.StoreWare.Wcs.HostBooking
{
	internal class Haupt
	{
		[STAThread]
		private static void Main()
		{
			AppDomain.CurrentDomain.UnhandledException += HandleAppDomainException;
			try
			{
				IUnityContainer container = WcsContainerFactory.GetChildInstance();
				container.RegisterFromWmsHandlers();
				container.RegisterFromWmsServices();

				if (AppConfigVerifier.CheckAndWriteToLog())
				{
					// LifeTimer Intervall ist in app.config ctrlTimer eingestellt
					int lifeTimerInterval = Convert.ToInt32(ConfigurationManager.AppSettings["ctrlTimer"]);
					string[] usedConnections = GetUsedConnections();

					ProcessManager manager = new(lifeTimerInterval, true,
						ConfigurationManager.AppSettings["ProcessClass"] == "None" ? ProcessClass.None : ProcessClass.Application,
						usedConnections);
					//Worker (Producer und Consumer) erstellen und verknüpfen
					RegisterWorkers(manager, lifeTimerInterval, container);
					// Starten
					manager.RunWorkers();
				}
			}
			catch (Exception exception)
			{
				Log.WriteException(exception);
				Console.WriteLine("main thread: e: {0} e: {1}", exception.StackTrace, exception);
			}
		}

		private static void HandleAppDomainException(object sender, UnhandledExceptionEventArgs e)
		{
			Exception exception = (Exception)e.ExceptionObject;
			if (exception == null)
			{
				throw new ArgumentNullException(nameof(exception));
			}

			Log.WriteException(exception);
			Console.WriteLine($@"main thread: e: {exception.StackTrace} e: {exception}");
		}

		/// <summary>
		///   Ruft die Conn Einträge aus der App.config ab die nicht leer sind
		/// </summary>
		/// <returns></returns>
		private static string[] GetUsedConnections()
		{
			ProcessParameter parameter = new();
			var connections = new List<string> { parameter.Conn1, parameter.Conn2, parameter.Conn3, parameter.Conn4, parameter.Conn5, parameter.Conn6 };
			string[] usedConnections = connections.FindAll(x => x != "leer").ToArray();
			return usedConnections;
		}

		/// <summary>
		///   Erstellt einen Producer und je nach useLoadBalancing einen oder elf Consumer und weißt diese dem Producer und dem
		///   Manager zu.
		/// </summary>
		/// <param name="manager"></param>
		/// <param name="ctrTimerInterval">ctrTimer aus App.config</param>
		internal static void RegisterWorkers(ProcessManager manager, int ctrTimerInterval, IUnityContainer unityContainer)
		{
			//AppSettings auslesen
			bool useLoadBalancing;
			try
			{
				useLoadBalancing = Convert.ToBoolean(ConfigurationManager.AppSettings["UseLoadBalancing"]);
			}
			catch (Exception e)
			{
				useLoadBalancing = false;
				Log.Write(LogLevel.Error, "Parameter 'useLoadBalancing' fehlt in der App.config, setze auf false");
			}
			int consumerQueueLength;
			try
			{
				consumerQueueLength = Convert.ToInt32(ConfigurationManager.AppSettings["ConsumerQueueLength"]);
			}
			catch (Exception e)
			{
				consumerQueueLength = 200;
				Log.Write(LogLevel.Error, "Parameter 'consumerQueueLength' fehlt in der App.config, setze auf 200");
			}

			// Producer anmelden
			int pollingInterval = Convert.ToInt32(ConfigurationManager.AppSettings["Intervall"]);
			HostBookingProducer producer = new(pollingInterval, useLoadBalancing, consumerQueueLength);
			manager.RegisterWorker(producer);
			string consumerName;
			HostBookingConsumer consumer;

			//Wenn useLoadBalancing = true, dann wird für jede Endziffer der HU ein Consumer erstellt und einer für Telegramme ohne HU (Default)
			if (useLoadBalancing)
			{
				for (int i = 0; i < 10; i++)
				{
					consumerName = $"Consumer_{i}";
					consumer = new HostBookingConsumer(consumerName, ctrTimerInterval / 2, consumerQueueLength, unityContainer);
					producer.AddConsumer(consumerName, consumer);
					manager.RegisterWorker(consumer);
				}
				consumerName = "Consumer_Default";
				consumer = new HostBookingConsumer(consumerName, ctrTimerInterval / 2, consumerQueueLength, unityContainer);
				producer.AddConsumer(consumerName, consumer);
				manager.RegisterWorker(consumer);
			}
			//ohne useLoadBalancing gibt es nur einen Consumer der alle Telegramme verarbeitet
			else
			{
				consumerName = "Consumer_All";
				consumer = new HostBookingConsumer(consumerName, ctrTimerInterval / 2, consumerQueueLength, unityContainer);
				producer.AddConsumer(consumerName, consumer);
				manager.RegisterWorker(consumer);
			}
		}
	}
}