/*
 * KMix -- KDE's full featured mini mixer
 *
 * Copyright (C) 2000 Stefan Schimanski <schimmi@kde.org>
 * Copyright (C) 2001 Preston Brown <pbrown@kde.org>
 * Copyright (C) 2003 Sven Leiber <s.leiber@web.de>
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library 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.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

// include files for QT
#include <tqmap.h>
#include <tqhbox.h>
#include <tqcheckbox.h>
#include <tqradiobutton.h>
#include <tqpushbutton.h>
#include <tqwidgetstack.h>
#include <tqlayout.h>
#include <tqtooltip.h>

// include files for KDE
#include <kcombobox.h>
#include <kiconloader.h>
#include <tdemessagebox.h>
#include <tdemenubar.h>
#include <klineeditdlg.h>
#include <tdelocale.h>
#include <tdeconfig.h>
#include <tdeaction.h>
#include <tdeapplication.h>
#include <kstdaction.h>
#include <kpanelapplet.h>
#include <tdepopupmenu.h>
#include <khelpmenu.h>
#include <kdebug.h>
#include <tdeaccel.h>
#include <kglobalaccel.h>
#include <kkeydialog.h>
#include <tdeconfigdialog.h>

// application specific includes
#include "mixertoolbox.h"
#include "kmix.h"
#include "kmixerwidget.h"
#include "kmixdockwidget.h"
#include "kmixtoolbox.h"
#include "kmixsettings.h"
#include "behaviorconfig.h"
#include "appearanceconfig.h"

#ifdef WITH_KMIX_EXPERIMENTAL
#include "experimental.h"
#endif


/**
 * Constructs a mixer window (KMix main window)
 */
KMixWindow::KMixWindow()
	: DCOPObject("kmix"), TDEMainWindow(0, 0, 0, 0), m_dockWidget( 0L )
{
	m_visibilityUpdateAllowed	= true;
	m_mixerWidgets.setAutoDelete(true);

#ifdef WITH_KMIX_EXPERIMENTAL
    MixerToolBox::initMixer(Mixer::mixers(), KMixSettings::multiDriver(), m_hwInfoString);
#else
    MixerToolBox::initMixer(Mixer::mixers(), false, m_hwInfoString);
#endif

    loadConfig();
	initActions();
	initWidgets();
	initMixerWidgets();
	updateDocking();

	if ( KMixSettings::visible() )
	{
		show();
	}
	else
	{
        hide();
	}
	connect( kapp, TQ_SIGNAL( aboutToQuit()), TQ_SLOT( saveSettings()) );
}


KMixWindow::~KMixWindow()
{
   MixerToolBox::deinitMixer();
}


void
KMixWindow::initActions()
{
	// file menu
	KStdAction::quit( this, TQ_SLOT(quit()), actionCollection());

	// settings menu
	KStdAction::showMenubar( this, TQ_SLOT(toggleMenuBar()), actionCollection());
        KStdAction::preferences( this, TQ_SLOT(showSettings()), actionCollection());
	new TDEAction( i18n( "Configure &Global Shortcuts..." ), "configure_shortcuts", 0, this,
		TQ_SLOT( configureGlobalShortcuts() ), actionCollection(), "settings_global" );
	KStdAction::keyBindings( guiFactory(), TQ_SLOT(configureShortcuts()), actionCollection());

	(void) new TDEAction( i18n( "Hardware &Information" ), 0, this, TQ_SLOT( slotHWInfo() ), actionCollection(), "hwinfo" );
	(void) new TDEAction( i18n( "Hide Mixer Window" ), Key_Escape, this, TQ_SLOT(hide()), actionCollection(), "hide_kmixwindow" );

	m_globalAccel = new TDEGlobalAccel(this, "KMix");
	m_globalAccel->insert( "Increase volume", i18n( "Increase Volume of Master Channel"), TQString(),
			TDEShortcut(), TDEShortcut(), this, TQ_SLOT( slotIncreaseVolume() ) );
	m_globalAccel->insert( "Decrease volume", i18n( "Decrease Volume of Master Channel"), TQString(),
			TDEShortcut(), TDEShortcut(), this, TQ_SLOT( slotDecreaseVolume() ) );
	m_globalAccel->insert( "Toggle mute", i18n( "Toggle Mute of Master Channel"), TQString(),
			TDEShortcut(), TDEShortcut(), this, TQ_SLOT( slotToggleMuted() ) );
	m_globalAccel->readSettings();
	m_globalAccel->updateConnections();

	createGUI( "kmixui.rc" );
}

