/***************************************************************************
    begin                : Mon Okt 14 2002
    copyright            : (C) 2002 by Gunnar Schmi Dt
    email                : gunnar@schmi-dt.de
    current mainainer:   : Gary Cramblitt <garycramblitt@comcast.net> 
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

// TQt includes. 
#include <tqlayout.h>
#include <tqlabel.h>
#include <tqgroupbox.h>
#include <tqstring.h>
#include <tqstringlist.h>
#include <tqdir.h>
#include <tqfileinfo.h>
#include <tqfile.h>

// KDE includes.
#include <tdetempfile.h>
#include <tdeaboutdata.h>
#include <tdeaboutapplication.h>
#include <kdebug.h>
#include <tdelocale.h>
#include <kdialog.h>
#include <kcombobox.h>
#include <tdestandarddirs.h>
#include <kurlrequester.h>
#include <kdialogbase.h>
#include <klineedit.h>
#include <knuminput.h>
#include <kprogress.h>

// KTTS includes.
#include <pluginconf.h>
#include <testplayer.h>
#include <talkercode.h>

// Hadifix includes.
#include "hadifixproc.h"
#include "voicefileui.h"
#include "hadifixconfigui.h"
#include "hadifixconf.h"
#include "hadifixconf.moc"

class HadifixConfPrivate {
   friend class HadifixConf;
   private:
      HadifixConfPrivate() {
         hadifixProc = 0;
         progressDlg = 0;
         findInitialConfig();
      };
      
      ~HadifixConfPrivate() {
         if (hadifixProc) hadifixProc->stopText();
         delete hadifixProc;
         if (!waveFile.isNull()) TQFile::remove(waveFile);
         delete progressDlg;
      };
      
      #include "initialconfig.h"

      void setConfiguration (TQString hadifixExec,  TQString mbrolaExec,
                             TQString voice,        bool male,
                             int volume, int time, int pitch,
                             TQString codecName)
      {
         configWidget->hadifixURL->setURL (hadifixExec);
         configWidget->mbrolaURL->setURL (mbrolaExec);
         configWidget->setVoice (voice, male);
         
         configWidget->volumeBox->setValue (volume);
         configWidget->timeBox->setValue (time);
         configWidget->frequencyBox->setValue (pitch);
         int codec = PlugInProc::codecNameToListIndex(codecName, codecList);
         configWidget->characterCodingBox->setCurrentItem(codec);
      }

      void initializeVoices () {
         TQStringList::iterator it;
         for (it = defaultVoices.begin(); it != defaultVoices.end(); ++it) {
            HadifixProc::VoiceGender gender;
            TQString name = TQFileInfo(*it).fileName();
            gender = HadifixProc::determineGender(defaultMbrolaExec, *it);
            if (gender == HadifixProc::MaleGender)
                configWidget->addVoice(*it, true, i18n("Male voice \"%1\"").arg(name));
            else if (gender == HadifixProc::FemaleGender)
                configWidget->addVoice(*it, false, i18n("Female voice \"%1\"").arg(name));
            else {
               if (name == "de1")
                   configWidget->addVoice(*it, false, i18n("Female voice \"%1\"").arg(name));
               else {
                   configWidget->addVoice(*it, true,  i18n("Unknown voice \"%1\"").arg(name));
                   configWidget->addVoice(*it, false, i18n("Unknown voice \"%1\"").arg(name));
               }
            }
         }
      };

      void initializeCharacterCodes() {
         // Build codec list and fill combobox.
         codecList = PlugInProc::buildCodecList();
         configWidget->characterCodingBox->clear();
         configWidget->characterCodingBox->insertStringList(codecList);
      }

      void setDefaultEncodingFromVoice() {
         TQString voiceFile = configWidget->getVoiceFilename();
         TQString voiceCode = TQFileInfo(voiceFile).baseName(false);
         voiceCode = voiceCode.left(2);
         TQString codecName = "Local";
         if (voiceCode == "de") codecName = "ISO 8859-1";
         if (voiceCode == "hu") codecName = "ISO 8859-2";
         configWidget->characterCodingBox->setCurrentItem(PlugInProc::codecNameToListIndex(
            codecName, codecList));
}

      void setDefaults () {
         TQStringList::iterator it = defaultVoices.begin();
         // Find a voice that matches language code, if any.
         if (!languageCode.isEmpty())
         {
            TQString justLang = languageCode.left(2);
            for (;it != defaultVoices.end();++it)
            {
               TQString voiceCode = TQFileInfo(*it).baseName(false).left(2);
               if (voiceCode == justLang) break;
            }
            if (it == defaultVoices.end()) it = defaultVoices.begin();
         }
         HadifixProc::VoiceGender gender;
         gender = HadifixProc::determineGender(defaultMbrolaExec, *it);
         
         setConfiguration (defaultHadifixExec, defaultMbrolaExec,
                           *it, gender == HadifixProc::MaleGender,
                           100, 100, 100, "Local");
      };

      void load (TDEConfig *config, const TQString &configGroup) {
         config->setGroup(configGroup);
         
         TQString voice = config->readEntry("voice", configWidget->getVoiceFilename());
         
         HadifixProc::VoiceGender gender;
         gender = HadifixProc::determineGender(defaultMbrolaExec, voice);
         bool isMale = (gender == HadifixProc::MaleGender);

         TQString defaultCodecName = "Local";
         // TODO: Need a better way to determine proper codec for each voice.
         // This will do for now.
         TQString voiceCode = TQFileInfo(voice).baseName(false);
         if (voiceCode.left(2) == "de") defaultCodecName = "ISO 8859-1";
         if (voiceCode.left(2) == "hu") defaultCodecName = "ISO 8859-2";
         
         setConfiguration (
               config->readEntry ("hadifixExec",defaultHadifixExec),
               config->readEntry ("mbrolaExec", defaultMbrolaExec),
               config->readEntry ("voice",      voice),
               config->readBoolEntry("gender",  isMale),
               config->readNumEntry ("volume",  100),
               config->readNumEntry ("time",    100),
               config->readNumEntry ("pitch",   100),
               config->readEntry ("codec",      defaultCodecName)
         );
      };

      void save (TDEConfig *config, const TQString &configGroup) {
         config->setGroup(configGroup);
         config->writeEntry ("hadifixExec", PlugInConf::realFilePath(configWidget->hadifixURL->url()));
         config->writeEntry ("mbrolaExec", PlugInConf::realFilePath(configWidget->mbrolaURL->url()));
         config->writeEntry ("voice",      configWidget->getVoiceFilename());
         config->writeEntry ("gender",     configWidget->isMaleVoice());
         config->writeEntry ("volume",     configWidget->volumeBox->value());
         config->writeEntry ("time",       configWidget->timeBox->value());
         config->writeEntry ("pitch",      configWidget->frequencyBox->value());
         config->writeEntry ("codec",      PlugInProc::codecIndexToCodecName(
                                              configWidget->characterCodingBox->currentItem(), codecList));
      }

      HadifixConfigUI *configWidget;

      TQString languageCode;
      TQString defaultHadifixExec;
      TQString defaultMbrolaExec;
      TQStringList defaultVoices;
      TQStringList codecList;

      // Wave file playing on play object.
      TQString waveFile;
      // Synthesizer.
      HadifixProc* hadifixProc;
      // Progress Dialog.
      KProgressDialog* progressDlg;
};

/** Constructor */
HadifixConf::HadifixConf( TQWidget* parent, const char* name, const TQStringList &) : 
   PlugInConf( parent, name ){
   // kdDebug() << "HadifixConf::HadifixConf: Running" << endl;
   TQVBoxLayout *layout = new TQVBoxLayout (this, KDialog::marginHint(), KDialog::spacingHint(), "CommandConfigWidgetLayout");
   layout->setAlignment (TQt::AlignTop); 

   d = new HadifixConfPrivate();
   d->configWidget = new HadifixConfigUI (this, "configWidget");
   
   TQString file = locate("data", "LICENSES/LGPL_V2");
   i18n("This plugin is distributed under the terms of the GPL v2 or later.");
   
   connect(d->configWidget->voiceButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(voiceButton_clicked()));
   connect(d->configWidget->testButton, TQ_SIGNAL(clicked()), this, TQ_SLOT(testButton_clicked()));
   connect(d->configWidget, TQ_SIGNAL(changed(bool)), this, TQ_SLOT(configChanged (bool)));
   connect(d->configWidget->characterCodingBox, TQ_SIGNAL(textChanged(const TQString&)),
        this, TQ_SLOT(configChanged()));
   connect(d->configWidget->voiceCombo, TQ_SIGNAL(activated(int)), this, TQ_SLOT(voiceCombo_activated(int)));
   d->initializeCharacterCodes();
   d->initializeVoices();
   d->setDefaults();
   layout->addWidget (d->configWidget);
}

