using CallFlow.CFD;
using CallFlow;
using MimeKit;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks.Dataflow;
using System.Threading.Tasks;
using System.Threading;
using System;
using TCX.Configuration;

namespace _73_dynWeiterleitung_aus
{
   public class Main : ScriptBase<Main>, ICallflow, ICallflowProcessor
   {
      private bool executionStarted;
      private bool executionFinished;
      private bool disconnectFlowPending;

      private BufferBlock<AbsEvent> eventBuffer;

      private int currentComponentIndex;
      private List<AbsComponent> mainFlowComponentList;
      private List<AbsComponent> disconnectFlowComponentList;
      private List<AbsComponent> errorFlowComponentList;
      private List<AbsComponent> currentFlowComponentList;

      private LogFormatter logFormatter;
      private TimerManager timerManager;
      private Dictionary<string, Variable> variableMap;
      private TempWavFileManager tempWavFileManager;
      private PromptQueue promptQueue;
      private OnlineServices onlineServices;
      private OfficeHoursManager officeHoursManager;

      private CfdAppScope scope;

      private void DisconnectCallAndExitCallflow()
      {
         if (currentFlowComponentList == disconnectFlowComponentList)
            logFormatter.Trace("Callflow finished...");
         else
         {
            logFormatter.Trace("Callflow finished, disconnecting call...");
            MyCall.Terminate();
         }
      }

      private async Task ExecuteErrorFlow()
      {
         if (currentFlowComponentList == errorFlowComponentList)
         {
            logFormatter.Trace("Error during error handler flow, exiting callflow...");
            DisconnectCallAndExitCallflow();
         }
         else if (currentFlowComponentList == disconnectFlowComponentList)
         {
            logFormatter.Trace("Error during disconnect handler flow, exiting callflow...");
            executionFinished = true;
         }
         else
         {
            currentFlowComponentList = errorFlowComponentList;
            currentComponentIndex = 0;
            if (errorFlowComponentList.Count > 0)
            {
               logFormatter.Trace("Start executing error handler flow...");
               await ProcessStart();
            }
            else
            {
               logFormatter.Trace("Error handler flow is empty...");
               DisconnectCallAndExitCallflow();
            }
         }
      }

      private async Task ExecuteDisconnectFlow()
      {
         currentFlowComponentList = disconnectFlowComponentList;
         currentComponentIndex = 0;
         disconnectFlowPending = false;
         if (disconnectFlowComponentList.Count > 0)
         {
            logFormatter.Trace("Start executing disconnect handler flow...");
            await ProcessStart();
         }
         else
         {
            logFormatter.Trace("Disconnect handler flow is empty...");
            executionFinished = true;
         }
      }

      private EventResults CheckEventResult(EventResults eventResult)
      {
         if (eventResult == EventResults.MoveToNextComponent && ++currentComponentIndex == currentFlowComponentList.Count)
         {
            DisconnectCallAndExitCallflow();
            return EventResults.Exit;
         }
         else if (eventResult == EventResults.Exit)
            DisconnectCallAndExitCallflow();

         return eventResult;
      }