void
KMixWindow::initWidgets()
{
	// Main widget and layout
	setCentralWidget( new TQWidget(  this, "qt_central_widget" ) );

	// Widgets layout
	widgetsLayout = new TQVBoxLayout(   centralWidget(), 0, 0, "widgetsLayout" );
	widgetsLayout->setResizeMode(TQLayout::Minimum); // works fine


	// Mixer widget line
	mixerNameLayout = new TQHBox( centralWidget(), "mixerNameLayout" );
	widgetsLayout->setStretchFactor( mixerNameLayout, 0 );
	TQSizePolicy qsp( TQSizePolicy::Ignored, TQSizePolicy::Maximum);
	mixerNameLayout->setSizePolicy(qsp);
	mixerNameLayout->setSpacing(KDialog::spacingHint());
	TQLabel *qlbl = new TQLabel( i18n("Current mixer:"), mixerNameLayout );
	qlbl->setFixedHeight(qlbl->sizeHint().height());
	m_cMixer = new KComboBox( FALSE, mixerNameLayout, "mixerCombo" );
	m_cMixer->setFixedHeight(m_cMixer->sizeHint().height());
	connect( m_cMixer, TQ_SIGNAL( activated( int ) ), this, TQ_SLOT( showSelectedMixer( int ) ) );
	TQToolTip::add( m_cMixer, i18n("Current mixer" ) );

	// Add first layout to widgets
	widgetsLayout->addWidget( mixerNameLayout );

	m_wsMixers = new TQWidgetStack( centralWidget(), "MixerWidgetStack" );
	widgetsLayout->setStretchFactor( m_wsMixers, 10 );
	widgetsLayout->addWidget( m_wsMixers );

	if ( KMixSettings::menubar() )
		menuBar()->show();
	else
		menuBar()->hide();

	widgetsLayout->activate();
}


void
KMixWindow::updateDocking()
{
	// delete old dock widget
	if (m_dockWidget)
	{
		delete m_dockWidget;
		m_dockWidget = 0L;
	}

	if ( KMixSettings::allowDocking() )
	{
		// create dock widget
		m_dockWidget = new KMixDockWidget( Mixer::mixers().first(), this, "mainDockWidget", KMixSettings::trayVolumeControl(), KMixSettings::dockIconMuting() );
		m_dockWidget->show();
	}
}

void
KMixWindow::saveSettings()
{
    saveConfig();
    saveVolumes();
}

void
KMixWindow::saveConfig()
{
   // make sure we don't start without any UI (in case the tray icon is disabled)
   bool startVisible = KMixSettings::visible();
   if ( !KMixSettings::allowDocking() )
   {
       startVisible = true;
   }

   KMixSettings::setSize(size());
   KMixSettings::setPosition(pos());
   KMixSettings::setVisible(startVisible);

   Mixer* mixerMasterCard = Mixer::masterCard();
   if ( mixerMasterCard != 0 )
   {
      KMixSettings::setMasterMixer(mixerMasterCard->id());
   }
   MixDevice* mdMaster = Mixer::masterCardDevice();
   if ( mdMaster != 0 )
   {
       KMixSettings::setMasterMixerDevice(mdMaster->getPK());
   }

   // save mixer widgets
   for ( KMixerWidget *mw = m_mixerWidgets.first(); mw != 0; mw = m_mixerWidgets.next() )
   {
	if ( mw->mixer()->isOpen() )
	{ // protect from unplugged devices (better do *not* save them)
		TQString grp;
		grp.sprintf( "%i", mw->id() );
		mw->saveConfig( TDESharedConfig::openConfig("kmixrc"), grp );
	}
   }

   KMixSettings::writeConfig();
}

