using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Threading;
using System.Windows;

using BeWo.Multitenancy;
using BeWo.QueryService;
using BeWo.Security;

using BS.Shared;
using BS.Shared.DataContracts;
using BS.Shared.DataContracts.Compact;

namespace BeWo.ServiceProxy
{
	// Ich verlass mich jetzt mal hierdrauf:
	// http://dotnetslackers.com/WSE/re-51155_WCF_Proxy_Performance_vs_WSE_V3.aspx
	// Man muss also nicht schließen wenn nur eine Instanz vom Proxy gehalten wird
	// und Services PerCall konfiguriert sind...
	public static class ServiceFacade
	{
		public static object _Lock = string.Empty;

		private static Dictionary<Type, EndpointAddress> _EndpointAddresses;

		private static readonly Dictionary<Type, object> _ProxyCache;

		private static int _RunningAsyncs;

		// Nur um die EndpointAddresses wegen neuer SiteOfOrigin zu aktualisieren
		public static Dictionary<Type, EndpointAddress> EndpointAddresses
		{
			get { return _EndpointAddresses; }

		}

		public static void UpdateServerAddresses(String siteOfOrigin)
		{

			String serverName = siteOfOrigin;
			var test = serverName.Substring(serverName.Length - 1, 1);
			if (serverName.Substring(serverName.Length - 1, 1) != "/")
				serverName += "/";
			_EndpointAddresses = new Dictionary<Type, EndpointAddress>
            {
                { typeof(IQueryService), new EndpointAddress(serverName + "QueryService.svc") }, 
                { typeof(IStreamingService), new EndpointAddress(serverName + "StreamingService.svc") }, 
                { typeof(IEmployeeService), new EndpointAddress(serverName + "EmployeeService.svc") }, 
                { typeof(ICustomerService), new EndpointAddress(serverName + "CustomerService.svc") }, 
                { typeof(IUserService), new EndpointAddress(serverName + "UserService.svc") }, 
                { typeof(IValueListService), new EndpointAddress(serverName + "ValueListService.svc") }, 
                { typeof(IDownloadService), new EndpointAddress(serverName + "DownloadService.svc") }, 
                { typeof(IResourceService), new EndpointAddress(serverName + "ResourceService.svc") }, 
                { typeof(IOperationsService), new EndpointAddress(serverName + "OperationsService.svc") }, 
                { typeof(IAnalysisService), new EndpointAddress(serverName + "AnalysisService.svc") }, 
                { typeof(IMailService), new EndpointAddress(serverName + "MailService.svc") }, 
                { typeof(IAccountingService), new EndpointAddress(serverName + "AccountingService.svc") },
				{ typeof(IReportService), new EndpointAddress(serverName + "ReportService.svc") }
            };

		}

		static ServiceFacade()
		{
			_ProxyCache = new Dictionary<Type, object>();
		}

		public static void DoAccountingServiceAsync<T>(Func<IAccountingService, T> pFunc, Action<T> pCallBack, bool showWaitDialog)
		{
			DoAsync<AccountingServiceClient, IAccountingService, T>(pFunc, pCallBack, showWaitDialog);
		}
		public static void DoAccountingServiceAsync<T>(Func<IAccountingService, T> pFunc, Action<T> pCallBack)
		{
			DoAsync<AccountingServiceClient, IAccountingService, T>(pFunc, pCallBack, true);
		}

		public static void DoAccountingServiceAsync(Action<IAccountingService> pAction)
		{
			DoAsync<AccountingServiceClient, IAccountingService>(pAction, true);
		}

		public static void DoAccountingServiceAsync(Action<IAccountingService> pAction, Action callback)
		{
			DoAsync<AccountingServiceClient, IAccountingService>(pAction, callback, true);
		}

		public static void DoAnalysisServiceAsync<T>(Func<IAnalysisService, T> pFunc, Action<T> pCallBack)
		{
			DoAsync<AnalysisServiceClient, IAnalysisService, T>(pFunc, pCallBack, true);
			
		}
		
		public static void DoAccountingServiceSync(Action<IAccountingService> pAction)
        {
            pAction.Invoke(GetInstance<AccountingServiceClient, IAccountingService>());
        }

		//
		public static void DoAnalysisServiceAsync(Action<IAnalysisService> pAction)
		{
			DoAsync<AnalysisServiceClient, IAnalysisService>(pAction, true);
		}

