private async void btnChat_Click(object sender, EventArgs e)
        {
            if (lstPeers.SelectedIndex == -1)
            {
                MessageBox.Show("Please select a peer.");
                return;
            }
            Peer remotePeer = Peer.CreateFromString(lstPeers.GetItemText(lstPeers.SelectedItem));

            _httpSignaler.SendToPeer(remotePeer.Id, "OpenDataChannel");
            await SetupPeer(remotePeer, true);
        }
        private async Task SetupPeer(Peer remotePeer, bool isInitiator)
        {
            var found = lstPeers.FindString(remotePeer.ToString());

            if (ListBox.NoMatches != found)
            {
                remotePeer = Peer.CreateFromString(lstPeers.GetItemText(lstPeers.Items[found]));
            }

            Tuple <OrtcController, ChatForm> tuple;

            if (_chatSessions.TryGetValue(remotePeer.Id, out tuple))
            {
                // already have a form created
                tuple.Item2.HandleRemotePeerDisconnected();
                tuple.Item2.BringToFront();
                tuple.Item1.Dispose();
                _chatSessions.Remove(remotePeer.Id);
            }
            else
            {
                if (!Properties.Settings.Default.MultipleConnections)
                {
                    if (_chatSessions.Count > 0)
                    {
                        // Clear out existing chat forms as only one chat form
                        // is allowed.
                        DisposeChatForms();
                    }
                }

                // no chat form created, spawn a new one
                tuple = new Tuple <OrtcController, ChatForm>(null, new ChatForm(OrtcController.LocalPeer, remotePeer));

                tuple.Item2.SendMessageToRemotePeer += ChatForm_SendMessageToRemotePeer;

                // Invoking .ShowDialog() would block the signaler's task
                // being waited upon. Show() block the current task
                // from continuing until the dialog is dismissed but does
                // not block other events from processing on the GUI
                // thread. Not blocking events is insufficient though. The
                // task is waited by the signaler to complete before the
                // next signaler message from the server is allowed to be
                // processed so a dialog cannot be spawned from within the
                // processing of a peer's message task.
                //
                // The solution though to the blocking of the signaler's
                // task when bringing up a dialog is rather simple: invoke
                // the .ShowDialog() method asynchronously on the GUI
                // thread and not from within the current signaler message
                // task. This allows the GUI to be displayed and events are
                // processed as normal including other signaler messages
                // from peers.
                BeginInvoke((Action)(() =>
                {
                    if (Properties.Settings.Default.MultipleConnections)
                    {
                        // show the form non model
                        tuple.Item2.Show();
                    }
                    else
                    {
                        // invoke this on the main thread without blocking the
                        // current thread from continuing
                        tuple.Item2.ShowDialog();
                    }
                }));
            }

            // create a new tuple and carry forward the chat form from the previous tuple
            tuple = new Tuple <OrtcController, ChatForm>(new OrtcController(remotePeer, isInitiator), tuple.Item2);
            _chatSessions.Add(remotePeer.Id, tuple);

            tuple.Item1.DataChannelConnected    += OrtcSignaler_OnDataChannelConnected;
            tuple.Item1.DataChannelDisconnected += OrtcSignaler_OnDataChannelDisconnected;
            tuple.Item1.SignalMessageToPeer     += OrtcSignaler_OnSignalMessageToPeer;
            tuple.Item1.DataChannelMessage      += OrtcSignaler_OnDataChannelMessage;

            await tuple.Item1.SetupAsync();
        }