void
KMixWindow::loadConfig()
{
   if (!KMixSettings::useDefaultMaster())
   {
       Mixer::setMasterCard(KMixSettings::masterMixer());
       Mixer::setMasterCardDevice(KMixSettings::masterMixerDevice());
   }

   m_toplevelOrientation = static_cast<TQt::Orientation>(KMixSettings::orientation());

   TDEToggleAction *a = static_cast<TDEToggleAction*>(actionCollection()->action("options_show_menubar"));
   if (a) a->setChecked( KMixSettings::menubar() );

   // restore window size and position
   if ( !kapp->isRestored() ) // done by the session manager otherwise
   {
        TQSize size = KMixSettings::size();
        if(!size.isEmpty())
        {
            resize(size);
        }
        move(KMixSettings::position());
    }
}


void
KMixWindow::initMixerWidgets()
{
   m_mixerWidgets.clear();

	int id=0;
	Mixer *mixer;

	// Attention!! If Mixer::mixers() is empty, we behave stupid. We don't show nothing and there
        //             is not even a context menu.
	for ( mixer=Mixer::mixers().first(),id=0; mixer!=0; mixer=Mixer::mixers().next(),id++ )
	{
		//kdDebug(67100) << "Mixer number: " << id << " Name: " << mixer->mixerName() << endl ;
		ViewBase::ViewFlags vflags = ViewBase::HasMenuBar;
		if (KMixSettings::menubar()) {
			vflags |= ViewBase::MenuBarVisible;
		}

#ifdef WITH_KMIX_EXPERIMENTAL
        if (KMixSettings::experimental_ViewSurround()) {
			vflags |= ViewBase::Experimental_SurroundView;
		}
        if (KMixSettings::experimental_ViewGrid()) {
			vflags |= ViewBase::Experimental_GridView;
		}
#endif

		if ( m_toplevelOrientation == TQt::Vertical ) {
			vflags |= ViewBase::Vertical;
		}
        else {
			vflags |= ViewBase::Horizontal;
		}

	
		KMixerWidget *mw = new KMixerWidget( id, mixer, mixer->mixerName(),
						     MixDevice::ALL, this, "KMixerWidget", vflags );
		m_mixerWidgets.append( mw );

		// Add to Combo and Stack
		m_cMixer->insertItem( mixer->mixerName() );
		m_wsMixers->addWidget( mw, id );

		TQString grp;
		grp.sprintf( "%i", mw->id() );
        mw->loadConfig( TDESharedConfig::openConfig("kmixrc"), grp );

		mw->setTicks(KMixSettings::tickmarks());
		mw->setLabels(KMixSettings::labels());
		mw->setValueStyle(KMixSettings::valueStyle());
		// !! I am still not sure whether this works 100% reliably - chris
		mw->show();
	}

	if (id == 1)
	{
		// don't show the Current Mixer bit unless we have multiple mixers
		mixerNameLayout->hide();
	}
}



bool
KMixWindow::queryClose ( )
{
    if ( KMixSettings::allowDocking() && !kapp->sessionSaving() )
    {
        hide();
        return false;
    }
    return true;
}


void
KMixWindow::quit()
{
	kapp->quit();
}


void
KMixWindow::showSettings()
{
   if (TDEConfigDialog::showDialog("KMixConfigDialog"))
   {
       return;
   }

   TDEConfigDialog *cfg = new TDEConfigDialog(this, "KMixConfigDialog", KMixSettings::self());

   KMixBehaviorConfig *cfgb = new KMixBehaviorConfig(0, "Behavior");
   cfg->addPage(cfgb, i18n("Behavior"), "configure");

   KMixAppearanceConfig *cfga = new KMixAppearanceConfig(0, "Appearance");
   cfg->addPage(cfga, i18n("Appearance"), "kmix");

#ifdef WITH_KMIX_EXPERIMENTAL
   KMixExperimental *cfgx = new KMixExperimental(0, "Experimental");
   cfg->addPage(cfgx, i18n("Experimental"), "bug");
#endif

   connect(cfg, TQ_SIGNAL(settingsChanged()), this, TQ_SLOT(applyPrefs()));

   cfg->show();
}