      private void InitializeVariables(string callID)
      {
         // Call variables
         variableMap["session.ani"] = new Variable(MyCall.Caller.CallerID);
         variableMap["session.callid"] = new Variable(callID);
         variableMap["session.dnis"] = new Variable(MyCall.DN.Number);
         variableMap["session.did"] = new Variable(MyCall.Caller.CalledNumber);
         variableMap["session.audioFolder"] = new Variable(Path.Combine(RecordingManager.Instance.AudioFolder, promptQueue.ProjectAudioFolder));
         variableMap["session.transferingExtension"] = new Variable(MyCall.ReferredByDN?.Number ?? string.Empty);
         variableMap["session.forwardingExtension"] = new Variable(MyCall.OnBehalfOf?.Number ?? string.Empty);

         // Standard variables
         variableMap["RecordResult.NothingRecorded"] = new Variable(RecordComponent.RecordResults.NothingRecorded);
         variableMap["RecordResult.StopDigit"] = new Variable(RecordComponent.RecordResults.StopDigit);
         variableMap["RecordResult.Completed"] = new Variable(RecordComponent.RecordResults.Completed);
         variableMap["MenuResult.Timeout"] = new Variable(MenuComponent.MenuResults.Timeout);
         variableMap["MenuResult.InvalidOption"] = new Variable(MenuComponent.MenuResults.InvalidOption);
         variableMap["MenuResult.ValidOption"] = new Variable(MenuComponent.MenuResults.ValidOption);
         variableMap["UserInputResult.Timeout"] = new Variable(UserInputComponent.UserInputResults.Timeout);
         variableMap["UserInputResult.InvalidDigits"] = new Variable(UserInputComponent.UserInputResults.InvalidDigits);
         variableMap["UserInputResult.ValidDigits"] = new Variable(UserInputComponent.UserInputResults.ValidDigits);
         variableMap["VoiceInputResult.Timeout"] = new Variable(VoiceInputComponent.VoiceInputResults.Timeout);
         variableMap["VoiceInputResult.InvalidInput"] = new Variable(VoiceInputComponent.VoiceInputResults.InvalidInput);
         variableMap["VoiceInputResult.ValidInput"] = new Variable(VoiceInputComponent.VoiceInputResults.ValidInput);
         variableMap["VoiceInputResult.ValidDtmfInput"] = new Variable(VoiceInputComponent.VoiceInputResults.ValidDtmfInput);

         // User variables
         variableMap["project$.ExtensionNr"] = new Variable("");
            variableMap["project$.AuswahlOpt"] = new Variable("");
            variableMap["project$.ZielNr"] = new Variable("");
            variableMap["RecordResult.NothingRecorded"] = new Variable(RecordComponent.RecordResults.NothingRecorded);
            variableMap["RecordResult.StopDigit"] = new Variable(RecordComponent.RecordResults.StopDigit);
            variableMap["RecordResult.Completed"] = new Variable(RecordComponent.RecordResults.Completed);
            variableMap["MenuResult.Timeout"] = new Variable(MenuComponent.MenuResults.Timeout);
            variableMap["MenuResult.InvalidOption"] = new Variable(MenuComponent.MenuResults.InvalidOption);
            variableMap["MenuResult.ValidOption"] = new Variable(MenuComponent.MenuResults.ValidOption);
            variableMap["UserInputResult.Timeout"] = new Variable(UserInputComponent.UserInputResults.Timeout);
            variableMap["UserInputResult.InvalidDigits"] = new Variable(UserInputComponent.UserInputResults.InvalidDigits);
            variableMap["UserInputResult.ValidDigits"] = new Variable(UserInputComponent.UserInputResults.ValidDigits);
            variableMap["VoiceInputResult.Timeout"] = new Variable(VoiceInputComponent.VoiceInputResults.Timeout);
            variableMap["VoiceInputResult.InvalidInput"] = new Variable(VoiceInputComponent.VoiceInputResults.InvalidInput);
            variableMap["VoiceInputResult.ValidInput"] = new Variable(VoiceInputComponent.VoiceInputResults.ValidInput);
            variableMap["VoiceInputResult.ValidDtmfInput"] = new Variable(VoiceInputComponent.VoiceInputResults.ValidDtmfInput);
            
        }