		public static void DoCustomerServiceAsync<T>(Func<ICustomerService, T> pFunc, Action<T> pCallBack, bool showWaitDialog)
		{
			DoAsync<CustomerServiceClient, ICustomerService, T>(pFunc, pCallBack, showWaitDialog);
		}

		public static void DoCustomerServiceAsync<T>(Func<ICustomerService, T> pFunc, Action<T> pCallBack)
		{
			DoAsync<CustomerServiceClient, ICustomerService, T>(pFunc, pCallBack, true);
		}

		public static void DoCustomerServiceAsync(Action<ICustomerService> pAction)
		{
			DoAsync<CustomerServiceClient, ICustomerService>(pAction, true);
		}

		public static void DoCustomerServiceSync(Action<ICustomerService> pAction)
		{
			pAction.Invoke(GetInstance<CustomerServiceClient, ICustomerService>());
		}

		public static T DoCustomerServiceSync<T>(Func<ICustomerService, T> pFunc)
		{
			try
			{
				return pFunc.Invoke(GetInstance<CustomerServiceClient, ICustomerService>());
			}
			catch (Exception e)
			{
				ShowErrorMessage(e);
			}

			return default(T);
		}

		public static void DoDownloadServiceAsync<T>(Func<IDownloadService, T> pFunc, Action<T> pCallBack)
		{
			DoAsync<DownloadServiceClient, IDownloadService, T>(pFunc, pCallBack, true);
		}

		public static void DoDownloadServiceAsync(Action<IDownloadService> pAction)
		{
			DoAsync<DownloadServiceClient, IDownloadService>(pAction, true);
		}

		public static void DoDownloadServiceSync(Action<IDownloadService> pAction)
		{
			try
			{
				pAction.Invoke(GetInstance<DownloadServiceClient, IDownloadService>());
			}
			catch (Exception e)
			{
				// if (HandleServiceProxyException(e))
				// throw e;
				ShowErrorMessage(e);
			}
		}

		public static void DoEmployeeServiceAsync<T>(Func<IEmployeeService, T> pFunc, Action<T> pCallBack, bool showWaitDialog)
		{
			DoAsync<EmployeeServiceClient, IEmployeeService, T>(pFunc, pCallBack, showWaitDialog);
		}

		public static void DoEmployeeServiceAsync<T>(Func<IEmployeeService, T> pFunc, Action<T> pCallBack)
		{
			DoAsync<EmployeeServiceClient, IEmployeeService, T>(pFunc, pCallBack, true);
		}

		public static void DoEmployeeServiceAsync(Action<IEmployeeService> pAction)
		{
			DoAsync<EmployeeServiceClient, IEmployeeService>(pAction, true);
		}

		public static void DoEmployeeServiceSync(Action<IEmployeeService> pAction)
		{
			try
			{
				pAction.Invoke(GetInstance<EmployeeServiceClient, IEmployeeService>());
			}
			catch (Exception e)
			{
				ShowErrorMessage(e);
			}
		}

	    public static T DoEmployeeServiceSync<T>(Func<IEmployeeService, T> pFunc)
	    {
	        try
	        {
                return pFunc.Invoke(GetInstance<EmployeeServiceClient, IEmployeeService>());
	        }
            catch (Exception e)
            {
                ShowErrorMessage(e);
            }

            return default(T);
	    }

        public static void DoWohnheimServiceAsync<T>(Func<ICustomerService, T> pFunc, Action<T> pCallBack, bool showWaitDialog)
        {
            DoAsync<CustomerServiceClient, ICustomerService, T>(pFunc, pCallBack, showWaitDialog);
        }

        public static void DoWohnheimServiceAsync<T>(Func<ICustomerService, T> pFunc, Action<T> pCallBack)
        {
            DoAsync<CustomerServiceClient, ICustomerService, T>(pFunc, pCallBack, true);
        }

        public static void DoWohnheimServiceAsync(Action<ICustomerService> pAction)
        {
            DoAsync<CustomerServiceClient,ICustomerService>(pAction, true);
        }

        public static void DoWohnheimServiceSync(Action<ICustomerService> pAction)
        {
            try
            {
                pAction.Invoke(GetInstance<CustomerServiceClient, ICustomerService>());
            }
            catch (Exception e)
            {
                ShowErrorMessage(e);
            }
        }
		