void
KMixWindow::showHelp()
{
	actionCollection()->action( "help_contents" )->activate();
}


void
KMixWindow::showAbout()
{
   actionCollection()->action( "help_about_app" )->activate();
}

/**
 * Loads the volumes of all mixers from kmixctrlrc.
 * In other words:
 * Restores the default voumes as stored via saveVolumes() or the
 * execution of "kmixctrl --save"
 */
/* Currently this is not in use
void
KMixWindow::loadVolumes()
{
	TDEConfig *cfg = new TDEConfig( "kmixctrlrc", true );
	for (Mixer *mixer=Mixer::mixers().first(); mixer!=0; mixer=Mixer::mixers().next())
	{
		mixer->volumeLoad( cfg );
	}
	delete cfg;
}
*/

/**
 * Stores the volumes of all mixers  Can be restored via loadVolumes() or
 * the kmixctrl application.
 */
void
KMixWindow::saveVolumes()
{
    TDEConfig *cfg = new TDEConfig( "kmixctrlrc", false );
    for (Mixer *mixer=Mixer::mixers().first(); mixer!=0; mixer=Mixer::mixers().next()) {
	//kdDebug(67100) << "KMixWindow::saveConfig()" << endl;
	if ( mixer->isOpen() ) { // protect from unplugged devices (better do *not* save them)
		mixer->volumeSave( cfg );
	}
    }
    delete cfg;
}


void
KMixWindow::applyPrefs()
{
   auto old = static_cast<TQt::Orientation>(KMixSettings::orientation());
   if (m_toplevelOrientation != old)
   {
      TQString msg = i18n("The change of orientation will be adopted on the next start of KMix.");
      KMessageBox::information(0, msg);
   }

   this->setUpdatesEnabled(false);
   updateDocking();

   for (KMixerWidget *mw=m_mixerWidgets.first(); mw!=0; mw=m_mixerWidgets.next())
   {
      mw->setTicks( KMixSettings::tickmarks() );
      mw->setLabels( KMixSettings::labels() );
      mw->setValueStyle( KMixSettings::valueStyle() );
      mw->mixer()->readSetFromHWforceUpdate(); // needed, as updateDocking() has reconstructed the DockWidget
   }

   this->setUpdatesEnabled(true);

   this->repaint(); // make KMix look fast (saveConfig() often uses several seconds)
   kapp->processEvents();
   saveConfig();
}


void
KMixWindow::toggleMenuBar()
{
	if( menuBar()->isShown() )
	{
		menuBar()->hide();
	}
   else
	{
		menuBar()->show();
	}
}

void
KMixWindow::showEvent( TQShowEvent * )
{
    if ( m_visibilityUpdateAllowed )
	m_isVisible = isVisible();
    // !! could possibly start polling now (idea: use someting like ref() and unref() on Mixer instance
}

void
KMixWindow::hideEvent( TQHideEvent * )
{
    if ( m_visibilityUpdateAllowed )
    {
	m_isVisible = isVisible();
    }
    // !! could possibly stop polling now (idea: use someting like ref() and unref() on Mixer instance
    //    Update: This is a stupid idea, because now the views are responsible for updating. So it will be done in the Views.
    //    But the dock icon is currently no View, so that must be done first.
}


void
KMixWindow::stopVisibilityUpdates() {
    m_visibilityUpdateAllowed = false;
}

void
KMixWindow::slotHWInfo() {
	KMessageBox::information( 0, m_hwInfoString, i18n("Mixer Hardware Information") );
}

