/*
    This file is part of kenvy24 KDE multimedia utility
    Copyright (c) 2007  Valentin RUSU kenvy24@rusu.info

    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) version 3 or, at the discretion of KDE e.V. (which shall
    act as a proxy as in section 14 of the GPLv3), 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, see <http://www.gnu.org/licenses/>.
*/

#include "mainpart.h"
#include "mainpartadaptor.h"
#include "ui_mainpart.h"
#include "partnames.h"
#include "envycardfactory.h"
#include "envycard.h"
#include "kenvy24partbase.h"

#include <QTimer>
#include <QComboBox>
#include <kdemacros.h>
#include <kparts/genericfactory.h>
#include <kparts/factory.h>
#include <kparts/part.h>
#include <kdebug.h>
#include <kmessagebox.h>
#include <kapplication.h>

#include "loadpart.h"


#define CONFIG_LAST_SELECTED_CARD   "lastSelectedCard"
#define CONFIG_CURRENT_CONFIG       "currentConfig"
#define CONFIG_CONFIG_NAMES         "configNames"
#define CONFIG_DONT_ASK_DELETE      "dontAskDeleteConfig"

K_PLUGIN_FACTORY(MainPartFactory, registerPlugin<MainPart>();)
K_EXPORT_PLUGIN(MainPartFactory( KENVY24_MAIN_PART ) )

MainPart::MainPart( QWidget* parentWidget, QObject *parent, const QVariantList& )
    : KEnvy24Part< Ui_MainPart >(parentWidget, parent, KENVY24_MAIN_PART),
    currentCard(0),
    settingsConfigGroup(0)
{
    setComponentData( MainPartFactory::componentData() );

    QTimer::singleShot( 10, this, SLOT( initTimerShot() ) );

    connect( uiBuilder->configSave, SIGNAL(clicked()), SLOT( saveConfig() ) );
    connect( uiBuilder->configDelete, SIGNAL(clicked()), SLOT( deleteConfig()));
}

/**
 * Do the main initialization of this part, wich will construct all the application
 */
void MainPart::initTimerShot()
{
    // load the contained parts
#if LOAD_PART_SURROUND > 0
    surroundPart = static_cast<KEnvy24PartBase*>( loadPart( uiBuilder->tabMixer, *manager(), KENVY24_SURROUND_PART ) );
    uiBuilder->surroundLayout->addWidget( surroundPart->widget() );
    connect( this, SIGNAL( saveConfigNow( KConfigGroup* ) ), surroundPart, SLOT( saveConfig( KConfigGroup* ) ) );
    connect( this, SIGNAL( loadConfigNow( KConfigGroup* ) ), surroundPart, SLOT( loadConfig( KConfigGroup* ) ) );
#else
    uiBuilder->tabWidget->removeTab(1);
#endif // LOAD_PART_SURROUND

    mixerPart = static_cast<KEnvy24PartBase*>( loadPart( uiBuilder->tabMixer, *manager(), KENVY24_MIXER_PART ) );
    uiBuilder->mixerLayout->addWidget( mixerPart->widget() );
    connect( this, SIGNAL( saveConfigNow( KConfigGroup* ) ), mixerPart, SLOT( saveConfig( KConfigGroup* ) ) );
    connect( this, SIGNAL( loadConfigNow( KConfigGroup* ) ), mixerPart, SLOT( loadConfig( KConfigGroup* ) ) );

    // restore configuration names
    restoreConfig();

    // enumerate ICE1712 cards and feed-in the selection combo-box
    EnvyCardFactory::CardList cardList = EnvyCardFactory::searchTheCards();
    if ( cardList.isEmpty() )
    {
        kDebug() << "Cannot find a Envy24 chip based sound card in your system. Will now quit !";
        KMessageBox::error( widget(), i18n("Cannot find an Envy24 chip based sound card in your system. Will now quit.") );
        QTimer::singleShot( 10, KApplication::kApplication(), SLOT(quit()) );
    }
    else
    {

        foreach ( EnvyCard* card,  cardList )
        {
            QVariant cardData = QVariant::fromValue(card);
            uiBuilder->cardName->addItem( card->getCardLongName(), cardData );
        }
        if ( uiBuilder->cardName->count() <= 1)
        {
            uiBuilder->cardName->setEnabled(false);
        }

        // connect this signal only now, to prevent spurious "cardSelected" events
        connect( uiBuilder->cardName, SIGNAL(currentIndexChanged(const QString&)), SLOT(cardChanged(const QString&)));

        // read last selected card from settings file and select it
        QString lastSelectedCard = partConfigGroup->readEntry( CONFIG_LAST_SELECTED_CARD );
        if (!lastSelectedCard.isEmpty())
        {
            kDebug() << "selecting last session card " << lastSelectedCard;
            uiBuilder->cardName->setCurrentIndex( uiBuilder->cardName->findText( lastSelectedCard ));
            cardChanged( lastSelectedCard );
        }
        else
        {
            uiBuilder->cardName->setCurrentIndex(0);
            cardChanged( uiBuilder->cardName->currentText() );
        }
    }

    // register with DBUS
    // TODO DBUS integration
    new MainPartAdaptor( this );
    QDBusConnection::sessionBus().registerObject( QLatin1String("/MainPart"), this );


    // connect this combo only now to avoid spurious signals emmitted during initialization, as some will affect the
    // configration management
    connect( uiBuilder->configList, SIGNAL(currentIndexChanged(const QString&)), SLOT( configSelectionChanged(const QString&)));
    connect( uiBuilder->configList, SIGNAL(editTextChanged(const QString&)), SLOT( configNameChanged(const QString&) ) );
}