/** Destructor */
HadifixConf::~HadifixConf(){
   // kdDebug() << "HadifixConf::~HadifixConf: Running" << endl;
   delete d;
}

void HadifixConf::load(TDEConfig *config, const TQString &configGroup) {
   // kdDebug() << "HadifixConf::load: Running" << endl;
   d->setDefaults();
   d->load (config, configGroup);
}

void HadifixConf::save(TDEConfig *config, const TQString &configGroup) {
   // kdDebug() << "HadifixConf::save: Running" << endl;
   d->save (config, configGroup);
}

void HadifixConf::defaults() {
   // kdDebug() << "HadifixConf::defaults: Running" << endl;
   d->setDefaults();
}

void HadifixConf::setDesiredLanguage(const TQString &lang)
{
    d->languageCode = lang;
}

TQString HadifixConf::getTalkerCode()
{
    if (!d->configWidget->hadifixURL->url().isEmpty() && !d->configWidget->mbrolaURL->url().isEmpty())
    {
        TQString voiceFile = d->configWidget->getVoiceFilename();
        if (TQFileInfo(voiceFile).exists())
        {
            // mbrola voice file names usually start with two-letter language code,
            // but this is by no means guaranteed.
            TQString voiceCode = TQFileInfo(voiceFile).baseName(false);
            TQString voiceLangCode = voiceCode.left(2);
            if (d->languageCode.left(2) != voiceLangCode)
            {
               // Verify that first two letters of voice filename are a valid language code.
               // If they are, switch to that language.
               if (!TalkerCode::languageCodeToLanguage(voiceLangCode).isEmpty())
                   d->languageCode = voiceLangCode;
            }
            TQString gender = "male";
            if (!d->configWidget->isMaleVoice()) gender = "female";
            TQString volume = "medium";
            if (d->configWidget->volumeBox->value() < 75) volume = "soft";
            if (d->configWidget->volumeBox->value() > 125) volume = "loud";
            TQString rate = "medium";
            if (d->configWidget->timeBox->value() < 75) rate = "slow";
            if (d->configWidget->timeBox->value() > 125) rate = "fast";
            return TQString(
                    "<voice lang=\"%1\" name=\"%2\" gender=\"%3\" />"
                    "<prosody volume=\"%4\" rate=\"%5\" />"
                    "<kttsd synthesizer=\"%6\" />")
                    .arg(d->languageCode)
                    .arg(voiceCode)
                    .arg(gender)
                    .arg(volume)
                    .arg(rate)
                    .arg("Hadifix");
        }
    }
    return TQString();
}

