WingMan – Rev 4

Subversion Repositories:
Rev:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Resources;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Gma.System.MouseKeyHook;
using WingMan.Communication;
using WingMan.Host;

namespace WingMan
{
    public partial class WingManForm : Form
    {
        private MQTTServer MQTTServer { get; set; }

        private MQTTClient MQTTClient { get; set; }

        private SemaphoreSlim MQTTServerSemaphore {get; set;} =  new SemaphoreSlim(1, 1);

        private SemaphoreSlim MQTTClientSemaphore { get; set; } = new SemaphoreSlim(1, 1);

        private static IKeyboardMouseEvents MouseKeyApplicationHook { get; set; }

        private static IKeyboardMouseEvents MouseKeyGlobalHook { get; set; }

        private List<string> MouseKeyCombo { get; set; }

        private HelmBindings HelmBindings { get; set; }

        private BindingSource HelmBindingSource { get; set; }

        public ConcurrentDictionary<string, HelmBindings> WingBindings { get; set; }

        public WingManForm()
        {
            InitializeComponent();

            MouseKeyGlobalHook = Hook.GlobalEvents();

            MQTTClient = new MQTTClient(this);
            MQTTServer = new MQTTServer(this);

            HelmBindings = new HelmBindings();
            HelmBindings.Bindings = new List<HelmBinding>();

            HelmBindingSource = new BindingSource();
            HelmBindingSource.DataSource = HelmBindings.Bindings;

            HelmBindingsListBox.DisplayMember = "DisplayName";
            HelmBindingsListBox.ValueMember = "Keys";
            HelmBindingsListBox.DataSource = HelmBindingSource;

            WingBindings = new ConcurrentDictionary<string, HelmBindings>();
        }

        private void AddressTextBoxClick(object sender, EventArgs e)
        {
            Address.BackColor = Color.Empty;
        }

        private void PortTextBoxClick(object sender, EventArgs e)
        {
            Port.BackColor = Color.Empty;
        }

        private async void HostButtonClickAsync(object sender, EventArgs e)
        {
            // Stop the MQTT server if it is running.
            if (MQTTServer.ServerRunning)
            {
                await StopServer();
                toolStripStatusLabel.Text = Properties.Strings.Server_stopped;
                HostButton.BackColor = Color.Empty;

                // Enable controls.
                ConnectButton.Enabled = true;
                EnableConnectionControls();
                return;
            }

            if (!ValidateAddressPort(out var ipAddress, out var port, out var nick))
                return;

            // Start the MQTT server.
            await StartServer(ipAddress, port, nick);
            toolStripStatusLabel.Text = Properties.Strings.Server_started;
            HostButton.BackColor = Color.Aquamarine;

            // Disable controls
            ConnectButton.Enabled = false;
            DisableConnectionControls();
        }

        private void DisableConnectionControls()
        {
            Address.Enabled = false;
            Port.Enabled = false;
            Nick.Enabled = false;
        }

        private void EnableConnectionControls()
        {
            Address.Enabled = true;
            Port.Enabled = true;
            Nick.Enabled = true;
        }

        private bool ValidateAddressPort(out IPAddress address, out int port, out string nick)
        {
            address = IPAddress.Any;
            port = 0;
            nick = string.Empty;

            if (string.IsNullOrEmpty(Address.Text) &&
                string.IsNullOrEmpty(Port.Text) &&
                string.IsNullOrEmpty(Nick.Text))
            {
                Address.BackColor = Color.LightPink;
                Port.BackColor = Color.LightPink;
                Nick.BackColor = Color.LightPink;
                return false;
            }

            if (!IPAddress.TryParse(Address.Text, out address))
            {
                Address.BackColor = Color.LightPink;
                return false;
            }

            if (!uint.TryParse(Port.Text, out var uPort))
            {
                Port.BackColor = Color.LightPink;
                return false;
            }

            port = (int) uPort;

            if (string.IsNullOrEmpty(Nick.Text))
            {
                Nick.BackColor = Color.LightPink;
                return false;
            }

            nick = Nick.Text;

            Address.BackColor = Color.Empty;
            Port.BackColor = Color.Empty;
            Nick.BackColor = Color.Empty;

            return true;
        }

