/*
  Description: 
     A dialog for user to select a Talker, either by specifying
     selected Talker attributes, or by specifying all attributes
     of an existing configured Talker.

  Copyright:
  (C) 2005 by Gary Cramblitt <garycramblitt@comcast.net>
  -------------------
  Original author: 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.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ******************************************************************************/

// TQt includes.
#include <tqcheckbox.h>
#include <tqradiobutton.h>
#include <tqhbox.h>
#include <tqgroupbox.h>

// KDE includes.
#include <kcombobox.h>
#include <ktrader.h>
#include <kpushbutton.h>
#include <tdelistview.h>
#include <klineedit.h>
#include <tdeconfig.h>
#include <kdebug.h>

// KTTS includes.
#include "utils.h"
#include "selecttalkerdlg.h"
#include "selecttalkerdlg.moc"

SelectTalkerDlg::SelectTalkerDlg(
    TQWidget* parent,
    const char* name,
    const TQString& caption,
    const TQString& talkerCode,
    bool runningTalkers) :

    KDialogBase(
        parent,
        name,
        true,
        caption,
        KDialogBase::Ok|KDialogBase::Cancel,
        KDialogBase::Ok)
{
    m_widget = new SelectTalkerWidget( this );
    // TODO: How do I do this in a general way and still get KDialogBase to properly resize?
    m_widget->setMinimumSize( TQSize(700,500) );
    // setInitialSize( TQSize(700,600) );
    setMainWidget( m_widget );
    m_runningTalkers = runningTalkers;
    m_talkerCode = TalkerCode( talkerCode, false );

    // Fill combo boxes.
    KComboBox* cb = m_widget->genderComboBox;
    cb->insertItem( TQString() );
    cb->insertItem( TalkerCode::translatedGender("male") );
    cb->insertItem( TalkerCode::translatedGender("female") );
    cb->insertItem( TalkerCode::translatedGender("neutral") );

    cb = m_widget->volumeComboBox;
    cb->insertItem( TQString() );
    cb->insertItem( TalkerCode::translatedVolume("medium") );
    cb->insertItem( TalkerCode::translatedVolume("loud") );
    cb->insertItem( TalkerCode::translatedVolume("soft") );

    cb = m_widget->rateComboBox;
    cb->insertItem( TQString() );
    cb->insertItem( TalkerCode::translatedRate("medium") );
    cb->insertItem( TalkerCode::translatedRate("fast") );
    cb->insertItem( TalkerCode::translatedRate("slow") );

    cb = m_widget->synthComboBox;
    cb->insertItem( TQString() );
    TDETrader::OfferList offers = TDETrader::self()->query("KTTSD/SynthPlugin");
    for(unsigned int i=0; i < offers.count() ; ++i)
        cb->insertItem(offers[i]->name());

    // Fill List View with list of Talkers.
    m_widget->talkersListView->setSorting( -1 );
    loadTalkers( m_runningTalkers );

    // Set initial radio button state.
    if ( talkerCode.isEmpty() )
        m_widget->useDefaultRadioButton->setChecked(true);
    else
    {
        TQString dummy;
        if (talkerCode == TalkerCode::normalizeTalkerCode(talkerCode, dummy))
            m_widget->useSpecificTalkerRadioButton->setChecked(true);
        else
            m_widget->useClosestMatchRadioButton->setChecked(true);
    }

    applyTalkerCodeToControls();
    enableDisableControls();

    connect(m_widget->useDefaultRadioButton, TQT_SIGNAL(clicked()),
            this, TQT_SLOT(configChanged()));
    connect(m_widget->useClosestMatchRadioButton, TQT_SIGNAL(clicked()),
            this, TQT_SLOT(configChanged()));
    connect(m_widget->useSpecificTalkerRadioButton, TQT_SIGNAL(clicked()),
            this, TQT_SLOT(configChanged()));

    connect(m_widget->languageBrowseButton, TQT_SIGNAL(clicked()),
            this, TQT_SLOT(slotLanguageBrowseButton_clicked()));

    connect(m_widget->synthComboBox, TQT_SIGNAL(activated(const TQString&)),
            this, TQT_SLOT(configChanged()));
    connect(m_widget->genderComboBox, TQT_SIGNAL(activated(const TQString&)),
            this, TQT_SLOT(configChanged()));
    connect(m_widget->volumeComboBox, TQT_SIGNAL(activated(const TQString&)),
            this, TQT_SLOT(configChanged()));
    connect(m_widget->rateComboBox, TQT_SIGNAL(activated(const TQString&)),
            this, TQT_SLOT(configChanged()));

    connect(m_widget->synthCheckBox, TQT_SIGNAL(toggled(bool)),
            this, TQT_SLOT(configChanged()));
    connect(m_widget->genderCheckBox, TQT_SIGNAL(toggled(bool)),
            this, TQT_SLOT(configChanged()));
    connect(m_widget->volumeCheckBox, TQT_SIGNAL(toggled(bool)),
            this, TQT_SLOT(configChanged()));
    connect(m_widget->rateCheckBox, TQT_SIGNAL(toggled(bool)),
            this, TQT_SLOT(configChanged()));

    connect(m_widget->talkersListView, TQT_SIGNAL(selectionChanged()),
            this, TQT_SLOT(slotTalkersListView_selectionChanged()));

    m_widget->talkersListView->setMinimumHeight( 120 );
}