      private void InitializeComponents(ICallflow callflow, ICall myCall, string logHeader)
      {
         scope = CfdModule.Instance.CreateScope(callflow, myCall, logHeader);

         {
            VariableAssignmentComponent variableAssignmentExtensionNr = scope.CreateComponent<VariableAssignmentComponent>("variableAssignmentExtensionNr");
            variableAssignmentExtensionNr.VariableName = "project$.ExtensionNr";
            variableAssignmentExtensionNr.VariableValueHandler = () => { return variableMap["session.ani"].Value; };
            mainFlowComponentList.Add(variableAssignmentExtensionNr);
            VariableAssignmentComponent variableAssignmentZielNr = scope.CreateComponent<VariableAssignmentComponent>("variableAssignmentZielNr");
            variableAssignmentZielNr.VariableName = "project$.ZielNr";
            variableAssignmentZielNr.VariableValueHandler = () => { return variableMap["session.ani"].Value; };
            mainFlowComponentList.Add(variableAssignmentZielNr);
            setStateAndFWDestination setStateAndFWDestination1 = new setStateAndFWDestination(onlineServices, officeHoursManager, scope, "setStateAndFWDestination1", callflow, myCall, logHeader);
            setStateAndFWDestination1.strDestNoSetter = () => { return variableMap["project$.ZielNr"].Value; };
            setStateAndFWDestination1.strExtensionNoSetter = () => { return variableMap["project$.ExtensionNr"].Value; };
            mainFlowComponentList.Add(setStateAndFWDestination1);
            }
            {
            }
            {
            }
            

         // Add a final DisconnectCall component to the main and error handler flows, in order to complete pending prompt playbacks...
         DisconnectCallComponent mainAutoAddedFinalDisconnectCall = scope.CreateComponent<DisconnectCallComponent>("mainAutoAddedFinalDisconnectCall");
         DisconnectCallComponent errorHandlerAutoAddedFinalDisconnectCall = scope.CreateComponent<DisconnectCallComponent>("errorHandlerAutoAddedFinalDisconnectCall");
         mainFlowComponentList.Add(mainAutoAddedFinalDisconnectCall);
         errorFlowComponentList.Add(errorHandlerAutoAddedFinalDisconnectCall);
      }

      public Main()
      {
         this.executionStarted = false;
         this.executionFinished = false;
         this.disconnectFlowPending = false;

         this.eventBuffer = new BufferBlock<AbsEvent>();

         this.currentComponentIndex = 0;
         this.mainFlowComponentList = new List<AbsComponent>();
         this.disconnectFlowComponentList = new List<AbsComponent>();
         this.errorFlowComponentList = new List<AbsComponent>();
         this.currentFlowComponentList = mainFlowComponentList;

         this.timerManager = new TimerManager();
         this.timerManager.OnTimeout += (state) => eventBuffer.Post(new TimeoutEvent(state));
         this.variableMap = new Dictionary<string, Variable>();

         AbsTextToSpeechEngine textToSpeechEngine = null;
         AbsSpeechToTextEngine speechToTextEngine = null;
         this.onlineServices = new OnlineServices(textToSpeechEngine, speechToTextEngine);
      }

      public override void Start()
      {
         string callID = MyCall?.Caller["chid"] ?? "Unknown";
         string logHeader = $"_73_dynWeiterleitung_aus - CallID {callID}";
         this.logFormatter = new LogFormatter(MyCall, logHeader, "Callflow");
         this.promptQueue = new PromptQueue(this, MyCall, "_73_dynWeiterleitung_aus", logHeader);
         this.tempWavFileManager = new TempWavFileManager(logFormatter);
         this.timerManager.CallStarted();
         this.officeHoursManager = new OfficeHoursManager(MyCall);

         logFormatter.Info($"ConnectionStatus:`{MyCall.Status}`");

         if (MyCall.Status == ConnectionStatus.Ringing)
            MyCall.AssureMedia().ContinueWith(_ => StartInternal(logHeader, callID));
         else
            StartInternal(logHeader, callID);
      }