MainPart::~MainPart()
{
    saveConfig();
}

bool MainPart::saveFile ()
{
    // nothing
    return true;
}

bool MainPart::openFile ()
{
    // nothing
    return true;
}

void MainPart::cardChanged(const QString& longName)
{
    kDebug() << longName;
    EnvyCard* theCard = uiBuilder->cardName->itemData( uiBuilder->cardName->findText(longName)).value< EnvyCard* >();
    if ( theCard )
        setActiveCard( theCard );
    else
    {
        // FIXME find out how this case appears sometimes during app startup
        kDebug() << "OOPS : card not found in the combobox ! Selecting first card";
        theCard = uiBuilder->cardName->itemData(0).value< EnvyCard* >();
        if (theCard)
            setActiveCard( theCard );
    }
}

void MainPart::selectCard(QString)
{
}

void MainPart::setActiveCard( EnvyCard* theCard )
{
    saveConfig();
    currentCard = theCard;

    // IMPORTANT :
    // Please be aware that ICE1712 cards has several ways to route
    // the signal from inputs to the outputs. But only one of these
    // routes is useful : the one that goes through the Digital Mix
    //
    // KEnvy24 is designed to work when the route goes through the
    // Digital Mix. This route can be set using envy24control tab 
    // 'patchbay/router'. The following line of code switches current
    // card in this mode. Please note that this is done only when a 
    // card is selected.
    currentCard->routeThroughMixer();

    emit cardSelected( theCard->getCardName() );
    mixerPart->setActiveCard( theCard );

    emit loadConfigNow( configGroup( uiBuilder->configList->currentText() ) );

    // save last selected into the settings file
    partConfigGroup->writeEntry( CONFIG_LAST_SELECTED_CARD, theCard->getCardLongName() );
    partConfigGroup->sync();
}

/**
 * this slot is called when the user type a setting name into the edit part
 * of the combo box
 */
void MainPart::configNameChanged(const QString& configName)
{
    // nothing to do (for instance)
}

/**
 * this slot is called when user selects a configuration into the combo box
 */
void MainPart::configSelectionChanged(const QString& configName)
{
    emit loadConfigNow( configGroup( configName ) );

    partConfigGroup->writeEntry( CONFIG_CURRENT_CONFIG, configName );
    partConfigGroup->sync();
}