        public static void DoMailServiceAsync<T>(Func<IMailService, T> pFunc, Action<T> pCallBack)
		{
			DoAsync<MailServiceClient, IMailService, T>(pFunc, pCallBack, true);
		}

		public static void DoMailServiceAsync(Action<IMailService> pAction)
		{
			DoAsync<MailServiceClient, IMailService>(pAction, true);
		}

		public static T DoMailServiceSync<T>(Func<IMailService, T> pFunc)
		{
			try
			{
				return pFunc.Invoke(GetInstance<MailServiceClient, IMailService>());
			}
			catch (Exception e)
			{
				ShowErrorMessage(e);
			}

			return default(T);
		}
		
		public static void DoMultipleAccountingServicesAsync(List<Action<IAccountingService>> pActions, Action pAllDoneCallBack)
		{
			DoMulitpleAsync<AccountingServiceClient, IAccountingService>(pActions, pAllDoneCallBack, true);
		}

		public static void DoMultipleCustomerServicesAsync(List<Action<ICustomerService>> pActions, Action pAllDoneCallBack)
		{
			DoMulitpleAsync<CustomerServiceClient, ICustomerService>(pActions, pAllDoneCallBack, true);
		}

		public static void DoMultipleEmployeeServicesAsync(List<Action<IEmployeeService>> pActions, Action pAllDoneCallBack)
		{
			DoMulitpleAsync<EmployeeServiceClient, IEmployeeService>(pActions, pAllDoneCallBack, true);
		}

		public static void DoMultipleEmployeeServicesAsync(List<Action<IEmployeeService>> pActions, Action pAllDoneCallBack, bool showWaitDialog)
		{
			DoMulitpleAsync<EmployeeServiceClient, IEmployeeService>(pActions, pAllDoneCallBack, showWaitDialog);
		}

		public static void DoMultipleMailServicesAsync(List<Action<IMailService>> pActions, Action pAllDoneCallBack)
		{
			DoMulitpleAsync<MailServiceClient, IMailService>(pActions, pAllDoneCallBack, true);
		}

		public static void DoMultipleOperationsServicesAsync(List<Action<IOperationsService>> pActions, Action pAllDoneCallBack)
		{
			DoMulitpleAsync<OperationsServiceClient, IOperationsService>(pActions, pAllDoneCallBack, true);
		}

		public static void DoMultipleOperationsServicesSync(List<Action<IOperationsService>> pActions)
		{
			DoMulitpleSync<OperationsServiceClient, IOperationsService>(pActions, true);
		}

		public static void DoMultipleResourceServicesAsync(List<Action<IResourceService>> pActions, Action pAllDoneCallBack)
		{
			DoMulitpleAsync<ResourceServiceClient, IResourceService>(pActions, pAllDoneCallBack, true);
		}

		public static void DoMultipleValueListServicesAsync(List<Action<IValueListService>> pActions, Action pAllDoneCallBack)
		{
			DoMulitpleAsync<ValueListServiceClient, IValueListService>(pActions, pAllDoneCallBack, true);
		}

		public static void DoOperationsServiceAsync<T>(Func<IOperationsService, T> pFunc, Action<T> pCallBack, bool showWaitDialog)
		{
			DoAsync<OperationsServiceClient, IOperationsService, T>(pFunc, pCallBack, showWaitDialog);
		}

		public static void DoOperationsServiceAsync<T>(Func<IOperationsService, T> pFunc, Action<T> pCallBack)
		{
			DoAsync<OperationsServiceClient, IOperationsService, T>(pFunc, pCallBack, true);
		}

		public static void DoOperationsServiceAsync(Action<IOperationsService> pAction, Action pCallBack)
		{
			DoAsync<OperationsServiceClient, IOperationsService>(pAction, pCallBack, true);
		}

		public static void DoOperationsServiceAsync(Action<IOperationsService> pAction)
		{
			DoAsync<OperationsServiceClient, IOperationsService>(pAction, true);
		}

		public static void DoOperationsServiceSync(Action<IOperationsService> pAction)
		{
			try
			{
				pAction.Invoke(GetInstance<OperationsServiceClient, IOperationsService>());
			}
			catch (Exception e)
			{
				ShowErrorMessage(e);
			}
		}