      private void StartInternal(string logHeader, string callID)
      {
         logFormatter.Trace("SetBackgroundAudio to false");
         MyCall.SetBackgroundAudio(false, new string[] { });

         logFormatter.Trace("Initialize components");
         InitializeComponents(this, MyCall, logHeader);
         logFormatter.Trace("Initialize variables");
         InitializeVariables(callID);

         MyCall.OnTerminated += () => eventBuffer.Post(new CallTerminatedEvent());
         MyCall.OnDTMFInput += x => eventBuffer.Post(new DTMFReceivedEvent(x));

         logFormatter.Trace("Start executing main flow...");
         eventBuffer.Post(new StartEvent());
         Task.Run(() => EventProcessingLoop());

         
      }

      public void PostStartEvent()
      {
         eventBuffer.Post(new StartEvent());
      }

      public void PostDTMFReceivedEvent(char digit)
      {
         eventBuffer.Post(new DTMFReceivedEvent(digit));
      }

      public void PostPromptPlayedEvent()
      {
         eventBuffer.Post(new PromptPlayedEvent());
      }

      public void PostTransferFailedEvent()
      {
         eventBuffer.Post(new TransferFailedEvent());
      }

      public void PostMakeCallResultEvent(bool result)
      {
         eventBuffer.Post(new MakeCallResultEvent(result));
      }

      public void PostCallTerminatedEvent()
      {
         eventBuffer.Post(new CallTerminatedEvent());
      }

      public void PostTimeoutEvent(object state)
      {
         eventBuffer.Post(new TimeoutEvent(state));
      }

      private async Task EventProcessingLoop()
      {
         executionStarted = true;
         while (!executionFinished)
         {
            AbsEvent evt = await eventBuffer.ReceiveAsync();
            await evt?.ProcessEvent(this);
         }

         if (scope != null) scope.Dispose();
      }

      public async Task ProcessStart()
      {
         try
         {
            EventResults eventResult;
            do
            {
               AbsComponent currentComponent = currentFlowComponentList[currentComponentIndex];
               logFormatter.Trace("Start executing component '" + currentComponent.Name + "'");
               eventResult = await currentComponent.Start(timerManager, variableMap, tempWavFileManager, promptQueue);
            }
            while (CheckEventResult(eventResult) == EventResults.MoveToNextComponent);

            if (eventResult == EventResults.Exit) executionFinished = true;
         }
         catch (Exception exc)
         {
            logFormatter.Error("Error executing last component: " + exc.ToString());
            await ExecuteErrorFlow();
         }
      }

      public async Task ProcessDTMFReceived(char digit)
      {
         try
         {
            AbsComponent currentComponent = currentFlowComponentList[currentComponentIndex];
            logFormatter.Trace("OnDTMFReceived for component '" + currentComponent.Name + "' - Digit: '" + digit + "'");
            EventResults eventResult = CheckEventResult(await currentComponent.OnDTMFReceived(timerManager, variableMap, tempWavFileManager, promptQueue, digit));
            if (eventResult == EventResults.MoveToNextComponent)
            {
               if (disconnectFlowPending)
                  await ExecuteDisconnectFlow();
               else
                  await ProcessStart();
            }
            else if (eventResult == EventResults.Exit)
               executionFinished = true;
         }
         catch (Exception exc)
         {
            logFormatter.Error("Error executing last component: " + exc.ToString());
            await ExecuteErrorFlow();
         }
      }

      public async Task ProcessPromptPlayed()
      {
         try
         {
            promptQueue.NotifyPlayFinished();
            AbsComponent currentComponent = currentFlowComponentList[currentComponentIndex];
            logFormatter.Trace("OnPromptPlayed for component '" + currentComponent.Name + "'");
            EventResults eventResult = CheckEventResult(await currentComponent.OnPromptPlayed(timerManager, variableMap, tempWavFileManager, promptQueue));
            if (eventResult == EventResults.MoveToNextComponent)
            {
               if (disconnectFlowPending)
                  await ExecuteDisconnectFlow();
               else
                  await ProcessStart();
            }
            else if (eventResult == EventResults.Exit)
               executionFinished = true;
         }
         catch (Exception exc)
         {
            logFormatter.Error("Error executing last component: " + exc.ToString());
            await ExecuteErrorFlow();
         }
      }

