/*    
    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 "mainviewimpl.h"
#include "envycard.h"
#include "monitorimpl.h"
#include "volumeimpl.h"

#include <qtimer.h>
#include <qlabel.h>
#include <qcombobox.h>
#include <kmessagebox.h>
#include <kapplication.h>
#include <kinputdialog.h>
#include <kpushbutton.h>
#include <ksharedconfig.h>
#include <qdial.h>
#include <krun.h>

#include <algorithm>

#define CONFIG_MAIN_GROUP "main-settings"
#define CONFIG_PROFILE_PREFIX "profile-"
#define CONFIG_LAST_PROFILE "lastProfile"

MainViewImpl::MainViewImpl( QWidget* parent ) : 
    QWidget( parent ), envyCard(NULL),
    patchAnalog( MAX_OUTPUT_CHANNELS ),
    patchSPDIF( MAX_SPDIF_CHANNELS ),
    currentProfileItem(NULL)
{
  
/*  envyCard = new EnvyCard();
  if ( ! envyCard->foundEnvyCard() ) {
      KMessageBox::error( this, "Cannot find a Envy24 chip based sound card in your system. Will now quit !" );
      QTimer::singleShot( 10, KApplication::kApplication(), SLOT(quit()) );
      return;
  }*/
  
  setupUi( this );
  
  monitorInAnalog1->setCaption( "Analog 1");
  monitorInSPDIF1->setCaption("SPDIF 1");
  
  monitorPCM1->setCaption("1");
  monitorPCM2->setCaption("2");
  monitorPCM3->setCaption("3");
  monitorPCM4->setCaption("4");
  
  monitorMixer->setCaption("Mixer");
  monitorMixer->hideMuteButtons();
  
  volumeInAnalog1Left->setCaption("L");
  volumeInAnalog1Left->setIndexAndChannel(0, LEFT);
  volumeInAnalog1Left->setMute( envyCard->getMixerAnalogInMuteState( 0, LEFT ) );
  volumeInAnalog1Left->setAdjustment( envyCard->getMixerCaptureAdjustement( 0, LEFT ) );

  volumeInAnalog1Right->setCaption("R");
  volumeInAnalog1Right->setIndexAndChannel(0, RIGHT);
  volumeInAnalog1Right->setMute( envyCard->getMixerAnalogInMuteState( 0, RIGHT ) );
  volumeInAnalog1Right->setAdjustment( envyCard->getMixerCaptureAdjustement( 0, RIGHT ));
  
  volumeInSPDIF1Left->setCaption("L");
  volumeInSPDIF1Left->setIndexAndChannel(0, LEFT);
  volumeInSPDIF1Left->setMute( envyCard->getMixerSPDIFMuteState( 0, LEFT ));
  volumeInSPDIF1Left->setAdjustment( envyCard->getMixerCaptureAdjustement( 0, LEFT ));
  
  volumeInSPDIF1Right->setCaption("R");
  volumeInSPDIF1Right->setIndexAndChannel(0, RIGHT);
  volumeInSPDIF1Right->setMute(envyCard->getMixerSPDIFMuteState( 0, RIGHT));
  volumeInSPDIF1Right->setAdjustment( envyCard->getMixerCaptureAdjustement( 0, RIGHT ));
  
  volumePCM1Left->setCaption("L");
  volumePCM1Left->setIndexAndChannel(0, LEFT);
  volumePCM1Left->setMute( envyCard->getMixerPCMMuteState( 0, LEFT));
  volumePCM1Left->setAdjustment( envyCard->getMixerPlaybackAdjustement( 0, LEFT ));
  
  volumePCM1Right->setCaption("R");
  volumePCM1Right->setIndexAndChannel(0, RIGHT);
  volumePCM1Right->setMute( envyCard->getMixerPCMMuteState( 0, RIGHT));
  volumePCM1Right->setAdjustment( envyCard->getMixerPlaybackAdjustement( 0, RIGHT ));
  
  volumePCM2Left->setCaption("L");
  volumePCM2Left->setIndexAndChannel(1, LEFT);
  volumePCM2Left->setMute(envyCard->getMixerPCMMuteState( 1, LEFT));
  volumePCM2Left->setAdjustment( envyCard->getMixerPlaybackAdjustement( 1, LEFT ));
  
  volumePCM2Right->setCaption("R");
  volumePCM2Right->setIndexAndChannel(1, RIGHT);
  volumePCM2Right->setMute(envyCard->getMixerPCMMuteState( 1, RIGHT));
  volumePCM2Right->setAdjustment( envyCard->getMixerPlaybackAdjustement( 1, RIGHT ));
  
  volumePCM3Left->setCaption("L");
  volumePCM3Left->setIndexAndChannel(2, LEFT);
  volumePCM3Left->setMute(envyCard->getMixerPCMMuteState( 2, LEFT));
  volumePCM3Left->setAdjustment( envyCard->getMixerPlaybackAdjustement( 2, LEFT ));
  
  volumePCM3Right->setCaption("R");
  volumePCM3Right->setIndexAndChannel(2, RIGHT);
  volumePCM3Right->setMute(envyCard->getMixerPCMMuteState( 2, RIGHT));
  volumePCM3Right->setAdjustment( envyCard->getMixerPlaybackAdjustement( 2, RIGHT ));
  
  volumePCM4Left->setCaption("L");
  volumePCM4Left->setIndexAndChannel(3, LEFT);
  volumePCM4Left->setMute(envyCard->getMixerPCMMuteState( 3, LEFT));
  volumePCM4Left->setAdjustment( envyCard->getMixerPlaybackAdjustement( 3, LEFT ));
  
  volumePCM4Right->setCaption("R");
  volumePCM4Right->setIndexAndChannel(3, RIGHT);
  volumePCM4Right->setMute(envyCard->getMixerPCMMuteState( 3, RIGHT));
  volumePCM4Right->setAdjustment( envyCard->getMixerPlaybackAdjustement( 3, RIGHT ));
  
  StereoLevels dacLevels = envyCard->getDACVolume();
  dac0Volume->setValue( dacLevels.left );
  dac1Volume->setValue( dacLevels.right );
  
  StereoLevels adcLevels = envyCard->getADCVolume();
  adc0Volume->setValue( adcLevels.left );
  adc1Volume->setValue( adcLevels.right );
  
  connect( volumeInAnalog1Left, SIGNAL( muted(int, LeftRight, bool) ), envyCard, SLOT( mixerMuteCaptureChannel( int, LeftRight, bool ) ) );
  connect( volumeInAnalog1Right, SIGNAL( muted(int, LeftRight, bool) ), envyCard,  SLOT( mixerMuteCaptureChannel( int, LeftRight, bool ) ) );
  connect( volumeInSPDIF1Left, SIGNAL( muted(int, LeftRight, bool) ), envyCard, SLOT( mixerMuteSPDIFChannel( int, LeftRight, bool ) ) );
  connect( volumeInSPDIF1Right, SIGNAL( muted(int, LeftRight, bool) ), envyCard,  SLOT( mixerMuteSPDIFChannel( int, LeftRight, bool ) ) );
  connect( volumePCM1Left, SIGNAL( muted(int, LeftRight, bool) ), envyCard, SLOT( mixerMutePlaybackChannel( int, LeftRight, bool ) ) );
  connect( volumePCM1Right, SIGNAL( muted(int, LeftRight, bool) ), envyCard,  SLOT( mixerMutePlaybackChannel( int, LeftRight, bool ) ) );
  connect( volumePCM2Left, SIGNAL( muted(int, LeftRight, bool) ), envyCard, SLOT( mixerMutePlaybackChannel( int, LeftRight, bool ) ) );
  connect( volumePCM2Right, SIGNAL( muted(int, LeftRight, bool) ), envyCard,  SLOT( mixerMutePlaybackChannel( int, LeftRight, bool ) ) );
  connect( volumePCM3Left, SIGNAL( muted(int, LeftRight, bool) ), envyCard, SLOT( mixerMutePlaybackChannel( int, LeftRight, bool ) ) );
  connect( volumePCM3Right, SIGNAL( muted(int, LeftRight, bool) ), envyCard,  SLOT( mixerMutePlaybackChannel( int, LeftRight, bool ) ) );
  connect( volumePCM4Left, SIGNAL( muted(int, LeftRight, bool) ), envyCard, SLOT( mixerMutePlaybackChannel( int, LeftRight, bool ) ) );
  connect( volumePCM4Right, SIGNAL( muted(int, LeftRight, bool) ), envyCard,  SLOT( mixerMutePlaybackChannel( int, LeftRight, bool ) ) );
  
  connect( volumeInAnalog1Left, SIGNAL( adjusted( int , LeftRight , int , int  ) ), envyCard, SLOT( setMixerCaptureVolume( int, LeftRight, int, int ) ) );
  connect( volumeInAnalog1Right, SIGNAL( adjusted( int , LeftRight , int , int  ) ), envyCard,  SLOT( setMixerCaptureVolume( int, LeftRight, int, int ) ) );
  connect( volumeInSPDIF1Left, SIGNAL( adjusted( int , LeftRight , int , int  ) ), envyCard, SLOT( setMixerSPDIFVolume( int, LeftRight, int , int  ) ) );
  connect( volumeInSPDIF1Right, SIGNAL( adjusted( int , LeftRight , int , int  ) ), envyCard,  SLOT( setMixerSPDIFVolume( int, LeftRight, int , int  ) ) );
  connect( volumePCM1Left, SIGNAL( adjusted( int , LeftRight , int , int  ) ), envyCard, SLOT( setMixerPlaybackVolume( int, LeftRight, int, int ) ) );
  connect( volumePCM1Right, SIGNAL( adjusted( int , LeftRight , int , int  ) ), envyCard,  SLOT( setMixerPlaybackVolume( int, LeftRight, int, int ) ) );
  connect( volumePCM2Left, SIGNAL( adjusted( int , LeftRight , int , int  ) ), envyCard, SLOT( setMixerPlaybackVolume( int, LeftRight, int, int ) ) );
  connect( volumePCM2Right, SIGNAL( adjusted( int , LeftRight , int , int  ) ), envyCard,  SLOT( setMixerPlaybackVolume( int, LeftRight, int, int ) ) );
  connect( volumePCM3Left, SIGNAL( adjusted( int , LeftRight , int , int  ) ), envyCard, SLOT( setMixerPlaybackVolume( int, LeftRight, int, int ) ) );
  connect( volumePCM3Right, SIGNAL( adjusted( int , LeftRight , int , int  ) ), envyCard,  SLOT( setMixerPlaybackVolume( int, LeftRight, int, int ) ) );
  connect( volumePCM4Left, SIGNAL( adjusted( int , LeftRight , int , int  ) ), envyCard, SLOT( setMixerPlaybackVolume( int, LeftRight, int, int ) ) );
  connect( volumePCM4Right, SIGNAL( adjusted( int , LeftRight , int , int  ) ), envyCard,  SLOT( setMixerPlaybackVolume( int, LeftRight, int, int ) ) );
  
  setupTimer();
  setupPatchbay();
  
  // react to ALSA driver events in order to reflect settings on our GUI controls
  connect( envyCard, SIGNAL( patchBayUpdated() ), SLOT( patchBayUpdated() ) );
  
  connect(envyCard, SIGNAL( mixerUpdatePCMMuteSwitch( int,  LeftRight, bool ) ), volumePCM1Left, SLOT( mixerUpdateMuteSwitch( int,  LeftRight, bool ) ) );
  connect(envyCard, SIGNAL( mixerUpdatePCMMuteSwitch( int,  LeftRight, bool ) ), volumePCM1Right, SLOT( mixerUpdateMuteSwitch( int,  LeftRight, bool ) ) );
  connect(envyCard, SIGNAL( mixerUpdatePCMMuteSwitch( int,  LeftRight, bool ) ), volumePCM2Left, SLOT( mixerUpdateMuteSwitch( int,  LeftRight, bool ) ) );
  connect(envyCard, SIGNAL( mixerUpdatePCMMuteSwitch( int,  LeftRight, bool ) ), volumePCM2Right, SLOT( mixerUpdateMuteSwitch( int,  LeftRight, bool ) ) );
  connect(envyCard, SIGNAL( mixerUpdatePCMMuteSwitch( int,  LeftRight, bool ) ), volumePCM3Left, SLOT( mixerUpdateMuteSwitch( int,  LeftRight, bool ) ) );
  connect(envyCard, SIGNAL( mixerUpdatePCMMuteSwitch( int,  LeftRight, bool ) ), volumePCM3Right, SLOT( mixerUpdateMuteSwitch( int,  LeftRight, bool ) ) );
  connect(envyCard, SIGNAL( mixerUpdatePCMMuteSwitch( int,  LeftRight, bool ) ), volumePCM4Left, SLOT( mixerUpdateMuteSwitch( int,  LeftRight, bool ) ) );
  connect(envyCard, SIGNAL( mixerUpdatePCMMuteSwitch( int,  LeftRight, bool ) ), volumePCM4Right, SLOT( mixerUpdateMuteSwitch( int,  LeftRight, bool ) ) );
  
  connect(envyCard, SIGNAL( mixerUpdateAnalogInMuteSwitch( int,  LeftRight, bool ) ), volumeInAnalog1Left, SLOT( mixerUpdateMuteSwitch( int,  LeftRight, bool ) ) );
  connect(envyCard, SIGNAL( mixerUpdateAnalogInMuteSwitch( int,  LeftRight, bool ) ), volumeInAnalog1Right, SLOT( mixerUpdateMuteSwitch( int,  LeftRight, bool ) ) );
  
  connect(envyCard, SIGNAL( mixerUpdateSPDIFInMuteSwitch( int,  LeftRight, bool ) ), volumeInSPDIF1Left, SLOT( mixerUpdateMuteSwitch( int,  LeftRight, bool ) ) );
  connect(envyCard, SIGNAL( mixerUpdateSPDIFInMuteSwitch( int,  LeftRight, bool ) ), volumeInSPDIF1Right, SLOT( mixerUpdateMuteSwitch( int,  LeftRight, bool ) ) );
  
  connect(envyCard, SIGNAL( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ), volumePCM1Left, SLOT( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ) );
  connect(envyCard, SIGNAL( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ), volumePCM1Right, SLOT( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ) );
  connect(envyCard, SIGNAL( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ), volumePCM2Left, SLOT( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ) );
  connect(envyCard, SIGNAL( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ), volumePCM2Right, SLOT( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ) );
  connect(envyCard, SIGNAL( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ), volumePCM3Left, SLOT( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ) );
  connect(envyCard, SIGNAL( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ), volumePCM3Right, SLOT( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ) );
  connect(envyCard, SIGNAL( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ), volumePCM4Left, SLOT( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ) );
  connect(envyCard, SIGNAL( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ), volumePCM4Right, SLOT( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ) );
  
  connect(envyCard, SIGNAL( mixerUpdateAnalogInVolume( int, LeftRight, MixerAdjustement ) ), volumeInAnalog1Left, SLOT( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ) );
  connect(envyCard, SIGNAL( mixerUpdateAnalogInVolume( int, LeftRight, MixerAdjustement ) ), volumeInAnalog1Right, SLOT( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ) );

  connect(envyCard, SIGNAL( mixerUpdateSPDIFVolume( int, LeftRight, MixerAdjustement ) ), volumeInSPDIF1Left, SLOT( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ) );
  connect(envyCard, SIGNAL( mixerUpdateSPDIFVolume( int, LeftRight, MixerAdjustement ) ), volumeInSPDIF1Right, SLOT( mixerUpdatePlaybackVolume( int, LeftRight, MixerAdjustement ) ) );
  
  // load profiles
  KSharedConfigPtr config = KGlobal::config();
  QStringList groupList = config->groupList();
  for ( QStringList::iterator it = groupList.begin(); it != groupList.end(); it++ ) {
      QString group = *it;
      if ( group.startsWith( CONFIG_PROFILE_PREFIX ) ) {
          // this is a saved profile, so read it
          profileList->insertItem( group.remove( CONFIG_PROFILE_PREFIX ) );
      }
  }
  
  KConfigGroup mainGroup( config, CONFIG_MAIN_GROUP );
  QString lastProfile;
  mainGroup.readEntry( CONFIG_LAST_PROFILE, lastProfile );
  if ( !lastProfile.isEmpty() ) {
      profileList->setSelected( profileList->findItem( lastProfile ), true );
  }
}