SelectTalkerDlg::~SelectTalkerDlg() { }

TQString SelectTalkerDlg::getSelectedTalkerCode()
{
    return m_talkerCode.getTalkerCode();
}

TQString SelectTalkerDlg::getSelectedTranslatedDescription()
{
    return m_talkerCode.getTranslatedDescription();
}

void SelectTalkerDlg::slotLanguageBrowseButton_clicked()
{
    // Create a  TQHBox to host TDEListView.
    TQHBox* hBox = new TQHBox(m_widget, "SelectLanguage_hbox");
    // Create a TDEListView and fill with all known languages.
    TDEListView* langLView = new TDEListView(hBox, "SelectLanguage_lview");
    langLView->addColumn(i18n("Language"));
    langLView->addColumn(i18n("Code"));
    langLView->setSelectionMode(TQListView::Single);
    TQStringList allLocales = TDEGlobal::locale()->allLanguagesTwoAlpha();
    TQString locale;
    TQString language;
    // Blank line so user can select no language.
    // Note: Don't use TQString(), which gets displayed at bottom of list, rather than top.
    TQListViewItem* item = new TDEListViewItem(langLView, "", "");
    if (m_talkerCode.languageCode().isEmpty()) item->setSelected(true);
    int allLocalesCount = allLocales.count();
    for (int ndx=0; ndx < allLocalesCount; ++ndx)
    {
        locale = allLocales[ndx];
        language = TalkerCode::languageCodeToLanguage(locale);
        item = new TDEListViewItem(langLView, language, locale);
        if (m_talkerCode.fullLanguageCode() == locale) item->setSelected(true);
    }
    // Sort by language.
    langLView->setSorting(0);
    langLView->sort();
    // Display the box in a dialog.
    KDialogBase* dlg = new KDialogBase(
        KDialogBase::Swallow,
        i18n("Select Languages"),
        KDialogBase::Help|KDialogBase::Ok|KDialogBase::Cancel,
        KDialogBase::Cancel,
        m_widget,
        "SelectLanguage_dlg",
        true,
        true);
    dlg->setMainWidget(hBox);
    dlg->setHelp("", "kttsd");
    dlg->setInitialSize(TQSize(300, 500), false);
    // TODO: This isn't working.  Furthermore, item appears selected but is not.
    langLView->ensureItemVisible(langLView->selectedItem());
    int dlgResult = dlg->exec();
    language = TQString();
    if (dlgResult == TQDialog::Accepted)
    {
        if (langLView->selectedItem())
        {
            language = langLView->selectedItem()->text(0);
            m_talkerCode.setFullLanguageCode( langLView->selectedItem()->text(1) );
        }
    }
    delete dlg;
    m_widget->languageLineEdit->setText(language);
    m_widget->languageCheckBox->setChecked( !language.isEmpty() );
    configChanged();
}

void SelectTalkerDlg::slotTalkersListView_selectionChanged()
{
    TQListViewItem* item = m_widget->talkersListView->selectedItem();
    if ( !item ) return;
    if (!m_widget->useSpecificTalkerRadioButton->isChecked()) return;
    configChanged();
}

void SelectTalkerDlg::configChanged()
{
    applyControlsToTalkerCode();
    applyTalkerCodeToControls();
    enableDisableControls();
}

void SelectTalkerDlg::applyTalkerCodeToControls()
{
    bool preferred = false;
    TQString code = m_talkerCode.getTalkerCode();

    // TODO: Need to display translated Synth names.
    KttsUtils::setCbItemFromText(m_widget->synthComboBox,
        TalkerCode::stripPrefer( m_talkerCode.plugInName(), preferred) );
    m_widget->synthCheckBox->setEnabled( !m_talkerCode.plugInName().isEmpty() );
    m_widget->synthCheckBox->setChecked( preferred );

    KttsUtils::setCbItemFromText(m_widget->genderComboBox,
        TalkerCode::translatedGender( TalkerCode::stripPrefer( m_talkerCode.gender(), preferred ) ) );
    m_widget->genderCheckBox->setEnabled( !m_talkerCode.gender().isEmpty() );
    m_widget->genderCheckBox->setChecked( preferred );

    KttsUtils::setCbItemFromText(m_widget->volumeComboBox,
        TalkerCode::translatedVolume( TalkerCode::stripPrefer(  m_talkerCode.volume(), preferred ) ) );
    m_widget->volumeCheckBox->setEnabled( !m_talkerCode.volume().isEmpty() );
    m_widget->volumeCheckBox->setChecked( preferred );

    KttsUtils::setCbItemFromText(m_widget->rateComboBox,
        TalkerCode::translatedRate( TalkerCode::stripPrefer( m_talkerCode.rate(), preferred ) ) );
    m_widget->rateCheckBox->setEnabled( !m_talkerCode.rate().isEmpty() );
    m_widget->rateCheckBox->setChecked( preferred );

    // Select closest matching specific Talker.
    int talkerIndex = TalkerCode::findClosestMatchingTalker(m_talkers, m_talkerCode.getTalkerCode(), false);
    TDEListView* lv = m_widget->talkersListView;
    TQListViewItem* item = lv->firstChild();
    if ( item )
    {
        while ( talkerIndex > 0 )
        {
            item = item->nextSibling();
            --talkerIndex;
        }
        lv->setSelected( item, true );
    }
}