      public async Task ProcessTransferFailed()
      {
         try
         {
            AbsComponent currentComponent = currentFlowComponentList[currentComponentIndex];
            logFormatter.Trace("OnTransferFailed for component '" + currentComponent.Name + "'");
            EventResults eventResult = CheckEventResult(await currentComponent.OnTransferFailed(timerManager, variableMap, tempWavFileManager, promptQueue));
            if (eventResult == EventResults.MoveToNextComponent)
            {
               if (disconnectFlowPending)
                  await ExecuteDisconnectFlow();
               else
                  await ProcessStart();
            }
            else if (eventResult == EventResults.Exit)
               executionFinished = true;
         }
         catch (Exception exc)
         {
            logFormatter.Error("Error executing last component: " + exc.ToString());
            await ExecuteErrorFlow();
         }
      }

      public async Task ProcessMakeCallResult(bool result)
      {
         try
         {
            AbsComponent currentComponent = currentFlowComponentList[currentComponentIndex];
            logFormatter.Trace("OnMakeCallResult for component '" + currentComponent.Name + "' - Result: '" + result + "'");
            EventResults eventResult = CheckEventResult(await currentComponent.OnMakeCallResult(timerManager, variableMap, tempWavFileManager, promptQueue, result));
            if (eventResult == EventResults.MoveToNextComponent)
            {
               if (disconnectFlowPending)
                  await ExecuteDisconnectFlow();
               else
                  await ProcessStart();
            }
            else if (eventResult == EventResults.Exit)
               executionFinished = true;
         }
         catch (Exception exc)
         {
            logFormatter.Error("Error executing last component: " + exc.ToString());
            await ExecuteErrorFlow();
         }
      }

      public async Task ProcessCallTerminated()
      {
         try
         {
            if (executionStarted)
            {
               // First notify the call termination to the current component
               AbsComponent currentComponent = currentFlowComponentList[currentComponentIndex];
               logFormatter.Trace("OnCallTerminated for component '" + currentComponent.Name + "'");

               // Don't wrap around CheckEventResult, because the call has been already disconnected, 
               // and the following action to execute depends on the returned value.
               EventResults eventResult = await currentComponent.OnCallTerminated(timerManager, variableMap, tempWavFileManager, promptQueue);
               if (eventResult == EventResults.MoveToNextComponent)
               {
                  // Next, if the current component has completed its job, execute the disconnect flow
                  await ExecuteDisconnectFlow();
               }
               else if (eventResult == EventResults.Wait)
               {
                  // If the user component needs more events, wait for it to finish, and signal here that we need to execute
                  // the disconnect handler flow of the callflow next...
                  disconnectFlowPending = true;
               }
               else if (eventResult == EventResults.Exit)
                  executionFinished = true;
            }
         }
         catch (Exception exc)
         {
            logFormatter.Error("Error executing last component: " + exc.ToString());
            await ExecuteErrorFlow();
         }
         finally
         {
            // Finally, delete temporary files
            tempWavFileManager.DeleteFilesAndFolders();
         }
      }

      public async Task ProcessTimeout(object state)
      {
         try
         {
            AbsComponent currentComponent = currentFlowComponentList[currentComponentIndex];
            logFormatter.Trace("OnTimeout for component '" + currentComponent.Name + "'");
            EventResults eventResult = CheckEventResult(await currentComponent.OnTimeout(timerManager, variableMap, tempWavFileManager, promptQueue, state));
            if (eventResult == EventResults.MoveToNextComponent)
            {
               if (disconnectFlowPending)
                  await ExecuteDisconnectFlow();
               else
                  await ProcessStart();
            }
            else if (eventResult == EventResults.Exit)
               executionFinished = true;
         }
         catch (Exception exc)
         {
            logFormatter.Error("Error executing last component: " + exc.ToString());
            await ExecuteErrorFlow();
         }
      }