        private async Task StartServer(IPAddress ipAddress, int port, string nick)
        {
            await MQTTServerSemaphore.WaitAsync();
            try
            {
                await MQTTServer.Start(ipAddress, port, nick);
            }
            finally
            {
                MQTTServerSemaphore.Release();
            }
        }

        private async Task StopServer()
        {
            await MQTTServerSemaphore.WaitAsync();
            try
            {
                await MQTTServer.Stop();
            }
            finally
            {
                MQTTServerSemaphore.Release();
            }
        }

        private async void ConnectButtonClickAsync(object sender, EventArgs e)
        {
            if (MQTTClient.ClientRunning)
            {
                await StopClient();
                ConnectButton.Text = Properties.Strings.Connect;
                ConnectButton.BackColor = Color.Empty;

                EnableConnectionControls();
                HostButton.Enabled = true;
                return;
            }

            if (!ValidateAddressPort(out var ipAddress, out var port, out var nick))
                return;

            await StartClient(ipAddress, port, nick);

            toolStripStatusLabel.Text = Properties.Strings.Client_started;
            ConnectButton.Text = Properties.Strings.Disconnect;
            ConnectButton.BackColor = Color.Aquamarine;

            HostButton.Enabled = false;
            DisableConnectionControls();
        }

        private async Task StopClient()
        {
            await MQTTClientSemaphore.WaitAsync();
            try
            {
                await MQTTClient.Stop();
            }
            finally
            {
                MQTTClientSemaphore.Release();
            }
        }

        private async Task StartClient(IPAddress ipAddress, int port, string nick)
        {
            await MQTTClientSemaphore.WaitAsync();
            try
            {
                await MQTTClient.Start(ipAddress, port, nick);
            }
            finally
            {
                MQTTClientSemaphore.Release();
            }
        }

        private async void LobbySayTextBoxKeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode != Keys.Enter)
                return;

            if (MQTTServer.ServerRunning)
            {
                await MQTTServer.BroadcastLobbyMessage(LobbySayTextBox.Text);
            }

