/***************************************************************************
 *   Copyright (C) 2005-2006 by Andreas Silberstorff   *
 *   ml@andreas-silberstorff.de   *
 *                                                                         *
 *   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 Steet, Fifth Floor, Boston, MA 02110-1301, USA.             *
 *                                                                         *
 *   In addition, as a special exception, the copyright holders give       *
 *   permission to link the code of this program with any edition of       *
 *   the Qt library by Trolltech AS, Norway (or with modified versions     *
 *   of Qt that use the same license as Qt), and distribute linked         *
 *   combinations including the two.  You must obey the GNU General        *
 *   Public License in all respects for all of the code used other than    *
 *   Qt.  If you modify this file, you may extend this exception to        *
 *   your version of the file, but you are not obligated to do so.  If     *
 *   you do not wish to do so, delete this exception statement from        *
 *   your version.                                                         *
 ***************************************************************************/

#include "kalva.h"
#include "kalvasidebar.h"
#include "settings.h"
#include "prefs.h"
#include "kalva_channellist.h"
#include "kalva_player.h"
#include "serialbrowser.h"
#include "kalvaprofile.h"
#include "kalvaprofilechoosercombo.h"
//#include <QtGui/QPrinter>
//#include <QtGui/QPrintDialog>

#include <qdragobject.h>
#include <qpainter.h>
#include <qpaintdevicemetrics.h>
#include <qstring.h>
#include <qlineedit.h>
#include <qtimer.h>
#include <qiconset.h>
#include <qptrlist.h>
#include <qregexp.h>

#include <kiconloader.h>
#include <klocale.h>
#include <kprocess.h>
#include <kmessagebox.h>
#include <kconfigdialog.h>
#include <kglobal.h>
#include <klocale.h>
#include <kiconloader.h>
#include <kdeversion.h>
#include <kmenubar.h>
#include <kstatusbar.h>
#include <kkeydialog.h>
#include <kedittoolbar.h>
#include <kstdaccel.h>
#include <kaction.h>
#include <kstdaction.h>
#include <kdebug.h>
#include <kstandarddirs.h>
#include <ktip.h>
#include <kmultitabbar.h>

#include <kparts/componentfactory.h>
#include "kalvaview.h"
#include "systray.h"
#include <kchlstfilterplugin.h>

#include <kprocess.h>
#include <kinputdialog.h>
#include <kapplication.h>
#include <kmessagebox.h>

#include <kdeversion.h>
#undef KDE_3_3_FEATURES
#if defined(KDE_MAKE_VERSION)
#if KDE_VERSION >= KDE_MAKE_VERSION(3,3,0)
	#define KDE_3_3_FEATURES
#endif
#endif

Kalva::Kalva()
    : KMainWindow(0, "Kalva", WDestructiveClose),
      m_view(new KalvaView(this)),
      m_printer(0)
{
    m_proc = 0;

    setAcceptDrops(true);
    setCentralWidget(m_view);
    setMinimumSize( QSize(  (m_view->width() + 10), (m_view->height() + 20) ) );

    setupActions();

    setupGUI();

    setupProfileChoosers();

    //createGUI(QString::null,false);
    setupPlugins();

    setAutoSaveSettings();

    readConfig();
    setupSystemTray();

    if (m_startDocked)
       hide();
    else
       tipOfTheDay();
    m_shuttingDown  = false;
}

Kalva::~Kalva()
{
}

void Kalva::setupActions()
{
    (void)new KAction(
           i18n("&Save"), 0,
           m_view->channellist(),    SLOT(slotSaveChannellist()),
           actionCollection(),       "save_channellist");
    (void)new KAction(
           i18n("Start &EPG"), 0,
           this,                    SLOT(slotStartEPG()),
           actionCollection(),       "find");
     KStdAction::quit(
            this,                     SLOT(slotQuit()),
            actionCollection());
     KStdAction::preferences(
            this,                     SLOT(optionsPreferences()),
            actionCollection());
    m_toggleDockOnCloseAction =	new KToggleAction(
           i18n("&Stay in System Tray on Close"),
	   0,
           actionCollection(),
           "dockOnClose");
    m_toggleSystemTrayAction = new KToggleAction(
           i18n("&Dock in System Tray"),
           0,
           actionCollection(),
           "toggleSystemTray");
    KStdAction::tipOfDay(
           this,                     SLOT( slotTipOfTheDay() ),
           actionCollection()
           )->setWhatsThis(
              i18n("This shows useful tips on the use of this application.")
           );

    connect (
           m_toggleSystemTrayAction, SIGNAL(toggled(bool)),
   	   this,                     SLOT(slotToggleSystemTray(bool)));


    createStandardStatusBarAction();
    setStandardToolBarMenuEnabled( true );

     // allow the view to change the statusbar and caption
//     connect(m_view, SIGNAL(signalChangeStatusbar(const QString&)),
//             this,   SLOT(changeStatusbar(const QString&)));
//     connect(m_view, SIGNAL(signalChangeCaption(const QString&)),
//             this,   SLOT(changeCaption(const QString&)));
    QTimer::singleShot( 200, this, SLOT(tipOfTheDay()) );
}