		public static void DoOperationsServiceAsyncNoWait<T>(Func<IOperationsService, T> pFunc, Action<T, object> pCallBack, object state)
		{
			DoAsync<OperationsServiceClient, IOperationsService, T>(pFunc, cb => pCallBack(cb, state), false);
		}

		public static T DoOperationsServiceSync<T>(Func<IOperationsService, T> pFunc)
		{
			try
			{
				return pFunc.Invoke(GetInstance<OperationsServiceClient, IOperationsService>());
			}
			catch (Exception e)
			{
				ShowErrorMessage(e);
			}

			return default(T);
		}

		public static void DoQueryServiceAsync<T>(Func<IQueryService, T> pFunc, Action<T> pCallBack)
		{
			DoAsync<QueryServiceClient, IQueryService, T>(pFunc, pCallBack, true);
		}

		public static void DoQueryServiceAsync(Action<IQueryService> pAction)
		{
			DoAsync<QueryServiceClient, IQueryService>(pAction, true);
		}

		public static T DoQueryServiceSync<T>(Func<IQueryService, T> pFunc)
		{
			try
			{
				return pFunc.Invoke(GetInstance<QueryServiceClient, IQueryService>());
			}
			catch (Exception e)
			{
				ShowErrorMessage(e);
			}

			return default(T);
		}

		public static void DoResourceServiceSync<T>(Func<IResourceService, T> pFunc)
		{
			try
			{
				pFunc.Invoke(GetInstance<ResourceServiceClient, IResourceService>());
			}
			catch (Exception e)
			{
				ShowErrorMessage(e);
			}
		}

		public static void DoResourceServiceAsync<T>(Func<IResourceService, T> pFunc, Action<T> pCallBack, bool showWaitDialog)
		{
			DoAsync<ResourceServiceClient, IResourceService, T>(pFunc, pCallBack, showWaitDialog);
		}

		public static void DoResourceServiceAsync<T>(Func<IResourceService, T> pFunc, Action<T> pCallBack)
		{
			DoAsync<ResourceServiceClient, IResourceService, T>(pFunc, pCallBack, true);
		}

		public static void DoResourceServiceAsync(Action<IResourceService> pAction)
		{
			DoAsync<ResourceServiceClient, IResourceService>(pAction, true);
		}

		public static void DoStreamingServiceAsync<T>(Func<IStreamingService, T> pFunc, Action<T> pCallBack, Action pErrorCallBack)
		{
			DoAsync<StreamingServiceClient, IStreamingService, T>(pFunc, pCallBack, pErrorCallBack, true);
		}

		public static void DoStreamingServiceAsync(Action<IStreamingService> pAction)
		{
			DoAsync<StreamingServiceClient, IStreamingService>(pAction, true);
		}

		public static void DoUserServiceAsync<T>(Func<IUserService, T> pFunc, Action<T> pCallBack, bool showWaitDialog)
		{
			DoAsync<UserServiceClient, IUserService, T>(pFunc, pCallBack, showWaitDialog);
		}

        public static T DoUserServiceSync<T>(Func<IUserService, T> pFunc)
        {
            return pFunc.Invoke(GetInstance<UserServiceClient, IUserService>());
        }

		public static void DoUserServiceAsync(Action<IUserService> pAction, bool showWaitDialog)
		{
			DoAsync<UserServiceClient, IUserService>(pAction, showWaitDialog);
		}

	    public static void DoUserServiceAsync(Action<IUserService> pAction, Action pCallBack, bool showWaitDialog)
	    {
	        DoAsync<UserServiceClient, IUserService>(pAction, pCallBack, showWaitDialog);
	    }

		public static void DoValueListServiceAsync<T>(Func<IValueListService, T> pFunc, Action<T> pCallBack)
		{
			DoAsync<ValueListServiceClient, IValueListService, T>(pFunc, pCallBack, true);
		}

		public static void DoValueListServiceSync<T>(Func<IValueListService, T> pFunc, Action<T> pCallBack)
		{
			DoAsync<ValueListServiceClient, IValueListService, T>(pFunc, pCallBack, true);
		}