            if (MQTTClient.ClientRunning)
            {
                await MQTTClient.BroadcastLobbyMessage(LobbySayTextBox.Text);
            }

        }

        private void HelmAddButtonClick(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(HelmNameTextBox.Text))
            {
                HelmNameTextBox.BackColor = Color.LightPink;
                return;
            }

            HelmAddButton.Enabled = false;

            MouseKeyCombo = new List<string>();

            MouseKeyApplicationHook = Hook.AppEvents();
            MouseKeyApplicationHook.MouseDown += MouseKeyHookOnMouseDown;
            MouseKeyApplicationHook.KeyUp += MouseKeyHookOnKeyUp;
            MouseKeyApplicationHook.KeyDown += MouseKeyHookOnKeyDown;
            MouseKeyApplicationHook.MouseUp += MouseKeyHookOnMouseUp;

        }

        private void MouseKeyHookOnKeyUp(object sender, KeyEventArgs e)
        {
            HelmBindings.Bindings.Add(new HelmBinding
            {
                Keys = MouseKeyCombo,
                Name = HelmNameTextBox.Text
            });

            HelmBindingSource.ResetBindings(false);

            MouseKeyApplicationHook.KeyDown -= MouseKeyHookOnKeyDown;
            MouseKeyApplicationHook.KeyUp -= MouseKeyHookOnKeyUp;

            MouseKeyApplicationHook.Dispose();

            HelmNameTextBox.Text = string.Empty;
            HelmAddButton.Enabled = true;
        }

        private void MouseKeyHookOnMouseUp(object sender, MouseEventArgs e)
        {
            HelmBindings.Bindings.Add(new HelmBinding
            {
                Keys = MouseKeyCombo,
                Name = HelmNameTextBox.Text
            });

            HelmBindingSource.ResetBindings(false);

            MouseKeyApplicationHook.KeyDown -= MouseKeyHookOnKeyDown;
            MouseKeyApplicationHook.KeyUp -= MouseKeyHookOnKeyUp;

            MouseKeyApplicationHook.Dispose();

            HelmNameTextBox.Text = string.Empty;
            HelmAddButton.Enabled = true;
        }


        private void MouseKeyHookOnMouseDown(object sender, MouseEventArgs e)
        {
            MouseKeyCombo.Add(MouseButtonToName(e.Button));
        }

        private void MouseKeyHookOnKeyDown(object sender, KeyEventArgs e)
        {
            e.SuppressKeyPress = true;

            MouseKeyCombo.Add(KeyCodeToName((int) e.KeyCode));
        }

        private void HelmNameTextBoxClick(object sender, EventArgs e)
        {
            HelmNameTextBox.BackColor = Color.Empty;
        }

        private string MouseButtonToName(MouseButtons button)
        {
            var mouseButton = string.Empty;
            switch (button)
            {
                case MouseButtons.Left:
                    mouseButton = "Left Mouse Button";
                    break;
                case MouseButtons.Middle:
                    mouseButton = "Middle Mouse Button";
                    break;
                case MouseButtons.Right:
                    mouseButton = "Right Mouse Button";
                    break;
            }

            return mouseButton;
        }

        private string KeyCodeToName(int code)
        {
            var keyString = string.Empty;
            switch (code)
            {
                case 0:
                    break;
                case 1:
                    keyString = "Mouse Left";
                    break;
                case 2:
                    keyString = "Mouse Right";
                    break;
                case 3:
                    keyString = "Cancel";
                    break;
                case 4:
                    keyString = "Mouse Middle";
                    break;
                case 5:
                    keyString = "Special 1";
                    break;
                case 6:
                    keyString = "Special 2";
                    break;
                case 8:
                    keyString = "Back";
                    break;
                case 9:
                    keyString = "TAB";
                    break;
                case 12:
                    keyString = "Clear";
                    break;
                case 13:
                    keyString = "Enter";
                    break;
                case 16:
                    keyString = "Shift";
                    break;
                case 17:
                    keyString = "Ctrl";
                    break;
                case 18:
                    keyString = "Menu";
                    break;
                case 19:
                    keyString = "Pause";
                    break;
                case 20:
                    keyString = "Caps Lock";
                    break;
                case 21:
                    keyString = "Kana/Hangul";
                    break;
                case 23:
                    keyString = "Junja";
                    break;
                case 24:
                    keyString = "Final";
                    break;
                case 25:
                    keyString = "Hanja/Kanji";
                    break;
                case 27:
                    keyString = "Esc";
                    break;
                case 28:
                    keyString = "Convert";
                    break;
                case 29:
                    keyString = "NonConvert";
                    break;
                case 30:
                    keyString = "Accept";
                    break;
                case 31:
                    keyString = "Mode";
                    break;
                case 32:
                    keyString = "Space";
                    break;
                case 33:
                    keyString = "Page Up";
                    break;
                case 34:
                    keyString = "Page Down";
                    break;
                case 35:
                    keyString = "End";
                    break;
                case 36:
                    keyString = "Home";
                    break;
                case 37:
                    keyString = "Left";
                    break;
                case 38:
                    keyString = "Up";
                    break;
                case 39:
                    keyString = "Right";
                    break;
                case 40:
                    keyString = "Down";
                    break;
                case 41:
                    keyString = "Select";
                    break;
                case 42:
                    keyString = "Print";
                    break;
                case 43:
                    keyString = "Execute";
                    break;
                case 44:
                    keyString = "Snapshot";
                    break;
                case 45:
                    keyString = "Insert";
                    break;
                case 46:
                    keyString = "Delete";
                    break;
                case 47:
                    keyString = "Help";
                    break;
                case 48:
                    keyString = "Num 0";
                    break;
                case 49:
                    keyString = "Num 1";
                    break;
                case 50:
                    keyString = "Num 2";
                    break;
                case 51:
                    keyString = "Num 3";
                    break;
                case 52:
                    keyString = "Num 4";
                    break;
                case 53:
                    keyString = "Num 5";
                    break;
                case 54:
                    keyString = "Num 6";
                    break;
                case 55:
                    keyString = "Num 7";
                    break;
                case 56:
                    keyString = "Num 8";
                    break;
                case 57:
                    keyString = "Num 9";
                    break;
                case 65:
                    keyString = "A";
                    break;
                case 66:
                    keyString = "B";
                    break;
                case 67:
                    keyString = "C";
                    break;
                case 68:
                    keyString = "D";
                    break;
                case 69:
                    keyString = "E";
                    break;
                case 70:
                    keyString = "F";
                    break;
                case 71:
                    keyString = "G";
                    break;
                case 72:
                    keyString = "H";
                    break;
                case 73:
                    keyString = "I";
                    break;
                case 74:
                    keyString = "J";
                    break;
                case 75:
                    keyString = "K";
                    break;
                case 76:
                    keyString = "L";
                    break;
                case 77:
                    keyString = "M";
                    break;
                case 78:
                    keyString = "N";
                    break;
                case 79:
                    keyString = "O";
                    break;
                case 80:
                    keyString = "P";
                    break;
                case 81:
                    keyString = "Q";
                    break;
                case 82:
                    keyString = "R";
                    break;
                case 83:
                    keyString = "S";
                    break;
                case 84:
                    keyString = "T";
                    break;
                case 85:
                    keyString = "U";
                    break;
                case 86:
                    keyString = "V";
                    break;
                case 87:
                    keyString = "W";
                    break;
                case 88:
                    keyString = "X";
                    break;
                case 89:
                    keyString = "Y";
                    break;
                case 90:
                    keyString = "Z";
                    break;
                case 91:
                    keyString = "Windows Left";
                    break;
                case 92:
                    keyString = "Windows Right";
                    break;
                case 93:
                    keyString = "Application";
                    break;
                case 95:
                    keyString = "Sleep";
                    break;
                case 96:
                    keyString = "NumPad 0";
                    break;
                case 97:
                    keyString = "NumPad 1";
                    break;
                case 98:
                    keyString = "NumPad 2";
                    break;
                case 99:
                    keyString = "NumPad 3";
                    break;
                case 100:
                    keyString = "NumPad 4";
                    break;
                case 101:
                    keyString = "NumPad 5";
                    break;
                case 102:
                    keyString = "NumPad 6";
                    break;
                case 103:
                    keyString = "NumPad 7";
                    break;
                case 104:
                    keyString = "NumPad 8";
                    break;
                case 105:
                    keyString = "NumPad 9";
                    break;
                case 106:
                    keyString = "NumPad *";
                    break;
                case 107:
                    keyString = "NumPad +";
                    break;
                case 108:
                    keyString = "NumPad .";
                    break;
                case 109:
                    keyString = "NumPad -";
                    break;
                case 110:
                    keyString = "NumPad ,";
                    break;
                case 111:
                    keyString = "NumPad /";
                    break;
                case 112:
                    keyString = "F1";
                    break;
                case 113:
                    keyString = "F2";
                    break;
                case 114:
                    keyString = "F3";
                    break;
                case 115:
                    keyString = "F4";
                    break;
                case 116:
                    keyString = "F5";
                    break;
                case 117:
                    keyString = "F6";
                    break;
                case 118:
                    keyString = "F7";
                    break;
                case 119:
                    keyString = "F8";
                    break;
                case 120:
                    keyString = "F9";
                    break;
                case 121:
                    keyString = "F10";
                    break;
                case 122:
                    keyString = "F11";
                    break;
                case 123:
                    keyString = "F12";
                    break;
                case 124:
                    keyString = "F13";
                    break;
                case 125:
                    keyString = "F14";
                    break;
                case 126:
                    keyString = "F15";
                    break;
                case 127:
                    keyString = "F16";
                    break;
                case 128:
                    keyString = "F17";
                    break;
                case 129:
                    keyString = "F18";
                    break;
                case 130:
                    keyString = "F19";
                    break;
                case 131:
                    keyString = "F20";
                    break;
                case 132:
                    keyString = "F21";
                    break;
                case 133:
                    keyString = "F22";
                    break;
                case 134:
                    keyString = "F23";
                    break;
                case 135:
                    keyString = "F24";
                    break;
                case 144:
                    keyString = "Num lock";
                    break;
                case 145:
                    keyString = "Scroll";
                    break;
                case 160:
                    keyString = "Shift Left";
                    break;
                case 161:
                    keyString = "Shift Right";
                    break;
                case 162:
                    keyString = "Ctrl Left";
                    break;
                case 163:
                    keyString = "Ctrl Right";
                    break;
                case 164:
                    keyString = "Menu Left";
                    break;
                case 165:
                    keyString = "Menu Right";
                    break;
                default:
                    break;
            }

            return keyString;
        }

        private void HelmRemoveButtonClick(object sender, EventArgs e)
        {
            var helmBinding = (HelmBinding)HelmBindingsListBox.SelectedItem;
            if (helmBinding == null)
                return;

            HelmBindings.Bindings.Remove(helmBinding);
            HelmBindingSource.ResetBindings(false);
        }
    }
}