/**********************************************************************

  Audacity: A Digital Audio Editor

  QualityPrefs.cpp

  Joshua Haberman
  James Crook


*******************************************************************//**

\class QualityPrefs
\brief A PrefsPanel used for setting audio quality.

*//*******************************************************************/

#include "../Audacity.h"
#include "QualityPrefs.h"

#include <wx/defs.h>

#include "../AudioIO.h"
#include "../Dither.h"
#include "../Prefs.h"
#include "../Resample.h"
#include "../SampleFormat.h"
#include "../ShuttleGui.h"
#include "../Internat.h"

#define ID_SAMPLE_RATE_CHOICE           7001

//////////

static const ComponentInterfaceSymbol choicesFormat[] = {
   { wxT("Format16Bit"), XO("16-bit") },
   { wxT("Format24Bit"), XO("24-bit") },
   { wxT("Format32BitFloat"), XO("32-bit float") }
};
static const size_t nChoicesFormat = WXSIZEOF( choicesFormat );
static const int intChoicesFormat[] = {
   int16Sample,
   int24Sample,
   floatSample
};
static_assert( nChoicesFormat == WXSIZEOF(intChoicesFormat), "size mismatch" );

static const size_t defaultChoiceFormat = 2; // floatSample

static EncodedEnumSetting formatSetting{
   wxT("/SamplingRate/DefaultProjectSampleFormatChoice"),
   choicesFormat, nChoicesFormat, defaultChoiceFormat,
   
   intChoicesFormat,
   wxT("/SamplingRate/DefaultProjectSampleFormat"),
};

//////////
static const ComponentInterfaceSymbol choicesDither[] = {
   { XO("None") },
   { XO("Rectangle") },
   { XO("Triangle") },
   { XO("Shaped") },
};
static const size_t nChoicesDither = WXSIZEOF( choicesDither );
static const int intChoicesDither[] = {
   (int) DitherType::none,
   (int) DitherType::rectangle,
   (int) DitherType::triangle,
   (int) DitherType::shaped,
};
static_assert(
   nChoicesDither == WXSIZEOF( intChoicesDither ),
   "size mismatch"
);

static const size_t defaultFastDither = 0; // none

static EncodedEnumSetting fastDitherSetting{
   wxT("Quality/DitherAlgorithmChoice"),
   choicesDither, nChoicesDither, defaultFastDither,
   intChoicesDither,
   wxT("Quality/DitherAlgorithm")
};

static const size_t defaultBestDither = 3; // shaped

static EncodedEnumSetting bestDitherSetting{
   wxT("Quality/HQDitherAlgorithmChoice"),
   choicesDither, nChoicesDither, defaultBestDither,

   intChoicesDither,
   wxT("Quality/HQDitherAlgorithm")
};

//////////
BEGIN_EVENT_TABLE(QualityPrefs, PrefsPanel)
   EVT_CHOICE(ID_SAMPLE_RATE_CHOICE, QualityPrefs::OnSampleRateChoice)
END_EVENT_TABLE()

QualityPrefs::QualityPrefs(wxWindow * parent, wxWindowID winid)
/* i18n-hint: meaning accuracy in reproduction of sounds */
:  PrefsPanel(parent, winid, _("Quality"))
{
   Populate();
}

QualityPrefs::~QualityPrefs()
{
}

void QualityPrefs::Populate()
{
   // First any pre-processing for constructing the GUI.
   GetNamesAndLabels();
   gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleRate"),
                &mOtherSampleRateValue,
                44100);

   //------------------------- Main section --------------------
   // Now construct the GUI itself.
   // Use 'eIsCreatingFromPrefs' so that the GUI is
   // initialised with values from gPrefs.
   ShuttleGui S(this, eIsCreatingFromPrefs);
   PopulateOrExchange(S);
   // ----------------------- End of main section --------------

   wxCommandEvent e;
   OnSampleRateChoice(e); // Enable/disable the control.
}