		public static T DoValueListServiceSync<T>(Func<IValueListService, T> pFunc)
		{
			try
			{
				return pFunc.Invoke(GetInstance<ValueListServiceClient, IValueListService>());
			}
			catch (Exception e)
			{
				ShowErrorMessage(e);
			}

			return default(T);
		}

		public static void DoValueListServiceAsync(Action<IValueListService> pAction)
		{
			DoAsync<ValueListServiceClient, IValueListService>(pAction, true);
		}

		public static void DoReportServiceAsync<T>(Func<IReportService, T> pFunc, Action<T> pCallBack)
		{
			DoAsync<ReportServiceClient, IReportService, T>(pFunc, pCallBack, true);
		}

		public static void DoReportServiceSync(Action<IReportService> pAction)
		{
			try
			{
				pAction.Invoke(GetInstance<ReportServiceClient, IReportService>());
			}
			catch (Exception e)
			{
				ShowErrorMessage(e);
			}
		}

		public static void DoReportServiceAsync(Action<IReportService> pAction)
		{
			DoAsync<ReportServiceClient, IReportService>(pAction, true);
		}

		public static T DoReportServiceSync<T>(Func<IReportService, T> pFunc)
		{
			try
			{
				return pFunc.Invoke(GetInstance<ReportServiceClient, IReportService>());
			}
			catch (Exception e)
			{
				ShowErrorMessage(e);
			}

			return default(T);
		}

		public static void GetAll<T>(Action<List<T>> pCallBack)
		{
			if (typeof(T).Equals(typeof(CompactCustomerDC)))
			{
				long? oid = null;

				if (!BeWoApp.LoggedOnUser.HasRight(UserRightType.ViewAll) && !BeWoApp.LoggedOnUser.HasRight(UserRightType.CustomerView_View))
				{
					oid = BeWoApp.LoggedOnUser.Employee.EmployeeOid;
				}

				DoCustomerServiceAsync(
					s => s.GetAllActiveCustomersCompact(false, oid),
					r =>
					{
						List<CompactCustomerDC> list = r;

						if (oid != null)
						{
							list = list.Where(c => c.IsRelatedToEmployee).ToList();
						}

						list.Sort((x, y) => { return (x.LastName + ", " + x.FirstName).CompareTo(y.LastName + ", " + y.FirstName); }); // (Comparison<CompactCustomerDC>)new CustomerSorter().GetComparison("LastName", BeWo.Core.AbstractSorter.SortDirection.Ascending));
						pCallBack(list.Cast<T>().ToList());
					});
			}
			else if (typeof(T).Equals(typeof(CompactEmployeeDC)))
			{
				DoEmployeeServiceAsync(s => s.GetAllActiveEmployeesCompact().Cast<T>().ToList(), r => pCallBack(r));
			}
			else if (typeof(T).Equals(typeof(CompactTeamDC)))
			{
				DoEmployeeServiceAsync(s => s.GetAllTeamsCompact().Cast<T>().ToList(), r => pCallBack(r));
			}
			else if (typeof(T).Equals(typeof(CompactOrganisationDC)))
			{
				DoCustomerServiceAsync(s => s.GetAllActiveOrganisationsCompact().Cast<T>().ToList(), r => pCallBack(r));
			}
			else if (typeof(T).Equals(typeof(CompactPersonDC)))
			{
				DoCustomerServiceAsync(s => s.GetAllActivePersonsCompact().Cast<T>().ToList(), r => pCallBack(r));
			}
			else if (typeof(T).Equals(typeof(CompactSupportConceptDC)))
			{
				DoCustomerServiceAsync(s => s.GetAllActiveSupportConceptsCompact().Cast<T>().ToList(), r => pCallBack(r));
			}
			else if (typeof(T).Equals(typeof(CompactUserDC)))
			{
				DoUserServiceAsync(s => s.GetAllUsersCompact().Cast<T>().ToList(), r => pCallBack(r), true);
			}
			else if (typeof (T) == typeof (ResourceDC))
			{
				DoResourceServiceAsync(s => s.GetAllResources().Cast<T>().ToList(), pCallBack, true);
			}
            else if (typeof (T) == typeof (TextbausteinDC))
            {
                DoOperationsServiceAsync(s => s.GetAllTextbausteine().Cast<T>().ToList(), pCallBack);
            }
			else
			{
				throw new NotImplementedException();
			}
		}