MainViewImpl::~MainViewImpl() {
/*
  if ( updateTimer != NULL ) {
    updateTimer->stop();
    delete updateTimer;
  }
*/
  if (envyCard != NULL) {
    delete envyCard;
    envyCard = NULL;
  }
}

void MainViewImpl::updateMeters() {
  
  envyCard->updatePeaks();
  monitorMixer->updateLevel( envyCard->getMixerLevels() );
}

void MainViewImpl::setupTimer() {
    updateTimer = new QTimer(this);
  
    connect( updateTimer, SIGNAL( timeout() ), SLOT( updateMeters() ) );
    monitorMixer->connect( updateTimer, SIGNAL( timeout() ), SLOT ( timerTick() ) );
    monitorPCM1->connect( updateTimer, SIGNAL( timeout() ), SLOT( timerTick() ) );
    monitorPCM2->connect( updateTimer, SIGNAL( timeout() ), SLOT( timerTick() ) );
    monitorPCM3->connect( updateTimer, SIGNAL( timeout() ), SLOT( timerTick() ) );
    monitorPCM4->connect( updateTimer, SIGNAL( timeout() ), SLOT( timerTick() ) );
    monitorInAnalog1->connect( updateTimer, SIGNAL( timeout() ), SLOT( timerTick() ) );
    monitorInSPDIF1->connect( updateTimer, SIGNAL( timeout() ), SLOT( timerTick() ) );
  
    monitorPCM1->setEventIndex( 0 );
    monitorPCM1->connect( envyCard, SIGNAL( pcmLevelUpdated( int, const StereoLevels& ) ), SLOT( levelUpdated( int, const StereoLevels& ) ) );
    monitorPCM2->setEventIndex( 1 );
    monitorPCM2->connect( envyCard, SIGNAL( pcmLevelUpdated( int, const StereoLevels& ) ), SLOT( levelUpdated( int, const StereoLevels& ) ) );
    monitorPCM3->setEventIndex( 2 );
    monitorPCM3->connect( envyCard, SIGNAL( pcmLevelUpdated( int, const StereoLevels& ) ), SLOT( levelUpdated( int, const StereoLevels& ) ) );
    monitorPCM4->setEventIndex( 3 );
    monitorPCM4->connect( envyCard, SIGNAL( pcmLevelUpdated( int, const StereoLevels& ) ), SLOT( levelUpdated( int, const StereoLevels& ) ) );
    
    monitorInAnalog1->setEventIndex( 0 );
    monitorInAnalog1->connect( envyCard, SIGNAL( inputLevelUpdated( int, const StereoLevels& ) ), SLOT( levelUpdated( int, const StereoLevels& ) ) );
    monitorInSPDIF1->setEventIndex( 0 );
    monitorInSPDIF1->connect( envyCard, SIGNAL( spdifInLevelUpdated( int, const StereoLevels& ) ), SLOT( levelUpdated( int, const StereoLevels& ) ) );
  
    updateTimer->start( UPDATE_TIMER_INTERVAL );
}