              // ------------------------------------------------------------------------------------------------------------
        // User Defined component
        // ------------------------------------------------------------------------------------------------------------
        public class setStateAndFWDestination : AbsUserComponent
        {
            private OnlineServices onlineServices;
            private OfficeHoursManager officeHoursManager;
            private CfdAppScope scope;

            private ObjectExpressionHandler _strDestNoHandler = null;
            private ObjectExpressionHandler _strExtensionNoHandler = null;
            

            protected override void InitializeVariables()
            {
                componentVariableMap["callflow$.strDestNo"] = new Variable("");
                componentVariableMap["callflow$.strExtensionNo"] = new Variable("");
                
            }

            protected override void InitializeComponents()
            {
                Dictionary<string, Variable> variableMap = componentVariableMap;
                {
            SetExtDNDDest1962110077ECCComponent SetExtDNDDest = new SetExtDNDDest1962110077ECCComponent("SetExtDNDDest", callflow, myCall, logHeader);
            SetExtDNDDest.Parameters.Add(new CallFlow.CFD.Parameter("strExtNr", () => { return variableMap["callflow$.strExtensionNo"].Value; }));
            SetExtDNDDest.Parameters.Add(new CallFlow.CFD.Parameter("strDestNr", () => { return variableMap["callflow$.strDestNo"].Value; }));
            mainFlowComponentList.Add(SetExtDNDDest);
            ConditionalComponent CreateCondition1 = scope.CreateComponent<ConditionalComponent>("CreateCondition1");
            mainFlowComponentList.Add(CreateCondition1);
            CreateCondition1.ConditionList.Add(() => { return Convert.ToBoolean(CFDFunctions.EQUAL(SetExtDNDDest.ReturnValue,0)); });
            CreateCondition1.ContainerList.Add(scope.CreateComponent<SequenceContainerComponent>("conditionalComponentBranch1"));
            TcxSetExtensionStatusComponent SetAvailable = scope.CreateComponent<TcxSetExtensionStatusComponent>("SetAvailable");
            SetAvailable.ExtensionHandler = () => { return Convert.ToString(variableMap["callflow$.strExtensionNo"].Value); };
            SetAvailable.ProfileNameHandler = () => { return "Available"; };
            CreateCondition1.ContainerList[0].ComponentList.Add(SetAvailable);
            PromptPlaybackComponent Weiterleitung_entfernt = scope.CreateComponent<PromptPlaybackComponent>("Weiterleitung_entfernt");
            Weiterleitung_entfernt.AllowDtmfInput = true;
            Weiterleitung_entfernt.Prompts.Add(new AudioFilePrompt(() => { return "Weiterleitung wurde entfernt.wav"; }));
            CreateCondition1.ContainerList[0].ComponentList.Add(Weiterleitung_entfernt);
            CreateCondition1.ConditionList.Add(() => { return Convert.ToBoolean(CFDFunctions.EQUAL(SetExtDNDDest.ReturnValue,4)); });
            CreateCondition1.ContainerList.Add(scope.CreateComponent<SequenceContainerComponent>("conditionalComponentBranch2"));
            TcxSetExtensionStatusComponent SetExtCustom2 = scope.CreateComponent<TcxSetExtensionStatusComponent>("SetExtCustom2");
            SetExtCustom2.ExtensionHandler = () => { return Convert.ToString(variableMap["callflow$.strExtensionNo"].Value); };
            SetExtCustom2.ProfileNameHandler = () => { return "Custom 2"; };
            CreateCondition1.ContainerList[1].ComponentList.Add(SetExtCustom2);
            PromptPlaybackComponent PlayDigits = scope.CreateComponent<PromptPlaybackComponent>("PlayDigits");
            PlayDigits.AllowDtmfInput = true;
            PlayDigits.Prompts.Add(new AudioFilePrompt(() => { return "IhreAnrufeWerdenAufDieFolgendeNummerWeitergeleitet-vicki.wav"; }));
            PlayDigits.Prompts.Add(new NumberPrompt(NumberPrompt.NumberFormats.OneByOne, () => { return Convert.ToString(variableMap["callflow$.strDestNo"].Value); }));
            CreateCondition1.ContainerList[1].ComponentList.Add(PlayDigits);
            }
            {
            }
            {
            }
            
            }
            
