context: using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Threading;
  using ContactPoint.Common;
  using ContactPoint.Common.Audio;
  
  namespace ContactPoint.Core.Audio
  {
      internal class Audio : IAudio
      {
          private readonly AudioLibrary.Interfaces.IAudio _internalAudio;
          private readonly List<AudioDevice> _audioDevices = new List<AudioDevice>();
          private readonly AsyncCallback _raiseDeviceChangedCallback;
          private readonly AsyncCallback _raiseDeviceCollectionChangedCallback;
          private readonly ManualResetEvent _initWaitHandle = new ManualResetEvent(false);
  
          public event Action<IAudioDevice> RecordingDeviceChanged;
          public event Action<IAudioDevice> PlaybackDeviceChanged;
          public event Action<IEnumerable<IAudioDevice>> AudioDevicesAdded;
          public event Action<IEnumerable<IAudioDevice>> AudioDevicesRemoved;
  
          public ICore Core { get; private set; }
  
          public IEnumerable<IAudioDevice> AudioDevices
          {
              get 
              {
                  return ConvertAudioDeviceCollection(_audioDevices);
              }
          }
  
          private AudioDevice _playbackDevice = null;
          public IAudioDevice PlaybackDevice
          {
              get { return _playbackDevice; }
              set
              {
                  _playbackDevice = value as AudioDevice;
  
                  if (_playbackDevice != null) _internalAudio.PlaybackDevice = _playbackDevice.InternalAudioDevice;
                  else _internalAudio.PlaybackDevice = null;
  
                  RaisePlaybackDeviceChanged(_playbackDevice);
              }
          }
  
          private AudioDevice _recordingDevice = null;
          public IAudioDevice RecordingDevice
          {
              get { return _recordingDevice; }
              set
              {
                  _recordingDevice = value as AudioDevice;
  
                  if (_recordingDevice != null) _internalAudio.RecordingDevice = _recordingDevice.InternalAudioDevice;
                  else _internalAudio.RecordingDevice = null;
  
                  RaiseRecordingDeviceChanged(_recordingDevice);
              }
          }
  
          public Audio(Core core)
          {
              Logger.LogNotice("Setting up audio devices");
  
              Core = core;
  
              _raiseDeviceChangedCallback = RaiseEventCallback<IAudioDevice>;
              _raiseDeviceCollectionChangedCallback = RaiseEventCallback<List<IAudioDevice>>;
  
              _internalAudio = AudioLoader.LoadAudio();
  
              Logger.LogNotice("Loading audio devices.");
              foreach (var device in _internalAudio.AudioDevices)
                  if (device != null)
                  {
                      var dev = new AudioDevice(this, device);
                      Logger.LogNotice(String.Format("Audio device loaded: {0} ({1}) ", dev.Name, dev.Type));
  
                      _audioDevices.Add(dev);
                  }
  
              _internalAudio.AudioDevicesAdded += OnAudioDevicesAdded;
              _internalAudio.AudioDevicesRemoved += OnAudioDevicesRemoved;
  
              _internalAudio.PlaybackDeviceChanged += OnPlaybackDeviceChanged;
              _internalAudio.RecordingDeviceChanged += OnRecordingDeviceChanged;
  
              InitializeDevices(true);
          }
  
          public IAudioDevice GetDeviceByName(string deviceName)
          {
              foreach (var device in _audioDevices)
                  if (device.Name == deviceName)
                      return device;
  
              return null;
          }
  
          public void Dispose()
          {
              if (RecordingDevice != null) Core.SettingsManager.Set("RecordingDeviceVolume", RecordingDevice.Volume);
              if (PlaybackDevice != null) Core.SettingsManager.Set("PlaybackDeviceVolume", PlaybackDevice.Volume);
  
              _initWaitHandle.Dispose();
              _internalAudio.Dispose();
          }
  
          void InitializeDevices(bool wait = false)
          {
              var recordingDevice = GetDeviceByName(Core.SettingsManager.GetValueOrSetDefault<string>("RecordingDevice", _internalAudio.DefaultRecordingDevice != null ? _internalAudio.DefaultRecordingDevice.Name : String.Empty));
              var playbackDevice = GetDeviceByName(Core.SettingsManager.GetValueOrSetDefault<string>("PlaybackDevice", _internalAudio.DefaultPlaybackDevice != null ? _internalAudio.DefaultPlaybackDevice.Name : String.Empty));
  
              if (recordingDevice == null || playbackDevice == null)
              {
                  if (wait && _initWaitHandle.WaitOne(TimeSpan.FromSeconds(5)))
                  {
                      InitializeDevices();
                  }
  
                  return;
              }
  
              PlaybackDevice = playbackDevice;
              RecordingDevice = recordingDevice;
  
              _initWaitHandle.Set();
  
              if (RecordingDevice != null) RecordingDevice.Volume = Core.SettingsManager.GetValueOrSetDefault("RecordingDeviceVolume", 50);
              if (PlaybackDevice != null) PlaybackDevice.Volume = Core.SettingsManager.GetValueOrSetDefault("PlaybackDeviceVolume", 50);
          }
  
          void OnAudioDevicesAdded(IEnumerable<AudioLibrary.Interfaces.IAudioDevice> addedDevices)
          {
              var newDevices = addedDevices.Where(x => x != null).Select(x => new AudioDevice(this, x));
  
              _audioDevices.AddRange(newDevices);
  
              var deviceGroups = _audioDevices.Select(x => new { Device = x, Devices = _audioDevices.Where(y => y.Name.StartsWith(x.Name)) }).ToArray();
              var uniqueDevices = deviceGroups.Select(x => x.Devices.OrderByDescending(y => y.Name.Length).FirstOrDefault()).Where(x => x != null).ToArray();
  
              _audioDevices.RemoveAll(x => !uniqueDevices.Contains(x));
  
              RaiseAudioDevicesAdded(ConvertAudioDeviceCollection(newDevices));
  
              _initWaitHandle.Set();
          }
  
          void OnAudioDevicesRemoved(IEnumerable<AudioLibrary.Interfaces.IAudioDevice> removedDevices)
          {
              var tempList = new List<AudioDevice>();
  
              foreach (var device in removedDevices)
                  tempList.Add(FindAudioDevice(device));
  
              foreach (var device in tempList)
                  _audioDevices.Remove(device);
  
              RaiseAudioDevicesRemoved(ConvertAudioDeviceCollection(tempList));
          }
  
          void OnPlaybackDeviceChanged(AudioLibrary.Interfaces.IAudioDevice device)
          {
              if (device != null && (_playbackDevice != null && device != _playbackDevice.InternalAudioDevice || _playbackDevice == null && device != null))
              {
                  var localDevice = FindAudioDevice(device);
  
                  if (localDevice == null) localDevice = new AudioDevice(this, device);
  
                  PlaybackDevice = localDevice;
              }
              else if (device == null && _playbackDevice != null)
                  PlaybackDevice = null;
          }
  
          void OnRecordingDeviceChanged(AudioLibrary.Interfaces.IAudioDevice device)
          {
              if (device != null && (_recordingDevice != null && device != _recordingDevice.InternalAudioDevice || _recordingDevice == null && device != null))
              {
                  var localDevice = FindAudioDevice(device);
  
                  if (localDevice == null) localDevice = new AudioDevice(this, device);
  
                  RecordingDevice = localDevice;
              }
              else if (device == null && _recordingDevice != null)
                  RecordingDevice = null;
          }
  
          private void RaiseAudioDevicesAdded(List<IAudioDevice> deviceCollection)
          {
              SafeRaiseEvent(AudioDevicesAdded, deviceCollection, _raiseDeviceCollectionChangedCallback);
          }
  
          private void RaiseAudioDevicesRemoved(List<IAudioDevice> deviceCollection)
          {
              SafeRaiseEvent(AudioDevicesRemoved, deviceCollection, _raiseDeviceCollectionChangedCallback);
  
              if (deviceCollection.Contains(RecordingDevice)) RecordingDevice = FindAudioDevice(_internalAudio.DefaultRecordingDevice);
              if (deviceCollection.Contains(PlaybackDevice)) PlaybackDevice = FindAudioDevice(_internalAudio.DefaultPlaybackDevice);
          }
  
          private void RaiseRecordingDeviceChanged(IAudioDevice device)
          {
              SafeRaiseEvent(RecordingDeviceChanged, device, _raiseDeviceChangedCallback);
          }
  
          private void RaisePlaybackDeviceChanged(IAudioDevice device)
          {
              SafeRaiseEvent(PlaybackDeviceChanged, device, _raiseDeviceChangedCallback);
          }
  
          AudioDevice FindAudioDevice(AudioLibrary.Interfaces.IAudioDevice internalAudioDevice)
          {
              foreach (var audioDevice in _audioDevices)
                  if (audioDevice.InternalAudioDevice == internalAudioDevice)
                      return audioDevice;
  
              return null;
          }
  
          List<IAudioDevice> ConvertAudioDeviceCollection(IEnumerable<AudioDevice> collection)
          {
  
gt:             var resultList = new List<IAudioDevice>();