void Kalva::setupProfileChoosers()  {
	hwprofilechooser = new KalvaProfileChooserCombo(
		 toolBar(), "hwprofile", KALVA_HARDWARE_PROFILE );
	hwprofilechooser->read();
	connect(
		hwprofilechooser,
		SIGNAL( highlighted( const QString& ) ),
		m_view->player(),
		SLOT( settingsChanged( const QString& ) ) );
	qprofilechooser = new KalvaProfileChooserCombo(
		 toolBar(), "qprofile", KALVA_QUALITY_PROFILE );
	qprofilechooser->read();
	connect(
		qprofilechooser,
		SIGNAL( highlighted( const QString& ) ),
		m_view->player(),
		SLOT( settingsChanged( const QString& ) ) );
	chlstchooser = new KalvaProfileChooserCombo(
		 toolBar(), "chlst", KALVA_CHANNELLIST );
	chlstchooser->read();
	connect(
		chlstchooser,
		SIGNAL( highlighted( const QString& ) ),
		this,
		SLOT( slotSetChlst( const QString& ) ) );
	toolBar()->insertWidget( 0, 70, hwprofilechooser );
	toolBar()->insertWidget( 1, 70, qprofilechooser );
	toolBar()->insertWidget( 2, 70, chlstchooser );
}

void Kalva::slotSetChlst( const QString & profile )  {
	kdDebug() << "in slotSetChlst()" << endl;
	Settings::setChannellist( profile );
	m_view->channellist()->loadChannellist();
	m_view->player()->settingsChanged(profile);
}

void Kalva::slotStartEPG()
{
    kdDebug() << "in slotStartEPG()" << endl;

    QString cmd = Settings::epg();
    if ( cmd.isEmpty() )  {
       return;
    }
    cmd.append( " " );

    QString opts  = Settings::editorOptions();
    if ( opts.isEmpty() )
       cmd.append( opts );

    kdDebug() << "cmd is not empty " << endl;

    if ( m_proc )
       delete m_proc;

    kdDebug() << "m_proc does not exist " << endl;
    m_proc = 0;

    if ( Settings::show_command() == true  ) {
#ifdef KDE_3_3_FEATURES
       bool ok;
       cmd = KInputDialog::getMultiLineText (
          i18n("Command to run"), 
          i18n("Command"), 
          cmd, 
          &ok,
          this,
          "cmdDlg"
       );
       if ( ok != true )  {
          return;
       }
#else
       int ret = KMessageBox::warningContinueCancel (
          this,
          cmd, 
          i18n("Command to run")
       );
       if ( ret != KMessageBox::Continue ) {
          return;
       }
#endif
    }
    m_proc = new KProcess;
    m_proc->setUseShell(true);
    QApplication::connect(
        m_proc, SIGNAL( processExited(KProcess *)),
        this,   SLOT( slotMplayerExited(KProcess *)));
    (*m_proc) << cmd;
    m_proc->start( KProcess::NotifyOnExit, KProcess::Stdin );
    changeStatusbar( "EPG called" );
}

void Kalva::setupPlugins()
{
    // Sets up the plugin interface, and load the plugins
    m_pluginInterface = new QObject( this, "m_pluginInterface" );
    new MyChannellistInterface( m_view,
                                m_pluginInterface,
                                "channellist interface" );

    QString uirc = locate("appdata", "kalvaui.rc");
    kdDebug() << "kalvaui.rc = " << uirc << endl;
    loadPlugins();
}

void Kalva::tipOfTheDay()
{
   KTipDialog::showTip( this );
}

void Kalva::slotTipOfTheDay()
{
   KTipDialog::showTip( this, QString::null,  true );
}

void Kalva::optionsPreferences()
{
	// The preference dialog is derived from prefs-base.ui
        // which is subclassed into Prefs
	//
	// compare the names of the widgets in the .ui file 
	// to the names of the variables in the .kcfg file
        KConfigDialog *dialog = new KConfigDialog(
              this, "settings", Settings::self(), KDialogBase::Swallow);
        dialog->addPage(new Prefs(), i18n("General"), "package_settings");

        connect(dialog, SIGNAL(settingsChanged()),
                m_view, SLOT(settingsChanged()));
        connect(dialog, SIGNAL(settingsChanged()),
                this, SLOT(slotSettingsChanged()));
        dialog->show();
}