void
KMixWindow::showSelectedMixer( int mixer )
{
	m_wsMixers->raiseWidget( mixer );
}

void
KMixWindow::configureGlobalShortcuts()
{
	KKeyDialog::configure( m_globalAccel, 0, false ) ;
        m_globalAccel->writeSettings();
        m_globalAccel->updateConnections();
}

// KMixIface DCOP interface methods
void KMixWindow::setVolume(int percentage)
{
   Mixer *mixerMaster = Mixer::masterCard();
   if (mixerMaster)
   {
      mixerMaster->setMasterVolume(percentage);
   }
}

void KMixWindow::increaseVolume(int percentage)
{
   Mixer *mixerMaster = Mixer::masterCard();
   if (mixerMaster)
   {
      MixDevice *md = mixerMaster->masterDevice();
      if (md)
      {
         mixerMaster->increaseVolume(md->num(), percentage);
      }
   }
}

void KMixWindow::decreaseVolume(int percentage)
{
   Mixer *mixerMaster = Mixer::masterCard();
   if (mixerMaster)
   {
      MixDevice *md = mixerMaster->masterDevice();
      if (md)
      {
         mixerMaster->decreaseVolume(md->num(), percentage);
      }
   }
}

int KMixWindow::volume()
{
   Mixer *mixerMaster = Mixer::masterCard();
   if (mixerMaster)
   {
      return mixerMaster->masterVolume();
   }
   return -1;
}

void KMixWindow::setAbsoluteVolume(long absoluteVolume)
{
   Mixer *mixerMaster = Mixer::masterCard();
   if (mixerMaster)
   {
      MixDevice *md = mixerMaster->masterDevice();
      if (md)
      {
         return mixerMaster->setAbsoluteVolume(md->num(), absoluteVolume);
      }
   }
}

long KMixWindow::absoluteVolume()
{
   Mixer *mixerMaster = Mixer::masterCard();
   if (mixerMaster)
   {
      MixDevice *md = mixerMaster->masterDevice();
      if (md)
      {
         return mixerMaster->absoluteVolume(md->num());
      }
   }
   return -1L;
}

long KMixWindow::absoluteVolumeMin()
{
   Mixer *mixerMaster = Mixer::masterCard();
   if (mixerMaster)
   {
      MixDevice *md = mixerMaster->masterDevice();
      if (md)
      {
         return mixerMaster->absoluteVolumeMin(md->num());
      }
   }
   return -1L;
}

long KMixWindow::absoluteVolumeMax()
{
   Mixer *mixerMaster = Mixer::masterCard();
   if (mixerMaster)
   {
      MixDevice *md = mixerMaster->masterDevice();
      if (md)
      {
         return mixerMaster->absoluteVolumeMax(md->num());
      }
   }
   return -1L;
}

void KMixWindow::setMute(bool on)
{
   Mixer *mixerMaster = Mixer::masterCard();
   if (mixerMaster)
   {
      mixerMaster->setMasterMute(on);
   }
}

void KMixWindow::toggleMute()
{
   Mixer *mixerMaster = Mixer::masterCard();
   if (mixerMaster)
   {
      mixerMaster->toggleMasterMute();
   }
}

bool KMixWindow::mute()
{
   Mixer *mixerMaster = Mixer::masterCard();
   if (mixerMaster)
   {
      return mixerMaster->masterMute();
   }
   return true;
}

TQString KMixWindow::mixerName()
{
   Mixer *mixerMaster = Mixer::masterCard();
   if (mixerMaster)
   {
      return mixerMaster->mixerName();
   }
   return TQString::null;
}

int KMixWindow::deviceIndex()
{
   Mixer *mixerMaster = Mixer::masterCard();
   if (mixerMaster)
   {
      return mixerMaster->masterDeviceIndex();
   }
   return -1;
}

void KMixWindow::setBalance(int balance)
{
   Mixer *mixerMaster = Mixer::masterCard();
   if (mixerMaster)
   {
      mixerMaster->setBalance(balance);
   }
}

#include "kmix.moc"