void HadifixConf::voiceButton_clicked () {
   KDialogBase *dialog = new KDialogBase (this, 0, true,
                                      i18n("Voice File - Hadifix Plugin"),
                                      KDialogBase::Ok|KDialogBase::Cancel,
                                      KDialogBase::Ok, true);
   VoiceFileWidget *widget = new VoiceFileWidget(dialog);
   dialog->setMainWidget (widget);

   widget->femaleOption->setChecked(!d->configWidget->isMaleVoice());
   widget->maleOption->setChecked(d->configWidget->isMaleVoice());
   widget->voiceFileURL->setURL(d->configWidget->getVoiceFilename());
   widget->mbrola = d->defaultMbrolaExec;

   if (dialog->exec() == TQDialog::Accepted) {
      d->configWidget->setVoice (widget->voiceFileURL->url(),
                                 widget->maleOption->isChecked());
      d->setDefaultEncodingFromVoice();
      emit changed(true);
   }
   
   delete dialog;
}

void HadifixConf::voiceCombo_activated(int /*index*/)
{
    d->setDefaultEncodingFromVoice();
}

void HadifixConf::testButton_clicked () {
   // If currently synthesizing, stop it.
   if (d->hadifixProc)
      d->hadifixProc->stopText();
   else
   {
      d->hadifixProc = new HadifixProc();
      connect (d->hadifixProc, TQ_SIGNAL(stopped()), this, TQ_SLOT(slotSynthStopped()));
   }
   // Create a temp file name for the wave file.
   KTempFile tempFile (locateLocal("tmp", "hadifixplugin-"), ".wav");
   TQString tmpWaveFile = tempFile.file()->name();
   tempFile.close();

    // Tell user to wait.
   d->progressDlg = new KProgressDialog(d->configWidget, "ktts_hadifix_testdlg",
      i18n("Testing"),
      i18n("Testing."),
      true);
   d->progressDlg->progressBar()->hide();
   d->progressDlg->setAllowCancel(true);

   // Speak a German sentence as hadifix is a German tts
   // TODO: Actually, Hadifix does support English (and other languages?) as well,
   // If you install the right voice files.  The hard part is finding and installing 
   // a working txt2pho for the desired language.  There seem to be some primitive french,
   // italian, and a few others, written in perl, but they have many issues.
   // Go to the mbrola website and click on "TTS" to learn more.

   // TQString testMsg = "K D E ist eine moderne grafische Arbeitsumgebung für UNIX-Computer.";
   TQString testMsg = testMessage(d->languageCode);
   connect (d->hadifixProc, TQ_SIGNAL(synthFinished()), this, TQ_SLOT(slotSynthFinished()));
   d->hadifixProc->synth (testMsg,
      realFilePath(d->configWidget->hadifixURL->url()),
      d->configWidget->isMaleVoice(),
      realFilePath(d->configWidget->mbrolaURL->url()),
      d->configWidget->getVoiceFilename(),
      d->configWidget->volumeBox->value(),
      d->configWidget->timeBox->value(),
      d->configWidget->frequencyBox->value(),
      PlugInProc::codecIndexToCodec(d->configWidget->characterCodingBox->currentItem(), d->codecList),
      tmpWaveFile);

   // Display progress dialog modally.  Processing continues when plugin signals synthFinished,
   // or if user clicks Cancel button.
   d->progressDlg->exec();
   disconnect (d->hadifixProc, TQ_SIGNAL(synthFinished()), this, TQ_SLOT(slotSynthFinished()));
   if (d->progressDlg->wasCancelled()) d->hadifixProc->stopText();
   delete d->progressDlg;
   d->progressDlg = 0;
}

void HadifixConf::slotSynthFinished()
{
    // If user canceled, progress dialog is gone, so exit.
    if (!d->progressDlg)
    {
        d->hadifixProc->ackFinished();
        return;
    }
    // Hide the Cancel button so user can't cancel in the middle of playback.
    d->progressDlg->showCancelButton(false);
    // Get new wavefile name.
    d->waveFile = d->hadifixProc->getFilename();
    // Tell synth we're done.
    d->hadifixProc->ackFinished();
    // Play the wave file (possibly adjusting its Speed).
    // Player object deletes the wave file when done.
    if (m_player) m_player->play(d->waveFile);
    TQFile::remove(d->waveFile);
    d->waveFile = TQString();
    if (d->progressDlg) d->progressDlg->close();
}

void HadifixConf::slotSynthStopped()
{
    // Clean up after canceling test.
    TQString filename = d->hadifixProc->getFilename();
    // kdDebug() << "HadifixConf::slotSynthStopped: filename = " << filename << endl;
    if (!filename.isNull()) TQFile::remove(filename);
}