PatchbayRoute::PatchbayRoute( RouteKind kind, int index, QLabel* label, QComboBox* leftCombo, QComboBox* rightCombo, MainViewImpl* view ) {
    this->kind = kind;
    this->index = index;
    this->label = label;
    this->leftCombo = leftCombo;
    this->rightCombo = rightCombo;
    this->view = view;
    
    if ( view->envyCard->isOutputActive(index) ) {
        label->setEnabled( true );
        leftCombo->setEnabled( true );
        rightCombo->setEnabled( true );
        
        fillCombos();
        patchBayUpdated();
    }
}

void PatchbayRoute::fillCombos() {
    if ( kind == ANALOG ) {
        QStringList strings;
        strings << "PCM 1" 
                <<  "Mixer Out" 
                << "Input S/PDIF 1 L" 
                << "Input S/PDIF 1 R"
                << "Input Analog 1 L"
                << "Input Analog 1 R";
        
        leftCombo->addItems(strings ); 
        rightCombo->addItems(strings );
    }
    else {
        QStringList strings;
        strings 
            << "S/PDIF 1"
            << "Mixer Out"
            << "Input S/PDIF 1 L"
            << "Input S/PDIF 1 R"
            << "Input Analog 1 L"
            << "Input Analog 1 R";
        
        leftCombo->addItems( strings );
        rightCombo->addItems( strings );
    }
    
    connect( leftCombo, SIGNAL( activated(int) ), SLOT( leftComboActivated(int) ) );
    connect( rightCombo, SIGNAL( activated(int) ), SLOT( rightComboActivated(int) ) );
}

