private void OnCallingEvent_Receive(Client client, BroadcastParams broadcastParams, CallingEventParams callEventParams)
        {
            CallingEventParams.ReceiveParams receiveParams = null;
            try { receiveParams = callEventParams.ParametersAs <CallingEventParams.ReceiveParams>(); }
            catch (Exception exc)
            {
                mLogger.LogWarning(exc, "Failed to parse ReceiveParams");
                return;
            }

            // @note A received call should only ever receive one receive event regardless of the state of the call, but we act as though
            // we could receive multiple just for sanity here, but the callbacks will still only be called when first created, which makes
            // this effectively a no-op on additional receive events
            Call call = null;
            Call tmp  = null;

            switch (receiveParams.Device.Type)
            {
            case CallDevice.DeviceType.phone:
            {
                CallDevice.PhoneParams phoneParams = null;
                try { phoneParams = receiveParams.Device.ParametersAs <CallDevice.PhoneParams>(); }
                catch (Exception exc)
                {
                    mLogger.LogWarning(exc, "Failed to parse PhoneParams");
                    return;
                }

                // If the call already exists under the real call id simply obtain the call, otherwise create a new call
                call = mCalls.GetOrAdd(receiveParams.CallID, k => (tmp = new PhoneCall(this, receiveParams.NodeID, receiveParams.CallID)
                    {
                        To = phoneParams.ToNumber,
                        From = phoneParams.FromNumber,
                        Timeout = phoneParams.Timeout,
                    }));
                break;
            }

            // @TODO: sip and webrtc
            default:
                mLogger.LogWarning("Unknown device type: {0}", receiveParams.Device.Type);
                return;
            }

            if (tmp == call)
            {
                call.Context = receiveParams.Context;

                OnCallCreated?.Invoke(this, call);
                OnCallReceived?.Invoke(this, call, receiveParams);
            }

            call.ReceiveHandler(callEventParams, receiveParams);
        }
        // High Level API

        public PhoneCall NewPhoneCall(string to, string from, int timeout = 30)
        {
            PhoneCall call = new PhoneCall(this, Guid.NewGuid().ToString())
            {
                To      = to,
                From    = from,
                Timeout = timeout,
            };

            mCalls.TryAdd(call.TemporaryID, call);
            OnCallCreated?.Invoke(this, call);
            return(call);
        }
        public SipCall NewSipCall(string to, string from, string fromName = null, string codecs = null, JArray headers = null, int timeout = 30, int?maxDuration = null, bool?webRTCMedia = null)
        {
            SipCall call = new SipCall(this, Guid.NewGuid().ToString())
            {
                To          = to,
                From        = from,
                FromName    = fromName,
                Headers     = headers,
                Codecs      = codecs,
                Timeout     = timeout,
                MaxDuration = maxDuration,
                WebRTCMedia = webRTCMedia
            };

            mCalls.TryAdd(call.TemporaryID, call);
            OnCallCreated?.Invoke(this, call);
            return(call);
        }
        private void OnCallingEvent_State(Client client, BroadcastParams broadcastParams, CallingEventParams callEventParams)
        {
            CallingEventParams.StateParams stateParams = null;
            try { stateParams = callEventParams.ParametersAs <CallingEventParams.StateParams>(); }
            catch (Exception exc)
            {
                Log(LogLevel.Warning, exc, "Failed to parse StateParams");
                return;
            }

            Call call = null;
            Call tmp  = null;

            switch (stateParams.Device.Type)
            {
            case CallDevice.DeviceType.phone:
            {
                CallDevice.PhoneParams phoneParams = null;
                try { phoneParams = stateParams.Device.ParametersAs <CallDevice.PhoneParams>(); }
                catch (Exception exc)
                {
                    Log(LogLevel.Warning, exc, "Failed to parse PhoneParams");
                    return;
                }

                // If the call already exists under the real call id simply obtain the call, however if the call was found under
                // a temporary call id then readd it here under the real call id, otherwise create a new call
                call = mCalls.GetOrAdd(stateParams.CallID, k => call ?? (tmp = new PhoneCall(this, stateParams.NodeID, stateParams.CallID)
                    {
                        To = phoneParams.ToNumber,
                        From = phoneParams.FromNumber,
                        Timeout = phoneParams.Timeout,
                        // Capture the state, it may not always be created the first time we see the call
                        State = stateParams.CallState,
                    }));
                break;
            }

            case CallDevice.DeviceType.sip:
            {
                CallDevice.SipParams sipParams = null;
                try { sipParams = stateParams.Device.ParametersAs <CallDevice.SipParams>(); }
                catch (Exception exc)
                {
                    Log(LogLevel.Warning, exc, "Failed to parse SipParams");
                    return;
                }

                // If the call already exists under the real call id simply obtain the call, however if the call was found under
                // a temporary call id then readd it here under the real call id, otherwise create a new call
                call = mCalls.GetOrAdd(stateParams.CallID, k => call ?? (tmp = new SipCall(this, stateParams.NodeID, stateParams.CallID)
                    {
                        To = sipParams.To,
                        From = sipParams.From,
                        FromName = sipParams.FromName,
                        Headers = sipParams.Headers,

                        // Capture the state, it may not always be created the first time we see the call
                        State = stateParams.CallState,
                    }));
                break;
            }

            // @TODO: webrtc
            default:
                Log(LogLevel.Warning, string.Format("Unknown device type: {0}", stateParams.Device.Type));
                return;
            }

            if (tmp == call)
            {
                OnCallCreated?.Invoke(this, call);
            }

            call.StateChangeHandler(callEventParams, stateParams);
        }
        private void OnCallingEvent_State(Client client, BroadcastParams broadcastParams, CallingEventParams callEventParams)
        {
            CallingEventParams.StateParams stateParams = null;
            try { stateParams = callEventParams.ParametersAs <CallingEventParams.StateParams>(); }
            catch (Exception exc)
            {
                mLogger.LogWarning(exc, "Failed to parse StateParams");
                return;
            }

            Call call = null;

            if (!string.IsNullOrWhiteSpace(stateParams.TemporaryCallID))
            {
                // Remove the call keyed by the temporary call id if it exists
                if (mCalls.TryRemove(stateParams.TemporaryCallID, out call))
                {
                    // Update the internal details for the call, including the real call id
                    call.NodeID = stateParams.NodeID;
                    call.ID     = stateParams.CallID;
                }
            }
            // If call is not null at this point, it means this is the first event for a call that was started with a temporary call id
            // and the call should be readded under the real call id

            Call tmp = null;

            switch (stateParams.Device.Type)
            {
            case CallDevice.DeviceType.phone:
            {
                CallDevice.PhoneParams phoneParams = null;
                try { phoneParams = stateParams.Device.ParametersAs <CallDevice.PhoneParams>(); }
                catch (Exception exc)
                {
                    mLogger.LogWarning(exc, "Failed to parse PhoneParams");
                    return;
                }

                // If the call already exists under the real call id simply obtain the call, however if the call was found under
                // a temporary call id then readd it here under the real call id, otherwise create a new call
                call = mCalls.GetOrAdd(stateParams.CallID, k => call ?? (tmp = new PhoneCall(this, stateParams.NodeID, stateParams.CallID)
                    {
                        To = phoneParams.ToNumber,
                        From = phoneParams.FromNumber,
                        Timeout = phoneParams.Timeout,
                        // Capture the state, it may not always be created the first time we see the call
                        State = stateParams.CallState,
                    }));
                break;
            }

            // @TODO: sip and webrtc
            default:
                mLogger.LogWarning("Unknown device type: {0}", stateParams.Device.Type);
                return;
            }

            if (tmp == call)
            {
                OnCallCreated?.Invoke(this, call);
            }

            call.StateChangeHandler(callEventParams, stateParams);
        }