void MainPart::restoreConfig()
{
    // read configuration names and feed the combo
    QString configNames = partConfigGroup->readEntry( CONFIG_CONFIG_NAMES );
    if (!configNames.isEmpty())
    {
        QStringList configs = configNames.split( ",", QString::SkipEmptyParts );
        QStringListIterator configIter( configs );
        while ( configIter.hasNext() )
            uiBuilder->configList->insertItem( uiBuilder->configList->count()+1, configIter.next() );
    }
    else
    {
        uiBuilder->configList->insertItem( 0, i18n("Default configuration") );
    }
    QString lastConfigName = partConfigGroup->readEntry( CONFIG_CURRENT_CONFIG );
    if (!lastConfigName.isEmpty())
    {
        uiBuilder->configList->setCurrentIndex( uiBuilder->configList->findText( lastConfigName ) );
/*
        kDebug() << "Loading last configuration " << lastConfigName;
        emit loadConfigNow( configGroup( lastConfigName ) );*/
    }
}

void MainPart::saveConfig()
{
    if ( currentCard != 0 )
    {
        // save last selected config into the settings file
        QString configName = uiBuilder->configList->currentText();
        kDebug() << "saveConfig " << configName;
        if ( configName.isEmpty() )
        {
            KMessageBox::error( widget(), i18n("Please select a configuration before trying to save it.") );
        }
        else
        {
            partConfigGroup->writeEntry( CONFIG_CURRENT_CONFIG, configName );

            // append this config name into this list if it does not exist or change
            // it to reflect the new case user entered
            int index = uiBuilder->configList->findText( configName, Qt::MatchFixedString );
            if ( -1 == index )
                uiBuilder->configList->insertItem( uiBuilder->configList->count()+1, configName );
            else
                uiBuilder->configList->setItemText( index, configName );

            // write config names (list contents) to our main config section
            saveConfigNames();

            // create config group and ask all parts to save settings there
            // NOTE a part must take care not to use config value names already used by
            // other parts
            KConfigGroup* config = configGroup( configName );
            emit saveConfigNow( config );
            config->sync();

            // finally, save settings to disk
            partConfigGroup->sync();
        }
    }
}

void MainPart::saveConfigNames()
{
    QString configNames;
    for (int i=0; i< uiBuilder->configList->count(); i++)
    {
        if ( i >0 )
            configNames.append( ',' );
        configNames.append( uiBuilder->configList->itemText( i ) );
    }
    partConfigGroup->writeEntry( CONFIG_CONFIG_NAMES, configNames );
    partConfigGroup->sync();
}

void MainPart::deleteConfig()
{
    QString configName = uiBuilder->configList->currentText();
    kDebug() << "deleteConfig " << configName;
    if ( configName.isEmpty() )
    {
        KMessageBox::error( widget(), i18n("Please select a configuration before trying to delete it.") );
    }
    else
    {
        QString question = QString(i18n("Really delete configuration '%1'?")).arg( configName );
        if ( KMessageBox::questionYesNo( ui, 
                    question, QString(), KStandardGuiItem::yes(), KStandardGuiItem::no(), CONFIG_DONT_ASK_DELETE )
                == KMessageBox::Yes ) 
        {
            configGroup( configName )->deleteGroup();
            // FIXME why the heck the combo does not find the text ?
            int index = uiBuilder->configList->findText( configName, Qt::MatchFixedString );
            if ( -1 == index )
                uiBuilder->configList->removeItem( index );
            else
                kDebug() << "config name '" << configName << "' not found in the combo";
    
            saveConfigNames();
        }
    }
}

/**
 * configurations are stored per card. This is handled by using selected card name
 * when building configuration section name
 */
KConfigGroup* MainPart::configGroup( QString configName )
{
    kDebug() << configName;
    if ( !currentCard )
        kError( "Internal error : no card is selected !" );
    QString configGroupName = QString("%1_config_%2_%3").
        arg( KENVY24_MAIN_PART ).
        arg( currentCard->getCardLongName() ).
        arg( configName );
    return new KConfigGroup( KGlobal::config(), configGroupName );
}

void MainPart::saveConfig( KConfigGroup* )
{
    // nothing to do
}

void MainPart::loadConfig( KConfigGroup* )
{
    // nothing to do
}

#include "mainpart.moc"
// kate: space-indent on; indent-width 4; replace-tabs on;