/// Gets the lists of names and lists of labels which are
/// used in the choice controls.
/// The names are what the user sees in the wxChoice.
/// The corresponding labels are what gets stored.
void QualityPrefs::GetNamesAndLabels()
{
   //------------ Sample Rate Names
   // JKC: I don't understand the following comment.
   //      Can someone please explain or correct it?
   // XXX: This should use a previously changed, but not yet saved
   //      sound card setting from the "I/O" preferences tab.
   // LLL: It means that until the user clicks "Ok" in preferences, the
   //      GetSupportedSampleRates() call should use the devices they
   //      may have changed on the Audio I/O page.  As coded, the sample
   //      rates it will return could be completely invalid as they will
   //      be what's supported by the devices that were selected BEFORE
   //      coming into preferences.
   //
   //      GetSupportedSampleRates() allows passing in device names, but
   //      how do you get at them as they are on the Audio I/O page????
   for (int i = 0; i < AudioIO::NumStandardRates; i++) {
      int iRate = AudioIO::StandardRates[i];
      mSampleRateLabels.push_back(iRate);
      mSampleRateNames.Add(wxString::Format(wxT("%i Hz"), iRate));
   }

   mSampleRateNames.Add(_("Other..."));

   // The label for the 'Other...' case can be any value at all.
   mSampleRateLabels.push_back(44100); // If chosen, this value will be overwritten
}

void QualityPrefs::PopulateOrExchange(ShuttleGui & S)
{
   S.SetBorder(2);
   S.StartScroller();

   S.StartStatic(_("Sampling"));
   {
      S.StartMultiColumn(2);
      {
         S.AddPrompt(_("Default Sample &Rate:"));

         S.StartMultiColumn(2);
         {
            // If the value in Prefs isn't in the list, then we want
            // the last item, 'Other...' to be shown.
            S.SetNoMatchSelector(mSampleRateNames.GetCount() - 1);
            // First the choice...
            // We make sure it uses the ID we want, so that we get changes
            S.Id(ID_SAMPLE_RATE_CHOICE);
            // We make sure we have a pointer to it, so that we can drive it.
            mSampleRates = S.TieNumberAsChoice( {},
                                       wxT("/SamplingRate/DefaultProjectSampleRate"),
                                       AudioIO::GetOptimalSupportedSampleRate(),
                                       mSampleRateNames,
                                       mSampleRateLabels);

            // Now do the edit box...
            mOtherSampleRate = S.TieNumericTextBox( {},
                                                   mOtherSampleRateValue,
                                                   15);
         }
         S.EndHorizontalLay();

         S.TieChoice(_("Default Sample &Format:"),
                     formatSetting);
      }
      S.EndMultiColumn();
   }
   S.EndStatic();

   S.StartStatic(_("Real-time Conversion"));
   {
      S.StartMultiColumn(2, wxEXPAND);
      {
         S.TieChoice(_("Sample Rate Con&verter:"),
                     Resample::FastMethodSetting);

         /* i18n-hint: technical term for randomization to reduce undesirable resampling artifacts */
         S.TieChoice(_("&Dither:"),
                     fastDitherSetting);
      }
      S.EndMultiColumn();
   }
   S.EndStatic();

   S.StartStatic(_("High-quality Conversion"));
   {
      S.StartMultiColumn(2);
      {
         S.TieChoice(_("Sample Rate Conver&ter:"),
                     Resample::BestMethodSetting);

         /* i18n-hint: technical term for randomization to reduce undesirable resampling artifacts */
         S.TieChoice(_("Dit&her:"),
                     bestDitherSetting);
      }
      S.EndMultiColumn();
   }
   S.EndStatic();
   S.EndScroller();

}

/// Enables or disables the Edit box depending on
/// whether we selected 'Other...' or not.
void QualityPrefs::OnSampleRateChoice(wxCommandEvent & WXUNUSED(e))
{
   int sel = mSampleRates->GetSelection();
   mOtherSampleRate->Enable(sel == (int)mSampleRates->GetCount() - 1);
}

bool QualityPrefs::Commit()
{
   ShuttleGui S(this, eIsSavingToPrefs);
   PopulateOrExchange(S);

   // The complex compound control may have value 'other' in which case the
   // value in prefs comes from the second field.
   if (mOtherSampleRate->IsEnabled()) {
      gPrefs->Write(wxT("/SamplingRate/DefaultProjectSampleRate"), mOtherSampleRateValue);
      gPrefs->Flush();
   }

   // Tell CopySamples() to use these ditherers now
   InitDitherers();

   return true;
}

wxString QualityPrefs::HelpPageName()
{
   return "Quality_Preferences";
}

PrefsPanel *QualityPrefsFactory::operator () (wxWindow *parent, wxWindowID winid)
{
   wxASSERT(parent); // to justify safenew
   return safenew QualityPrefs(parent, winid);
}

sampleFormat QualityPrefs::SampleFormatChoice()
{
   return (sampleFormat)formatSetting.ReadInt();
}

DitherType QualityPrefs::FastDitherChoice()
{
   return (DitherType) fastDitherSetting.ReadInt();
}

DitherType QualityPrefs::BestDitherChoice()
{
   return (DitherType) bestDitherSetting.ReadInt();
}