            public setStateAndFWDestination(OnlineServices onlineServices, OfficeHoursManager officeHoursManager,
                CfdAppScope scope, string name, ICallflow callflow, ICall myCall, string logHeader) : base(name, callflow, myCall, logHeader)
            {
                this.onlineServices = onlineServices;
                this.officeHoursManager = officeHoursManager;
                this.scope = scope;
            }
     
            protected override void GetVariableValues()
            {
                if (_strDestNoHandler != null) componentVariableMap["callflow$.strDestNo"].Set(_strDestNoHandler());
                if (_strExtensionNoHandler != null) componentVariableMap["callflow$.strExtensionNo"].Set(_strExtensionNoHandler());
                
            }
            
            public ObjectExpressionHandler strDestNoSetter { set { _strDestNoHandler = value; } }
            public object strDestNo { get { return componentVariableMap["callflow$.strDestNo"].Value; } }
            public ObjectExpressionHandler strExtensionNoSetter { set { _strExtensionNoHandler = value; } }
            public object strExtensionNo { get { return componentVariableMap["callflow$.strExtensionNo"].Value; } }
            

            private bool IsServerInHoliday(ICall myCall)
            {
                Tenant tenant = myCall.PS.GetTenant();
                return tenant != null && tenant.IsHoliday(new DateTimeOffset(DateTime.Now));
            }

            private bool IsServerOfficeHourActive(ICall myCall)
            {
		            Tenant tenant = myCall.PS.GetTenant();
		            if (tenant == null) return false;
		
		            string overrideOfficeTime = tenant.GetPropertyValue("OVERRIDEOFFICETIME");
		            if (!String.IsNullOrEmpty(overrideOfficeTime))
		            {
		                if (overrideOfficeTime == "1") // Forced to in office hours
		                    return true;
		                else if (overrideOfficeTime == "2") // Forced to out of office hours
		                    return false;
		            }
		
		            DateTime nowDt = DateTime.Now;
		            if (tenant.IsHoliday(new DateTimeOffset(nowDt))) return false;
		
		            Schedule officeHours = tenant.Hours;
		            Nullable<bool> result = officeHours.IsActiveTime(nowDt);
		            return result.GetValueOrDefault(false);
            }
        }
public class SetExtDNDDest1962110077ECCComponent : ExternalCodeExecutionComponent
            {
                public List<CallFlow.CFD.Parameter> Parameters { get; } = new List<CallFlow.CFD.Parameter>();
                public SetExtDNDDest1962110077ECCComponent(string name, ICallflow callflow, ICall myCall, string projectName) : base(name, callflow, myCall, projectName) {}
                protected override object ExecuteCode()
                {
                    return SetExtDNDDest(Convert.ToString(Parameters[0].Value), Convert.ToString(Parameters[1].Value));
                }
            