void PatchbayRoute::patchBayUpdated() const {
    int leftState;
    if ( kind == ANALOG )
        leftState = view->envyCard->getAnalogRouteState( index, LEFT );
    else
        leftState = view->envyCard->getSPDIFRouteState( index, LEFT );
    
    int rightState;
    if ( kind == ANALOG)
        rightState = view->envyCard->getAnalogRouteState( index, RIGHT );
    else
        rightState = view->envyCard->getSPDIFRouteState( index, RIGHT );
        
    leftCombo->setCurrentIndex( leftState );
    rightCombo->setCurrentIndex( rightState );
}

void PatchbayRoute::leftComboActivated( int listIndex ) {
    view->envyCard->patchBaySetRoute( kind, index, LEFT, (EnvyCard::PatchBayRoutes)listIndex );
}

void PatchbayRoute::rightComboActivated( int listIndex ) {
    view->envyCard->patchBaySetRoute( kind, index, RIGHT, (EnvyCard::PatchBayRoutes)listIndex );
}

void MainViewImpl::setupPatchbay() {
    assert( MAX_OUTPUT_CHANNELS == 4 );
    patchAnalog[ 0 ] = new PatchbayRoute( ANALOG, 0, patchAO1label, patchAO1L, patchAO1R, this );
    patchAnalog[ 1 ] = new PatchbayRoute( ANALOG, 1, patchAO2label, patchAO2L, patchAO2R, this );
    patchAnalog[ 2 ] = new PatchbayRoute( ANALOG, 2, patchAO3label, patchAO3L, patchAO3R, this );
    patchAnalog[ 3 ] = new PatchbayRoute( ANALOG, 3, patchAO4label, patchAO4L, patchAO4R, this );
    
    assert( MAX_SPDIF_CHANNELS == 1 );
    patchSPDIF[ 0 ] = new PatchbayRoute( SPDIF, 0, patchSPDIFO1label, patchSPDIFO1L, patchSPDIFO1R, this );
}

