//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// CommonHelpers satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.Threading;

namespace CommonHelpers {

//**************************************************************************************************
/// <summary>
/// </summary>
[HThreadSafe]
public static partial class HBeeper {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

static HBeeper()
{
   CommonHelpers_Plus.Access();
}

//--------------------------------------------------------------------------------------------------

static void PlayBeeps(int[] beeps)
{
   for (int i=0; i<beeps.Length && !_StopFlag_; i+=2)
   {
      if (Monitor.TryEnter(_StopSignalSyncObject_))
         Monitor.Exit(_StopSignalSyncObject_);
      else
      {
         if (Monitor.TryEnter(_StopControlSyncObject_))
            Monitor.Exit(_StopControlSyncObject_);
         else
            _StopOwnerThread_ = _StopInitiatorThread_;
         break;
      }
      int
         frequency= beeps[i],
         duration = beeps[i+1];
      if (frequency>0)
      {
#if WIN32
         if ( !Win32_Plus.Beep( (uint)frequency, (uint)duration ) )
            throw HWin32Error.CreateExceptionFromLastError();
#else // !WIN32:
         HConsole.TryBeep( frequency, duration );
#endif // !WIN32
      }
      else
         Thread.Sleep( duration );
   }
}

//--------------------------------------------------------------------------------------------------

static int[] PrepareBeeps(double[] beeps)
{
   if (!HArith.IsEven(beeps.Length))
      throw new ArgumentException(
         "Beep sequence must be of even length." );

   var result = new int[ beeps.Length ];

   lock (_SyncRoot_)
      for (int i=0; i<beeps.Length; i+=2)
      {
         double
            frequencyScale= beeps[i  ],
            durationScale = beeps[i+1];
         int
            frequency, duration;
         try
         {
            frequency = (int)( BaseFrequency*frequencyScale );
            duration  = (int)( BaseDuration *durationScale  );
         }
         catch (Exception e)
         {
            if (e is OverflowException)
               throw new ArgumentException("Incorrect beep sequence.");
            throw;
         }
         if ( frequencyScale!=0 && !IsValidFrequency(frequency) )
            if (frequency!=0)
               throw HArgumentExceptionCreator.Create(
                  "Incorrect beep frequency: {0} (Hz).\n"+
                  "Valid range: {1}..{2} (Hz) or 0 (for silence).",
                  frequency, MinFrequency, MaxFrequency );
            else
               throw HArgumentExceptionCreator.Create(
                  "Incorrect beep frequency.\n"+
                  "Valid range: {0}..{1} (Hz) or 0 (for silence).",
                  MinFrequency, MaxFrequency );
         VerifyDuration( duration );

         result[i  ] = frequency;
         result[i+1] = duration;
      }

   return result;
}

//--------------------------------------------------------------------------------------------------

static void PlaybackThread()
{
   lock (_ThreadActivitySyncObject_)
   {
      Monitor.Pulse(_ThreadActivitySyncObject_);
      for (;;)
      {
         Monitor.Wait(_ThreadActivitySyncObject_);
         lock (_PlaybackSyncObject_)
         {
            _StopFlag_ = false;
            _StartedEvent_.Set();
            PlayBeeps( _PlaybackBeeps_ );
            _PlaybackThread_.Priority = _DefaultPriority_;
         }
      }
   }
}

//==================================================================================================

const ThreadPriority
   _DefaultPriority_ = ThreadPriority.Highest,
   _PlaybackPriority_ = ThreadPriority.AboveNormal;

static volatile int
   _BaseFrequency_ = BaseFrequencyDefault,
   _BaseDuration_  = BaseDurationDefault;
static volatile bool
   _IsMute_,
   _IsInitialized_,
   _StopFlag_;
static volatile int[]
   _PlaybackBeeps_;
static volatile AutoResetEvent
   _StartedEvent_;
static volatile Thread
   _PlaybackThread_,
   _StopInitiatorThread_,
   _StopOwnerThread_;

static readonly object
   _SyncRoot_                 = new object(),
   _InitializationSyncObject_ = new object(),
   _StartSyncObject_          = new object(),
   _ThreadActivitySyncObject_ = new object(),
   _PlaybackSyncObject_       = new object(),
   _StopSignalSyncObject_     = new object(),
   _StopControlSyncObject_    = new object();

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // HBeeper
//**************************************************************************************************

} // CommonHelpers