		// returnvalue with callback
		private static void DoAsync<TClient, TInterface, T>(Func<TInterface, T> pFunc, Action<T> pCallback,
			bool showWaitDialog)
			where TClient : ClientBase<TInterface>, TInterface, new()
			where TInterface : class
		{
			DoAsync<TClient, TInterface, T>(pFunc, pCallback, null, showWaitDialog);
		}

		private static void DoAsync<TClient, TInterface, T>(Func<TInterface, T> pFunc, Action<T> pCallback, Action pErrorCallback, bool showWaitDialog)
			where TClient : ClientBase<TInterface>, TInterface, new()
			where TInterface : class
		{
			try
			{
				if (showWaitDialog)
				{
					StartWaiting();
				}

				pFunc.BeginInvoke(
					GetInstance<TClient, TInterface>(),
					cb =>
					{
						try
						{
							T lResult = pFunc.EndInvoke(cb);
							if (pCallback != null)
							{
								pCallback(lResult);
							}

							if (showWaitDialog)
							{
								EndWaiting();
							}
						}
						catch (Exception e)
						{
							// bool lThrow = ShowErrorMessage(e);
							// if (showWaitDialog) EndWaiting();
							// if (lThrow)
							// throw e;
							if (showWaitDialog)
							{
								EndWaiting();
							}

							ShowErrorMessage(e);
							if (pErrorCallback != null)
							{
								pErrorCallback();
							}
						}
					},
					null);
			}
			catch (Exception e)
			{
				// bool lThrow = ShowErrorMessage(e);
				// if (showWaitDialog) EndWaiting();
				// if (lThrow)
				// throw e;
				if (showWaitDialog)
				{
					EndWaiting();
				}

				ShowErrorMessage(e);
			}
		}

		// void without callback
		private static void DoAsync<TClient, TInterface>(Action<TInterface> pAction, bool showWaitDialog)
			where TClient : ClientBase<TInterface>, TInterface, new()
			where TInterface : class
		{
			DoAsync<TClient, TInterface>(pAction, null, showWaitDialog);
		}

		// private static void DoAsync<TClient, TInterface>(Action<TInterface> pAction, Action pCallback, bool )
		// void with callback
		private static void DoAsync<TClient, TInterface>(Action<TInterface> pAction, Action pCallback, bool showWaitDialog)
			where TClient : ClientBase<TInterface>, TInterface, new()
			where TInterface : class
		{
			try
			{
				if (showWaitDialog)
				{
					StartWaiting();
				}

				pAction.BeginInvoke(
					GetInstance<TClient, TInterface>(),
					cb =>
					{
						try
						{
							pAction.EndInvoke(cb);
							if (pCallback != null)
							{
								pCallback();
							}

							if (showWaitDialog)
							{
								EndWaiting();
							}
						}
						catch (Exception e)
						{
							// bool lThrow = ShowErrorMessage(e);
							// if (showWaitDialog) EndWaiting();
							// if (lThrow)
							// throw e;
							if (showWaitDialog)
							{
								EndWaiting();
							}

							ShowErrorMessage(e);
						}
					},
					null);
			}
			catch (Exception e)
			{
				// bool lThrow = ShowErrorMessage(e);
				// if (showWaitDialog) EndWaiting();
				// if (lThrow)
				// throw e;
				if (showWaitDialog)
				{
					EndWaiting();
				}

				ShowErrorMessage(e);
			}
		}

		private static void DoMulitpleAsync<TClient, TInterface>(List<Action<TInterface>> pActions, Action pAllDoneCallBack, bool showWaitDialog)
			where TClient : ClientBase<TInterface>, TInterface, new()
			where TInterface : class
		{
			int lCount = pActions.Count;
			if (lCount == 0)
			{
				pAllDoneCallBack();
			}

			foreach (var iAction in pActions)
			{
				DoAsync<TClient, TInterface>(
					iAction,
					delegate
					{
						Monitor.Enter(_Lock);
						lCount--;
						Monitor.Exit(_Lock);
						if (lCount == 0)
						{
							pAllDoneCallBack();
						}
					},
					showWaitDialog);
			}
		}