struct CallPatchBayUpdated : public std::unary_function< PatchbayRoute, void > {
    void operator () ( const PatchbayRoute* route ) const { route->patchBayUpdated(); }
};

void MainViewImpl::patchBayUpdated() {
    std::for_each( patchAnalog.begin(), patchAnalog.end(), CallPatchBayUpdated() );
}

void MainViewImpl::newProfile() {
    QString profileName;
    
    for (;;) {
        profileName = KInputDialog::getText( "New profile", "Enter profile name" );
        Q3ListBoxItem* existingItem = profileList->findItem( profileName );
        if ( existingItem != NULL ) {
            QString message = QString("A profile named '%1' already exists. Please enter anoter name or cancel").arg(profileName);
            profileName.truncate(0);
            int action = KMessageBox::warningContinueCancel( this, message );
            if ( action == KMessageBox::Continue )
                continue;
        }
        break;
    }
    
    if ( profileName.length() >0 ) {
        profileList->insertItem( profileName );
        profileList->setSelected( profileList->findItem( profileName ), true );
    }
}

void MainViewImpl::loadProfile(Q3ListBoxItem* item) {
    if ( item ) {
        if ( currentProfileItem != NULL ) {
            // TODO: prompt to save previously selected profile, if changes are detected
        }
        currentProfileItem = item;
        deleteProfileBtn->setEnabled( true );
        updateProfileBtn->setEnabled( true );
        
        // TODO; extract a method containing this loading stuff
        QString profileGroup = getProfileConfigName( currentProfileItem->text() );
        KSharedConfigPtr config = KGlobal::config();
        if ( config->hasGroup( profileGroup ) ) {
            
            KConfigGroup configGroup( config, profileGroup );
            
            volumePCM1Left->loadFromConfig( configGroup );
            volumePCM1Right->loadFromConfig( configGroup );
            volumePCM2Left->loadFromConfig( configGroup );
            volumePCM2Right->loadFromConfig( configGroup );
            volumePCM3Left->loadFromConfig( configGroup );
            volumePCM3Right->loadFromConfig( configGroup );
            volumePCM4Left->loadFromConfig( configGroup );
            volumePCM4Right->loadFromConfig( configGroup );
        
            volumeInAnalog1Left->loadFromConfig( configGroup );
            volumeInAnalog1Right->loadFromConfig( configGroup );
        
            volumeInSPDIF1Left->loadFromConfig( configGroup );
            volumeInSPDIF1Right->loadFromConfig( configGroup );
            
#define LOAD_COMBO_STATE( combo ) \
            { \
                int item = 0; \
                configGroup.readEntry( QString( "%1-item").arg( combo->objectName() ), item ); \
                if ( item != 0 ) \
                    combo->setCurrentIndex( item ); \
            }
            
            LOAD_COMBO_STATE( patchAO1L );
            LOAD_COMBO_STATE( patchAO1R );
            LOAD_COMBO_STATE( patchAO2L );
            LOAD_COMBO_STATE( patchAO2R );
            LOAD_COMBO_STATE( patchAO3L );
            LOAD_COMBO_STATE( patchAO3R );
            LOAD_COMBO_STATE( patchAO4L );
            LOAD_COMBO_STATE( patchAO4R );
            LOAD_COMBO_STATE( patchAO5L );
            LOAD_COMBO_STATE( patchAO5R );
            LOAD_COMBO_STATE( patchAO6L );
            LOAD_COMBO_STATE( patchAO6R );
            LOAD_COMBO_STATE( patchAO7L );
            LOAD_COMBO_STATE( patchAO7R );
            LOAD_COMBO_STATE( patchAO8L );
            LOAD_COMBO_STATE( patchAO8R );
            LOAD_COMBO_STATE( patchSPDIFO1L );
            LOAD_COMBO_STATE( patchSPDIFO1R );
            LOAD_COMBO_STATE( patchSPDIFO2L );
            LOAD_COMBO_STATE( patchSPDIFO2R );
        
#define LOAD_DIAL_STATE( vol ) \
            { \
                int item = -1; \
                configGroup.readEntry( QString( "%1-value").arg( vol->objectName() ), item ); \
                if ( item != -1 ) \
                    vol->setValue( item ); \
            }
            
            LOAD_DIAL_STATE( dac0Volume );
            LOAD_DIAL_STATE( dac1Volume );
            LOAD_DIAL_STATE( adc0Volume );
            LOAD_DIAL_STATE( adc1Volume );
            
            // TODO; load other controls state
        }
        
        KConfigGroup mainGroup( config, CONFIG_MAIN_GROUP );
        mainGroup.writeEntry( CONFIG_LAST_PROFILE, currentProfileItem->text() );
        
        config->sync();
    }
    else {
        deleteProfileBtn->setEnabled( false );
        updateProfileBtn->setEnabled( false );
    }
}

