WingMan – Rev 32

Subversion Repositories:
Rev:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet;
using WingMan.Communication;

namespace WingMan.Bindings
{
    public class KeyBindingsSynchronizer : IDisposable
    {
        public delegate void MouseKeyBindingsSynchronized(object sender, KeyBindingsSynchronizerEventArgs e);

        public KeyBindingsSynchronizer(LocalKeyBindings localKeyBindings, MqttCommunication mqttCommunication,
            TaskScheduler taskScheduler, CancellationToken cancellationToken)
        {
            LocalKeyBindings = localKeyBindings;
            MqttCommunication = mqttCommunication;
            CancellationToken = cancellationToken;
            TaskScheduler = taskScheduler;

            SynchronizedMouseKeyBindings = new ConcurrentDictionary<string, List<KeyBinding>>();

            MqttCommunication.OnMessageReceived += MqttCommunicationOnMessageReceived;

            Task.Run(PeriodicSynchronize, CancellationToken);
        }

        private LocalKeyBindings LocalKeyBindings { get; }

        private ConcurrentDictionary<string, List<KeyBinding>> SynchronizedMouseKeyBindings { get; }

        private MqttCommunication MqttCommunication { get; }

        private CancellationToken CancellationToken { get; }
        private TaskScheduler TaskScheduler { get; }

        public void Dispose()
        {
            MqttCommunication.OnMessageReceived -= MqttCommunicationOnMessageReceived;
        }

        public event MouseKeyBindingsSynchronized OnMouseKeyBindingsSynchronized;

        private async void MqttCommunicationOnMessageReceived(object sender,
            MqttApplicationMessageReceivedEventArgs e)
        {
            if (e.ApplicationMessage.Topic != "exchange")
                return;

            using (var memoryStream = new MemoryStream(e.ApplicationMessage.Payload))
            {
                memoryStream.Position = 0L;

                var mouseKeyBindingsExchange =
                    (KeyBindingExchange) KeyBindingExchange.XmlSerializer.Deserialize(memoryStream);

                // Do not add own bindings.
                if (string.Equals(mouseKeyBindingsExchange.Nick, MqttCommunication.Nick))
                    return;

                if (SynchronizedMouseKeyBindings.TryGetValue(mouseKeyBindingsExchange.Nick, out var mouseKeyBinding) &&
                    mouseKeyBinding.SequenceEqual(mouseKeyBindingsExchange.KeyBindings))
                    return;

                await Task.Delay(0)
                    .ContinueWith(
                        _ => OnMouseKeyBindingsSynchronized?.Invoke(sender,
                            new KeyBindingsSynchronizerEventArgs(
                                mouseKeyBindingsExchange)),
                        CancellationToken, TaskContinuationOptions.None, TaskScheduler);

                // Nick does not exist so the bindings will be added.
                SynchronizedMouseKeyBindings.AddOrUpdate(mouseKeyBindingsExchange.Nick,
                    mouseKeyBindingsExchange.KeyBindings, (s, list) => mouseKeyBindingsExchange.KeyBindings);
            }
        }

        private async Task PeriodicSynchronize()
        {
            do
            {
                await Task.Delay(1000, CancellationToken);

                if (!MqttCommunication.Running)
                    continue;

                using (var memoryStream = new MemoryStream())
                {
                    KeyBindingExchange.XmlSerializer.Serialize(memoryStream,
                        new KeyBindingExchange(MqttCommunication.Nick, LocalKeyBindings.Bindings));

                    memoryStream.Position = 0L;

                    await MqttCommunication.Broadcast("exchange", memoryStream.ToArray());
                }
            } while (!CancellationToken.IsCancellationRequested);
        }
    }
}

Generated by GNU Enscript 1.6.5.90.