//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// CommonHelpers satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
namespace CommonHelpers {

using System;
using System.Threading;

//**************************************************************************************************
partial class HBeeper {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public const int
   MinFrequency  = 37, // (0x25)
   MaxFrequency  = Int16.MaxValue, // 32767 (0x7FFF)

   MinDuration   = 0,
   MaxDuration   = Int32.MaxValue,

   BaseFrequencyDefault = 440,
   BaseDurationDefault  = 100;

public const double
   SimpleBeepRelativeFrequency = 2.0,
   SimpleBeepRelativeDuration  = 2.0;

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

public static int BaseFrequency
{
   get
   {
      return _BaseFrequency_;
   }
   set
   {
      VerifyFrequency( value );
      lock (_SyncRoot_)
         _BaseFrequency_ = value;
   }
}

public static int BaseDuration
{
   get
   {
      return _BaseDuration_;
   }
   set
   {
      VerifyDuration( value );
      lock (_SyncRoot_)
         _BaseDuration_ = value;
   }
}

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

public static bool IsMute
{
   get
   {
      return _IsMute_;
   }
   set
   {
      lock (_SyncRoot_)
         _IsMute_ = value;
   }
}

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

public static bool IsInitialized
{
   get { return _IsInitialized_; }
}

public static bool IsPlaybackActive
{
   get
   {
      if (Monitor.TryEnter(_PlaybackSyncObject_))
      {
         Monitor.Exit(_PlaybackSyncObject_);
         return true;
      }
      return false;
   }
}

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

public static bool IsValidFrequency(int frequency)
{
   return HOps.IsBetween(frequency,MinFrequency,MaxFrequency);
}

public static bool IsValidDuration(int duration)
{
   return HOps.IsBetween(duration,MinDuration,MaxDuration);
}

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

/// <exception cref="ArgumentOutOfRangeException" />
public static void VerifyFrequency(int frequency)
{
   if (!IsValidFrequency( frequency ))
      throw HArgumentOutOfRangeExceptionCreator.Create(
         "Incorrect beep frequency: {0} (Hz).\n"+
         "Valid range: {1}..{2} (Hz).",
         frequency, MinFrequency, MaxFrequency );
}

/// <exception cref="ArgumentOutOfRangeException" />
public static void VerifyDuration(int duration)
{
   if (!IsValidDuration( duration ))
      throw HArgumentOutOfRangeExceptionCreator.Create(
         "Incorrect beep duration: {0} (ms).\n"+
         "Valid range: {1}..{2}.",
         duration, MinDuration, MaxDuration );
}

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

public static void Initialize()
{
   lock (_InitializationSyncObject_)
      if (!_IsInitialized_)
      {
         _PlaybackThread_ = new Thread( PlaybackThread );
         _PlaybackThread_.Priority = _DefaultPriority_;
         _PlaybackThread_.IsBackground = true;

         _StartedEvent_ = new AutoResetEvent(false);

         lock (_ThreadActivitySyncObject_)
         {
            _PlaybackThread_.Start();
            Monitor.Wait(_ThreadActivitySyncObject_);
         }

         _IsInitialized_ = true;
      }
}

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

public static void Beep()
{
   Beep( SimpleBeepRelativeFrequency );
}

public static void Beep(bool synchronously)
{
   Beep( synchronously, SimpleBeepRelativeFrequency );
}

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

public static void SyncBeep()
{
   SyncBeep( SimpleBeepRelativeFrequency );
}

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

public static void Beep(double relativeFrequency)
{
   Beep( false, relativeFrequency, SimpleBeepRelativeDuration );
}

public static void Beep(bool synchronously,double relativeFrequency)
{
   Beep( synchronously, relativeFrequency, SimpleBeepRelativeDuration );
}

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

public static void SyncBeep(double relativeFrequency)
{
   SyncBeep( relativeFrequency, SimpleBeepRelativeDuration );
}

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

/// <exception cref="ArgumentOutOfRangeException" />
public static void Beep(double relativeFrequency,double relativeDuration)
{
   Beep( false, relativeFrequency, relativeDuration );
}

/// <exception cref="ArgumentOutOfRangeException" />
public static void Beep(bool synchronously,double relativeFrequency,double relativeDuration)
{
   Play( synchronously, new double[]{ relativeFrequency, relativeDuration } );
}

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

/// <exception cref="ArgumentOutOfRangeException" />
public static void SyncBeep(double relativeFrequency,double relativeDuration)
{
   Beep( true, relativeFrequency, relativeDuration );
}

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="ArgumentException" />
public static void Play(params double[] beeps)
{
   Play(false,beeps);
}

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="ArgumentException" />
public static void Play(bool synchronously,params double[] beeps)
{
   int[] beepsToPlay;

   lock (_SyncRoot_)
   {
      if (beeps==null || IsMute)
         return;
      beepsToPlay = PrepareBeeps( beeps );
   }

   Monitor.Enter(_StopSignalSyncObject_);

   lock (_StartSyncObject_)
   {
      Initialize();

      lock (_ThreadActivitySyncObject_)
      {
         Monitor.Exit(_StopSignalSyncObject_);

         _PlaybackBeeps_ = beepsToPlay;

         _PlaybackThread_.Priority =
            HOps.IsOneOf( Thread.CurrentThread.Priority,
               ThreadPriority.AboveNormal,
               ThreadPriority.Highest ) ?
            ThreadPriority.Highest : _PlaybackPriority_;

         Monitor.Pulse(_ThreadActivitySyncObject_);
      }
      _StartedEvent_.WaitOne();

      if (synchronously)
         WaitActivePlayback();
   }
}

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentOutOfRangeException" />
/// <exception cref="ArgumentException" />
public static void SyncPlay(params double[] beeps)
{
   Play(true,beeps);
}

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

public static bool StopActivePlayback()
{
   return StopActivePlayback( true );
}

public static bool StopActivePlayback(bool waitPlaybackFinish)
{
   _StopFlag_ = true;
   if (!waitPlaybackFinish)
      return false;
   lock (_StopControlSyncObject_)
   {
      _StopInitiatorThread_= Thread.CurrentThread;
      _StopOwnerThread_ = null;
      lock ( _StopSignalSyncObject_ )
         WaitActivePlayback();
      return _StopOwnerThread_ == Thread.CurrentThread;
   }
}

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

public static void WaitActivePlayback()
{
   lock (_PlaybackSyncObject_) {}
}

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

} // CommonHelpers