void Kalva::slotSettingsChanged()  {
	hwprofilechooser->setCurrentText(
              Settings::hwprofile().remove(QRegExp("^.*/") ) );
	qprofilechooser->setCurrentText(
              Settings::qprofile().remove(QRegExp("^.*/") ) );
	chlstchooser->setCurrentText(
              Settings::channellist().remove(QRegExp("^.*/") ));
}

void Kalva::changeStatusbar(const QString& text)
{
    // display the text on the statusbar
    statusBar()->message(text, 2000);
}

void Kalva::changeCaption(const QString& text)
{
    // display the text on the caption
    setCaption(text);
}

void Kalva::loadPlugins()
{
    kdDebug() << "=========================================" << endl;
    kdDebug() << "in loadPlugins()" << endl;
    kdDebug() << "=========================================" << endl;

    KalvaChannellist* channellist = m_view->channellist();
    KTrader::OfferList offers =  KTrader::self()->query("KChlstFilterPlugin/Plugin");

//    guiFactory()->addClient(this);
//    kdDebug() << "added self to guiGFactory" << endl;

    KTrader::OfferList::ConstIterator iter;
    for(iter = offers.begin(); iter != offers.end(); ++iter) {
        KService::Ptr service = *iter;
        int errCode = 0;

        KChlstFilterPlugin::Plugin* plugin =
            KParts::ComponentFactory::createInstanceFromService<KChlstFilterPlugin::Plugin>
            ( service, m_pluginInterface, 0, QStringList(), &errCode);
        debugPluginInstanciationErrNo( errCode );

        if ( plugin ) {
            kdDebug() << "plugin successfully instanciated" << endl;

            guiFactory()->addClient( plugin );

            kdDebug() << "addClient( plugin ) success" << endl;

            connect(plugin,      SIGNAL(chlstImported(const QStringList& )),
                      channellist, SLOT(slotImport(const QStringList& )));

            kdDebug() << QString("Loaded plugin %1").arg( plugin->name() ) << endl;
        }

    }
    changeStatusbar( "Plugins loaded" );
}


void Kalva::reloadPlugins()
{
    kdDebug() << "=========================================" << endl;
    kdDebug() << "in reloadPlugins()" << endl;
    kdDebug() << "=========================================" << endl;

    QPtrList<KXMLGUIClient> clients = guiFactory()->clients();
    for( QPtrListIterator<KXMLGUIClient> it( clients );
            it.current(); ++it )
    {
        guiFactory()->removeClient( *it );
    }
    guiFactory()->addClient( this );

    KalvaChannellist* channellist = m_view->channellist();
    KTrader::OfferList offers =  KTrader::self()->query("KChlstFilterPlugin/Plugin");

    KTrader::OfferList::ConstIterator iter;
    for(iter = offers.begin(); iter != offers.end(); ++iter) {
        KService::Ptr service = *iter;
        int errCode = 0;

        KChlstFilterPlugin::Plugin* plugin =
            KParts::ComponentFactory::createInstanceFromService<KChlstFilterPlugin::Plugin>
            ( service, m_pluginInterface, 0, QStringList(), &errCode);
        debugPluginInstanciationErrNo( errCode );

        if ( plugin ) {
            kdDebug() << "plugin successfully instanciated" << endl;

            guiFactory()->addClient( plugin );

            kdDebug() << "addClient( plugin ) success" << endl;

            connect(plugin,      SIGNAL(chlstImported(const QStringList& )),
                      channellist, SLOT(slotImport(const QStringList& )));

            kdDebug() << QString("Loaded plugin %1").arg( plugin->name() ) << endl;
        }

    }
    changeStatusbar( "Plugins loaded" );
}


void Kalva::debugPluginInstanciationErrNo ( const int & ErrNo )
{
   switch (ErrNo)
   {
     case 0:
         return;
     case 1:
         kdDebug() << "ErrNoServiceFound - no service implementing the given mimetype and fullfilling the given constraint expression can be found." << endl; 
     case 2:
         kdDebug() << "ErrServiceProvidesNoLibrary - the specified service provides no shared library." << endl; 
     case 3:
         kdDebug() << "ErrNoLibrary - the specified library could not be loaded. Use KLibLoader::lastErrorMessage for details." << endl; 
         //kdDebug() << KLibLoader::lastErrorMessage() << endl; 
     case 4:
         kdDebug() << "ErrNoFactory - the library does not export a factory for creating components." << endl; 
     case 5:
         kdDebug() << "ErrNoComponent" << endl;
   }
}