void SelectTalkerDlg::applyControlsToTalkerCode()
{
    if ( m_widget->useDefaultRadioButton->isChecked() )
        m_talkerCode = TalkerCode(TQString(), false);
    else if ( m_widget->useClosestMatchRadioButton->isChecked() )
    {
        // Language already stored in talker code.

        TQString t = m_widget->synthComboBox->currentText();
        if ( !t.isEmpty() && m_widget->synthCheckBox->isChecked() ) t.prepend("*");
        m_talkerCode.setPlugInName( t );

        t = TalkerCode::untranslatedGender( m_widget->genderComboBox->currentText() );
        if ( !t.isEmpty() && m_widget->genderCheckBox->isChecked() ) t.prepend("*");
        m_talkerCode.setGender( t );

        t = TalkerCode::untranslatedVolume( m_widget->volumeComboBox->currentText() );
        if ( !t.isEmpty() && m_widget->volumeCheckBox->isChecked() ) t.prepend("*");
        m_talkerCode.setVolume( t );

        t = TalkerCode::untranslatedRate( m_widget->rateComboBox->currentText() );
        if ( !t.isEmpty() && m_widget->rateCheckBox->isChecked() ) t.prepend("*");
        m_talkerCode.setRate( t );
    }
    else if (m_widget->useSpecificTalkerRadioButton->isChecked() )
    {
        TQListViewItem* item = m_widget->talkersListView->selectedItem();
        if ( item )
        {
            int itemIndex = -1;
            while ( item )
            {
                item = item->itemAbove();
                itemIndex++;
            }
            m_talkerCode = TalkerCode( &(m_talkers[itemIndex]), false );
        }
    }
}

void SelectTalkerDlg::loadTalkers(bool /*runningTalkers*/)
{
    m_talkers.clear();
    TDEListView* lv = m_widget->talkersListView;
    lv->clear();
    TQListViewItem* item;
    TDEConfig* config = new TDEConfig("kttsdrc");
    config->setGroup("General");
    TQStringList talkerIDsList = config->readListEntry("TalkerIDs", ',');
    if (!talkerIDsList.isEmpty())
    {
        TQStringList::ConstIterator itEnd(talkerIDsList.constEnd());
        for( TQStringList::ConstIterator it = talkerIDsList.constBegin(); it != itEnd; ++it )
        {
            TQString talkerID = *it;
            config->setGroup("Talker_" + talkerID);
            TQString talkerCode = config->readEntry("TalkerCode", TQString());
            // Parse and normalize the talker code.
            TalkerCode talker = TalkerCode(talkerCode, true);
            m_talkers.append(talker);
            TQString desktopEntryName = config->readEntry("DesktopEntryName", TQString());
            TQString synthName = TalkerCode::TalkerDesktopEntryNameToName(desktopEntryName);
            // Display in List View using translated strings.
            item = new TDEListViewItem(lv, item);
            TQString fullLanguageCode = talker.fullLanguageCode();
            TQString language = TalkerCode::languageCodeToLanguage(fullLanguageCode);
            item->setText(tlvcLanguage, language);
            // Don't update the Synthesizer name with plugInName.  The former is a translated
            // name; the latter an English name.
            // if (!plugInName.isEmpty()) talkerItem->setText(tlvcSynthName, plugInName);
            if (!synthName.isEmpty())
                item->setText(tlvcSynthName, synthName);
            if (!talker.voice().isEmpty())
                item->setText(tlvcVoice, talker.voice());
            if (!talker.gender().isEmpty())
                item->setText(tlvcGender, TalkerCode::translatedGender(talker.gender()));
            if (!talker.volume().isEmpty())
                item->setText(tlvcVolume, TalkerCode::translatedVolume(talker.volume()));
            if (!talker.rate().isEmpty())
                item->setText(tlvcRate, TalkerCode::translatedRate(talker.rate()));
        }
    }
    delete config;
}

void SelectTalkerDlg::enableDisableControls()
{
    bool enableClosest = ( m_widget->useClosestMatchRadioButton->isChecked() );
    bool enableSpecific = ( m_widget->useSpecificTalkerRadioButton->isChecked() );
    m_widget->closestMatchGroupBox->setEnabled( enableClosest );
    m_widget->talkersListView->setEnabled( enableSpecific );
}