void MainViewImpl::updateProfile() {
    QString profileGroup = getProfileConfigName( currentProfileItem->text() );
    KSharedConfigPtr config = KGlobal::config();
    KConfigGroup configGroup( config, profileGroup );
    
    volumePCM1Left->saveToConfig( configGroup );
    volumePCM1Right->saveToConfig( configGroup );
    volumePCM2Left->saveToConfig( configGroup );
    volumePCM2Right->saveToConfig( configGroup );
    volumePCM3Left->saveToConfig( configGroup );
    volumePCM3Right->saveToConfig( configGroup );
    volumePCM4Left->saveToConfig( configGroup );
    volumePCM4Right->saveToConfig( configGroup );
    
    volumeInAnalog1Left->saveToConfig( configGroup );
    volumeInAnalog1Right->saveToConfig( configGroup );
    
    volumeInSPDIF1Left->saveToConfig( configGroup );
    volumeInSPDIF1Right->saveToConfig( configGroup );
    
#define SAVE_COMBO_STATE( combo ) \
    configGroup.writeEntry( QString( "%1-item" ).arg( combo->objectName() ), combo->currentIndex() );
    
    SAVE_COMBO_STATE( patchAO1L );
    SAVE_COMBO_STATE( patchAO1R );
    SAVE_COMBO_STATE( patchAO2L );
    SAVE_COMBO_STATE( patchAO2R );
    SAVE_COMBO_STATE( patchAO3L );
    SAVE_COMBO_STATE( patchAO3R );
    SAVE_COMBO_STATE( patchAO4L );
    SAVE_COMBO_STATE( patchAO4R );
    SAVE_COMBO_STATE( patchAO5L );
    SAVE_COMBO_STATE( patchAO5R );
    SAVE_COMBO_STATE( patchAO6L );
    SAVE_COMBO_STATE( patchAO6R );
    SAVE_COMBO_STATE( patchAO7L );
    SAVE_COMBO_STATE( patchAO7R );
    SAVE_COMBO_STATE( patchAO8L );
    SAVE_COMBO_STATE( patchAO8R );
    SAVE_COMBO_STATE( patchSPDIFO1L );
    SAVE_COMBO_STATE( patchSPDIFO1R );
    SAVE_COMBO_STATE( patchSPDIFO2L );
    SAVE_COMBO_STATE( patchSPDIFO2R );
    
    configGroup.writeEntry( QString( "%1-value" ).arg( dac0Volume->objectName() ), dac0Volume->value() );
    configGroup.writeEntry( QString( "%1-value" ).arg( dac1Volume->objectName() ), dac1Volume->value() );
    configGroup.writeEntry( QString( "%1-value" ).arg( adc0Volume->objectName() ), adc0Volume->value() );
    configGroup.writeEntry( QString( "%1-value" ).arg( adc1Volume->objectName() ), adc1Volume->value() );
    
    // TODO; save other controls state
    
    // finally, save the profile
    config->sync();
}

void MainViewImpl::deleteProfile() {
    if ( KMessageBox::questionYesNo( this, "Delete selected profile?" ) == KMessageBox::Yes ) {
        if ( currentProfileItem != NULL ) {
            KSharedConfigPtr config = KGlobal::config();
            QString configGroup = getProfileConfigName( currentProfileItem->text() );
            config->deleteGroup( configGroup );
            delete currentProfileItem;
        }
    }
}

QString MainViewImpl::getProfileConfigName( const QString& profileName ) const {
    return QString( CONFIG_PROFILE_PREFIX "%1" ).arg( profileName );
}

void MainViewImpl::dac0Changed(int level) {
    envyCard->setDACVolume( level, LEFT );
}

void MainViewImpl::dac1Changed(int level) {
    envyCard->setDACVolume( level, RIGHT );
}

void MainViewImpl::adc0Changed(int level) {
    envyCard->setADCVolume( level, LEFT );
}

void MainViewImpl::adc1Changed(int level) {
    envyCard->setADCVolume( level, RIGHT );
}

void MainViewImpl::wikiURLClicked(const QString& url) {
    new KRun( KUrl( url ), this );
}

#include "mainviewimpl.moc"