            private object SetExtDNDDest(string strExtNr, string strDestNr)
                {
            var ext = PhoneSystem.Root.GetDNByNumber(strExtNr) as Extension;
var profile=ext.FwdProfiles.Where( x => x.Name == "Custom 2").First(); // 'Available', 'Away', 'Out of office', 'Custom 1', 'Custom 2', maybe parameter?

var NewStatus=-1;
if( profile != null ) {
    // DestinationStruct need 3 parameter
    // 1 DestinationType: 'None', 'VoiceMail', 'Extension', 'Queue', 'RingGroup', 'IVR', 'External', 'Fax', 'Boomerang' (external number),
    //                    'Deflect', 'VoiceMailOfDestination', 'Callback' (reserved), 'RoutePoint' 
    // 2 internal DN, maybe select by parameter  - OR - 
    // 3 external number as string, maybe select by parameter

	// DestinationType anhand der Zielnummer (strDestNr) bestimmen
	bool isInternal = false;
	bool isExtension = false;
	bool isRG = false;
	bool isIVR = false;
	bool isQUEUE = false;
	
	// check if strDestNr is a internal extension
	var extension = PhoneSystem.Root.GetDNByNumber(strDestNr) as Extension;
	if( extension != null ) {
		isInternal = true;
		isExtension = true;
	}
	
	// check if strDestNr is a ringgroup
	var allRGs = PhoneSystem.Root.GetRingGroups();
	foreach (var rg in allRGs)
	{
		if (rg.Number == strDestNr) 
		{
			isInternal = true;
			isRG = true;
			break;
		}
	}
	
	// check if strDestNr is a IVR/receptionist
	var allIVRs = PhoneSystem.Root.GetIVRs();
	foreach (var ivr in allIVRs)
	{
		if (ivr.Number == strDestNr) 
		{
			isInternal = true;
			isIVR = true;
			break;
		}
	}
	
	// check if strDestNr is a Queue
	var allQUEUQs = PhoneSystem.Root.GetQueues();
	foreach (var queue in allQUEUQs)
	{
		if (queue.Number == strDestNr) 
		{
			isInternal = true;
			isQUEUE = true;
			break;
		}
	}



	// define the DestinationStruct depending on the type of the destination target....
	
	var dest = new DestinationStruct();
	if (strExtNr.Equals(strDestNr)) {
		// set state to available if destination number is the number of the extension
		NewStatus = 0;
	}
	else {
		if( isInternal) {
			if( isExtension ) {	
				dest.To=DestinationType.Extension; 
				dest.Internal=PhoneSystem.Root.GetDNByNumber(strDestNr);  // needed if internal destination 
			}
			else if( isRG ) {	

				dest.To=DestinationType.RingGroup; 
				dest.Internal=PhoneSystem.Root.GetDNByNumber(strDestNr);  // needed if internal destination 
			}
			else if( isIVR ) {	

				dest.To=DestinationType.IVR; 
				dest.Internal=PhoneSystem.Root.GetDNByNumber(strDestNr);  // needed if internal destination 
			}
			else if( isQUEUE ) {	

				dest.To=DestinationType.Queue; 
				dest.Internal=PhoneSystem.Root.GetDNByNumber(strDestNr);  // needed if internal destination 
			}
		}
		else {

			dest.To=DestinationType.External; 
			dest.External=strDestNr;                                // needed if external destination
		
		}
		

		NewStatus = 4;
	}

    
    if( NewStatus >0)
	{
		// Depending on the type of status (present or absent), the forwarding must be entered in other target fields
		if( profile.TypeOfRouting == RoutingType.Available) {
			var route=profile.AvailableRoute;                   // maybe select by parameter?
			route.NoAnswer.AllCalls = dest;
			route.NoAnswer.Internal = dest;
			route.Busy.AllCalls = route.NotRegistered.AllCalls = dest;
			route.Busy.Internal = route.NotRegistered.Internal = dest;
		}
		if( profile.TypeOfRouting == RoutingType.Away) {
			var route=profile.AwayRoute;
			var external = profile.AwayRoute.External;
			route.Internal.AllHours = dest;                     // maybe select by parameter?
			route.Internal.OutOfOfficeHours = dest;
			route.External.AllHours = dest;
			route.External.OutOfOfficeHours = dest;
		}
		ext.Save();
	}
	
}
return( NewStatus);    }
            }
            
   }
}
