context: using System;
  using System.Collections.Generic;
  using System.ComponentModel;
  using System.Diagnostics;
  using System.Diagnostics.CodeAnalysis;
  using System.Drawing;
  using System.Globalization;
  using System.IO;
  using System.Linq;
  using System.Text.RegularExpressions;
  using System.Threading;
  using System.Windows.Forms;
  
  using Microsoft.Office.Interop.PowerPoint;
  
  using NAudio.Wave;
  
  using PowerPointLabs.ActionFramework.Common.Log;
  using PowerPointLabs.ActionFramework.NarrationsLab;
  using PowerPointLabs.AudioMisc;
  using PowerPointLabs.ColorThemes.Extensions;
  using PowerPointLabs.ELearningLab.Service;
  using PowerPointLabs.Models;
  using PowerPointLabs.TextCollection;
  using PowerPointLabs.Views;
  using PowerPointLabs.XMLMisc;
  
  using PPExtraEventHelper;
  
  namespace PowerPointLabs.ELearningLab.Views
  {
      [SuppressMessage("Microsoft.StyleCop.CSharp.OrderingRules", "SA1202:ElementsMustBeOrderedByAccess", Justification = "To refactor to partials")]
      // These task panes use User Control from Container 
      public partial class RecorderTaskPane : UserControl
      {
  #pragma warning disable 0618
          // a collection of audio buffer, for buffering slide show time recording
          public List<List<Tuple<Audio, int>>> AudioBuffer;
          // map slide id to relative index
          private readonly Dictionary<int, int> _slideRelativeMapper;
          // this offset is used to map a slide id to relative slide id
          private int _relativeSlideCounter;
          // a collection of slides, each slide has a list of audio object
          private readonly List<List<Audio>> _audioList;
          // a collection of slides, each slide has a list of script
          private readonly List<List<string>> _scriptList;
          // a buffer to store the audio that has been replaced
          private Audio _undoAudioBuffer;
          private TempStorage _storage;
  
          // Records save and display
          private const string SaveNameFormat = "Slide {0} Speech";
          private const string SpeechShapePrefix = "PowerPointLabs Speech";
          private const string SpeechShapePrefixOld = "AudioGen Speech";
          private const string SpeechShapeFormat = "PowerPointLabs Speech {0}";
  
          private string _tempFullPath
          {
              get
              {
                  return _storage.TempPath;
              }
          }
          private string _tempWaveFileNameFormat
          {
              get
              {
                  return String.Format("{0}temp{{0}}.{1}", _tempFullPath, Audio.RecordedFormatExtension);
              }
          }
          private string _tempShapAudioXmlFormat
          {
              get
              {
                  return _tempFullPath + "slide{0}.xml";
              }
          }
  
          private int _recordClipCnt;
          private int _recordTotalLength;
  
          private enum RecorderStatus
          {
              Idle,
              Recording,
              Playing,
              Pause
          }
          private enum ScriptStatus
          {
              Default,
              Generated,
              Recorded,
              Untracked
          }
  
          # region Recorder Utilities
          // these utilities wrapped NAudio functions
          private IWaveIn _waveInStream;
          private WaveFileWriter _waveFileWriter;
          private int _currentLength;
  
          private void WaveInStreamOnDataAvailable(object sender, WaveInEventArgs waveInEventArgs)
          {
              if (InvokeRequired)
              {
                  BeginInvoke(new EventHandler<WaveInEventArgs>(WaveInStreamOnDataAvailable), sender, waveInEventArgs);
              }
              else
              {
                  if (_waveFileWriter != null)
                  {
                      _waveFileWriter.Write(waveInEventArgs.Buffer, 0, waveInEventArgs.BytesRecorded);
                      _currentLength = (int)(_waveFileWriter.Length * 1000 / _waveFileWriter.WaveFormat.AverageBytesPerSecond);
                  }
              }
          }
  
          private void NCleanup()
          {
              try
              {
                  _currentLength = 0;
  
                  if (_waveInStream != null)
                  {
                      _waveInStream.Dispose();
                      _waveInStream = null;
                  }
  
                  if (_waveFileWriter != null)
                  {
                      try
                      {
                          _waveFileWriter.Dispose();
                          _waveFileWriter = null;
                      }
                      catch (Exception e)
                      {
                          ErrorDialogBox.ShowDialog("Error when stopping", "File writing stops with error.", e);
                          // eat exception locally
                      }
                  }
              }
              catch (Exception e)
              {
                  ErrorDialogBox.ShowDialog("Error when resource releasing",
                                                "Resources cannot be released successfully.", e);
                  throw;
              }
          }
  
          private bool NInputDeviceExists()
          {
              return WaveIn.DeviceCount > 0;
          }
  
          private void NStartRecordAudio(string fileName, int rate, int bits, int channel, bool isBackground)
          {
              try
              {
                  // prepare wave header and wav output file
                  if (isBackground)
                  {
                      _waveInStream = new WaveInEvent();
                  }
                  else
                  {
                      _waveInStream = new WaveIn();
                  }
  
                  _waveInStream.WaveFormat = new WaveFormat(rate, bits, channel);
                  _waveFileWriter = new WaveFileWriter(fileName, _waveInStream.WaveFormat);
  
                  _waveInStream.DataAvailable += WaveInStreamOnDataAvailable;
                  //_waveInStream.RecordingStopped += WaveInStreamOnRecordingStopped;
  
                  // start recording here
                  _waveInStream.StartRecording();
              }
              catch (Exception e)
              {
                  ErrorDialogBox.ShowDialog("Error during recording", "Audio record cannot be started.", e);
                  throw;
              }
          }
  
          private void NStopRecordAudio()
          {
              try
              {
                  if (_waveInStream != null)
                  {
                      _waveInStream.StopRecording();
                  }
              }
              catch (Exception e)
              {
                  ErrorDialogBox.ShowDialog("Error when Stopping", "Audio recording stops with error.", e);
                  throw;
              }
          }
  
          private void NMergeAudios(string[] audios, string outputName)
          {
              try
              {
                  byte[] buffer = new byte[2048];
                  WaveFileWriter writer = null;
  
                  // delete the old file if it exists
                  if (File.Exists(outputName))
                  {
                      File.Delete(outputName);
                  }
  
                  if (audios.Length == 1)
                  {
                      if (audios[0] != outputName)
                      {
                          File.Move(audios[0], outputName);
                      }
  
                      return;
                  }
  
                  foreach (string audio in audios)
                  {
                      using (WaveFileReader reader = new WaveFileReader(audio))
                      {
                          if (writer == null)
                          {
                              writer = new WaveFileWriter(outputName, reader.WaveFormat);
                          }
                          else
                          {
                              if (!reader.WaveFormat.Equals(writer.WaveFormat))
                              {
                                  throw new InvalidOperationException(
                                      String.Format("Can't concatenate {0} files that don't share the same format", Audio.RecordedFormatName));
                              }
                          }
  
                          int read;
                          while ((read = reader.Read(buffer, 0, buffer.Length)) > 0)
                          {
                              writer.Write(buffer, 0, read);
                          }
                      }
  
                      File.Delete(audio);
                  }
  
                  if (writer != null)
                  {
                      writer.Dispose();
                  }
              }
              catch (Exception e)
              {
                  ErrorDialogBox.ShowDialog("Error when Merging", "Audios cannot be merged.", e);
                  throw;
              }
          }
  
          private void NMergeAudios(string path, string baseName, string outputName)
          {
              IEnumerable<string> audioFiles = Directory.EnumerateFiles(path, String.Format("*.{0}", Audio.RecordedFormatExtension));
              string[] audios = audioFiles.Where(audio => audio.Contains(baseName)).ToArray();
  
              NMergeAudios(audios, outputName);
          }
  
          private int NGetRecordLengthMillis()
          {
              return _currentLength;
          }
          # endregion
  
          # region Helper Functions
          private void ResetRecorder()
          {
              soundTrackBar.Value = 0;
              timerLabel.Text = NarrationsLabText.RecorderInitialTimer;
              statusLabel.Text = NarrationsLabText.RecorderReadyStatusLabel;
  
              recButton.Image = Properties.Resources.Record;
              playButton.Image = Properties.Resources.Play;
  
              _recButtonStatus = RecorderStatus.Idle;
              _playButtonStatus = RecorderStatus.Idle;
          }
  
          private void ResetTimer()
          {
              _timerCnt = 0;
              timerLabel.Text = NarrationsLabText.RecorderInitialTimer;
              if (_timer != null)
              {
                  _timer.Dispose();
              }
          }
  
          private void ResetTrackbar(int soundBarDefaultPos)
          {
              if (_trackbarThread != null && _trackbarThread.IsAlive)
              {
                  _trackbarThread.Interrupt();
              }
  
              if (_stopwatch != null)
              {
                  _stopwatch.Reset();
              }
  
              soundTrackBar.Value = soundBarDefaultPos;
          }
  
          private void ResetSession()
          {
              // close unfinished sound session, both from wavin and mci
              AudioHelper.CloseAudio();
              NCleanup();
  
              // reset timer and trackbar
              ResetTimer();
              ResetTrackbar(0);
          }
  
          private void SetAllRecorderButtonState(bool enable)
          {
              recButton.Enabled = enable;
              playButton.Enabled = enable;
              stopButton.Enabled = enable;
          }
  
          private int GetRelativeSlideIndex(int curId)
          {
              if (!_slideRelativeMapper.ContainsKey(curId))
              {
                  _slideRelativeMapper[curId] = _relativeSlideCounter;
  
                  _relativeSlideCounter++;
              }
  
              return _slideRelativeMapper[curId];
          }
  
          private int GetRecordIndexFromScriptIndex(int relativeId, int scriptIndex)
          {
              int recordIndex = -1;
  
              // if no matched script, return -1 directly
              if (scriptIndex == -1)
              {
                  return -1;
              }
  
              for (int i = 0; i < _audioList[relativeId].Count; i++)
              {
                  Audio audio = _audioList[relativeId][i];
  
                  if (audio.MatchScriptID == scriptIndex)
                  {
                      recordIndex = i;
                  }
  
                  // since the list is sorted according to match script id, if the current
                  // matched script ID is larger than script index, we can conclude that
                  // there's no mactched record
                  if (audio.MatchScriptID > scriptIndex)
                  {
                      break;
                  }
              }
  
              return recordIndex;
          }
  
          private Audio GetPlaybackFromList()
          {
              int relativeSlideId = GetRelativeSlideIndex(PowerPointCurrentPresentationInfo.CurrentSlide.ID);
  
gt:             int playbackIndex = -1;