void Kalva::readConfig()
{
    KConfigGroup kalvaConfig(KGlobal::config(), "Kalva");
    m_startDocked = kalvaConfig.readBoolEntry("StartDocked", false);
    m_toggleSystemTrayAction->setChecked(
           kalvaConfig.readBoolEntry("DockInSystemTray", true));
    m_toggleDockOnCloseAction->setChecked(
           kalvaConfig.readBoolEntry("DockOnClose", true));

    m_view->sidebar()->readConfig( kalvaConfig );
    QString lastStation = kalvaConfig.readEntry("lastStation");

    kdDebug() << "read last station as "
                      << lastStation << endl;

    m_view->channellist()->setStation( lastStation );
    restoreWindowSize(KGlobal::config());
}

void Kalva::saveConfig()
{
    KConfigGroup kalvaConfig(KGlobal::config(), "Kalva");
    kalvaConfig.writeEntry( "StartDocked",
                            m_startDocked);
    kalvaConfig.writeEntry( "DockInSystemTray",
                            m_toggleSystemTrayAction->isChecked());
    kalvaConfig.writeEntry( "DockOnClose",
                            m_toggleDockOnCloseAction->isChecked());
    saveWindowSize(KGlobal::config());

    m_view->sidebar()->setConfig( kalvaConfig );

    KGlobal::config()->sync();
}

void Kalva::slotConfigureKeys()
{
    KKeyDialog dlg( false, this );
    QPtrList<KXMLGUIClient> clients = guiFactory()->clients();
    for( QPtrListIterator<KXMLGUIClient> it( clients );
            it.current(); ++it )
    {
        dlg.insert( (*it)->actionCollection() );
    }
    dlg.configure();
}

void Kalva::slotConfigureToolbars()
{
    kdDebug() << "in slotConfigureToolbars()" << endl;

    saveMainWindowSettings(KGlobal::config(), autoSaveGroup());

    KEditToolbar dlg( guiFactory() );
    connect(&dlg, SIGNAL(newToolbarConfig()),
            this, SLOT(saveNewToolbarConfig()));
    dlg.exec();
}

void Kalva::saveNewToolbarConfig()
{
    kdDebug() << "in saveNewToolbarConfig()" << endl;
    applyMainWindowSettings( KGlobal::config(), autoSaveGroup() );

    //createGUI(QString::null,false);
    //reloadPlugins();

    KMessageBox::detailedSorry   (
       this,
       i18n("The changes will not show up untill you restart Kalva."), 
       i18n("<qt>If you are an experienced kde coder I want to invite you to take a close look at Kalva's sources. Please through me a line if you know how to provide immediate rebuilding the toolbar without loosing the actions load via plugins...</qt>"), 
       i18n("Changes will only take effect on restart.")
       );
}

bool Kalva::queryExit()
{
    m_startDocked = !isVisible();
    hide();

    delete m_systemTray;
    m_systemTray = 0;

    saveConfig();
    return true;
}

bool Kalva::queryClose()
{
    if(!m_shuttingDown &&
       !kapp->sessionSaving() &&
       m_systemTray &&
       m_toggleDockOnCloseAction->isChecked())
    {
	KMessageBox::information(this,
	    i18n("<qt>Closing the main window will keep Kalva running in the system tray. "
		 "Use Quit from the File menu to quit the application.</qt>"),
	    i18n("Docking in System Tray"), "hideOnCloseInfo");
	hide();
	return false;
    }
    else
	return true;
}


void Kalva::setupSystemTray()
{
   if(m_toggleSystemTrayAction && m_toggleSystemTrayAction->isChecked()) {
      m_systemTray = new SystemTray(this, "systemTray");
      m_systemTray->show();
      m_toggleDockOnCloseAction->setEnabled(true);
      connect(m_systemTray, SIGNAL(quitSelected()), this, SLOT(slotAboutToQuit()));
   }
   else {
      m_systemTray = 0;
      m_toggleDockOnCloseAction->setEnabled(false);
   }
}

void Kalva::slotToggleSystemTray(bool enabled)
{
    if(enabled && !m_systemTray)
	setupSystemTray();
   else if(!enabled && m_systemTray) {
      delete m_systemTray;
      m_systemTray = 0;
      m_toggleDockOnCloseAction->setEnabled(false);
   }
}

void Kalva::slotAboutToQuit()
{
    m_shuttingDown = true;
}

void Kalva::slotQuit()
{
    m_shuttingDown = true;
    kapp->quit();
}

#include "kalva.moc"