		private static void DoMulitpleSync<TClient, TInterface>(List<Action<TInterface>> pActions, bool showWaitDialog)
			where TClient : ClientBase<TInterface>, TInterface, new()
			where TInterface : class
		{
			int lCount = pActions.Count;

			if (showWaitDialog)
			{
				StartWaiting();
			}
			foreach (var iAction in pActions)
			{
				iAction.Invoke(GetInstance<TClient, TInterface>());
			}
			if (showWaitDialog)
			{
				EndWaiting();
			}
		}

		private static void EndWaiting()
		{
			Monitor.Enter(_Lock);
			if ((--_RunningAsyncs) == 0 && BeWoApp.MainControl != null)
			{
				BeWoApp.MainControl.EndWaiting();
			}

			Monitor.Exit(_Lock);
		}

		private static TInterface GetInstance<TClient, TInterface>()
			where TClient : ClientBase<TInterface>, TInterface, new()
			where TInterface : class
		{
			TClient lClient = null;
			if (_ProxyCache.ContainsKey(typeof(TClient)))
			{
				lClient = (TClient)_ProxyCache[typeof(TClient)];

				if (lClient.State == CommunicationState.Closing || lClient.State == CommunicationState.Closed || lClient.State == CommunicationState.Faulted)
				{
					if (lClient.State == CommunicationState.Faulted)
					{
						lClient.Abort();
					}

					lClient = null;
					_ProxyCache.Remove(typeof(TClient));
				}
			}

			if (lClient != null)
			{// EndpointAddresses aktualisieren
				if (!lClient.Endpoint.Address.Equals(EndpointAddresses[typeof(TInterface)]))
				{
					if (lClient.State == CommunicationState.Opened)
						lClient.Close();

					if (_ProxyCache.ContainsKey(typeof(TClient)))
						_ProxyCache.Remove(typeof(TClient));
					lClient = null;
				}
			}

			if (lClient == null)
			{
				lClient = new TClient();
				_ProxyCache.Add(typeof(TClient), lClient);
				lClient.Endpoint.Behaviors.Add(new SecurityHeaderEndpointBehavior());
				lClient.Endpoint.Behaviors.Add(new MultitenancyEndpointBehavior());

				lClient.Endpoint.Address = EndpointAddresses[typeof(TInterface)];

				
			}


			return lClient;
		}

		private static void ShowErrorMessage(Exception e)
		{
			if (e is FaultException<BeWoFault>)
			{
				var lBeWoFault = (e as FaultException<BeWoFault>).Detail;

				String rowUpdated = "Der Datensatz wurde in der Zwischenzeit von einem anderen Benutzer geändert. Ihre Änderungen konnten leider NICHT gespeichert werden. Das Problem tritt auf, wenn mehrere Benutzer gleichzeitig den gleichen Datensatz bearbeiten.\n\nSie müssen den Datensatz schließen und erneut öffnen, um die Änderungen vornehmen zu können.";
				switch (lBeWoFault.FaultType)
				{
					case BeWoFaultType.Concurrency:
						BeWoApp.ShowError(rowUpdated, e, false);
						break;
					case BeWoFaultType.DeleteNotPossible:
						MessageBox.Show(lBeWoFault.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Asterisk);
						break;
					case BeWoFaultType.LoginNameTaken:
						MessageBox.Show(lBeWoFault.Message, "Warnung", MessageBoxButton.OK, MessageBoxImage.Asterisk);
						return;
                    case BeWoFaultType.CustomWithoutDetails:
                        BeWoApp.ShowError(lBeWoFault.Message, e, false);
				        break;
					default:
						if (!String.IsNullOrEmpty(e.Message) && e.Message.IndexOf("Row was updated or deleted by another transaction") >= 0)
						{
							BeWoApp.ShowError(rowUpdated, e, false);
						}
						else
						{
							if (!String.IsNullOrEmpty(lBeWoFault.Message))
							{
								BeWoApp.ShowError(lBeWoFault.Message, e, true);
							}
							else
							{
								BeWoApp.ShowError(null, e, true);
							}
						}
						break;
				}
			}
			else
			{
				BeWoApp.ShowError(null, e, true);
			}
		}

		private static void StartWaiting()
		{
			Monitor.Enter(_Lock);
			if ((++_RunningAsyncs) == 1 && BeWoApp.MainControl != null)
			{
				BeWoApp.MainControl.StartWaiting();
			}

			Monitor.Exit(_Lock);
		}

	}
}