/**************************************************************************
                          tderadioapp.cpp  -  description
                             -------------------
    begin                : Sa Feb  9 CET 2002
    copyright            : (C) 2002 by Klas Kalass / Martin Witte / Frank Schwanz
    email                : klas.kalass@gmx.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.                                   *
 *                                                                         *
 ***************************************************************************/

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


#include <tdeaboutdata.h>
#include <tdelocale.h>
#include <klibloader.h>
#include <tdeconfig.h>
#include <tdemessagebox.h>
#include <kstandarddirs.h>

// #include <kprogress.h>

#include "include/tderadioapp.h"
#include "include/aboutwidget.h"
#include "include/errorlog-interfaces.h"

#include "include/debug-profiler.h"

/////////////////////////////////////////////////////////////////////////////
//// TDERadioAbout

AboutPageInfo  TDERadioAbout::createAboutPage ()
{
    const char *description = I18N_NOOP(
        "TDERadio - The Radio Application for TDE"
        "<P>"
        "With TDERadio you can listen to radio broadcasts with the help of your "
        "V4L/V4L2 compatible radio card."
        "<P>"
        "The TDERadio Project contains a station preset data database. To complete "
        "this database you are encouraged to contribute your station preset file "
        "to the project. Just send it to one of the authors. "
        "<P>"
        "If you like to contribute your ideas, your own plugins or translations, "
        "don't hesitate to contact one of the authors."
        "<P>"
    );

    TDEAboutData aboutData("tderadio", "TDERadio",
                         VERSION,
                         description,
                         TDEAboutData::License_GPL,
                         "(c) 2002-2006 Martin Witte, Klas Kalass",
                         0,
                         "http://sourceforge.net/projects/tderadio",
                         0);
    aboutData.addAuthor("Martin Witte",  I18N_NOOP("Preset Database, Remote Control Support, Alarms, Rewrite for TDERadio 0.3.0, Misc"), "witte@kawo1.rwth-aachen.de");
    aboutData.addAuthor("Marcus Camen",  I18N_NOOP("Buildsystem, Standards Conformance, Cleanups"), "mcamen@mcamen.de");
    aboutData.addAuthor("Klas Kalass",   I18N_NOOP("Miscellaneous"), "klas.kalass@gmx.de");
    aboutData.addAuthor("Frank Schwanz", I18N_NOOP("idea, first basic application"), "schwanz@fh-brandenburg.de");

    aboutData.addCredit(I18N_NOOP("Many People around the World ... "),
                        I18N_NOOP("... which contributed station preset files \n"
                                  "and tested early and unstable snapshots of TDERadio \n"
                                  "with much patience"));

    return AboutPageInfo(
              new TDERadioAboutWidget(aboutData, TDERadioAboutWidget::AbtAppStandard),
              "TDERadio",
              "TDERadio",
              "tderadio"
           );
}


/////////////////////////////////////////////////////////////////////////////
//// PluginLibraryInfo

PluginLibraryInfo::PluginLibraryInfo (const TQString &lib_name)
 : library (NULL),
   init_func(NULL),
   info_func(NULL),
   libload_func(NULL),
   libunload_func(NULL)
{
    library = KLibLoader::self()->library(lib_name.ascii());
    if (library) {
        info_func      = (t_tderadio_plugin_info_func)     library->symbol("TDERadioPlugin_GetAvailablePlugins");
        init_func      = (t_tderadio_plugin_init_func)     library->symbol("TDERadioPlugin_CreatePlugin");
        libload_func   = (t_tderadio_plugin_libload_func)  library->symbol("TDERadioPlugin_LoadLibrary");
        libunload_func = (t_tderadio_plugin_libunload_func)library->symbol("TDERadioPlugin_UnloadLibrary");
        if (info_func && init_func && libload_func && libunload_func) {
            libload_func();
            info_func(plugins);
        } else {
            KMessageBox::error(NULL,
                               i18n("Library %1: Plugin Entry Point is missing\n")
                                .arg(lib_name),
                               i18n("Plugin Library Load Error"));
            library->unload();
            info_func = NULL;
            init_func = NULL;
            library   = NULL;
        }
    } else {
            KMessageBox::error(NULL,
                               i18n("Library %1: \n%2")
                                .arg(lib_name)
                                .arg(KLibLoader::self()->lastErrorMessage()),
                               i18n("Plugin Library Load Error"));
    }
}


/////////////////////////////////////////////////////////////////////////////
//// TDERadioApp

TDERadioApp::TDERadioApp()
  : TDEApplication(),
    m_quitting(false)
{
    m_Instances.setAutoDelete(true);
    connect(this, TQ_SIGNAL(aboutToQuit()), this, TQ_SLOT(slotAboutToQuit()));
}


TDERadioApp::~TDERadioApp()
{
    IErrorLogClient::staticLogDebug("TDERadioApp::~TDERadioApp()");
}

void TDERadioApp::saveState()
{
    IErrorLogClient::staticLogDebug(i18n("saveState"));
    saveState(TDEGlobal::config());
}

void TDERadioApp::saveState (TDEConfig *c)
{
    c->setGroup("Global");
    c->writeEntry("instances", m_Instances.count());

    int i = 0;
    TQDictIterator<PluginManager> it(m_Instances);
    for (; it.current(); ++it, ++i) {
        c->setGroup("Global");
        c->writeEntry("instance_name_" + TQString::number(i), it.currentKey());
        it.current()->saveState(c);
    }

    c->setGroup("Plugin Libraries");
    c->writeEntry("count", m_PluginLibraries.count());
    int idx = 0;
    TQMapConstIterator<TQString, PluginLibraryInfo> end = m_PluginLibraries.end();
    for (TQMapConstIterator<TQString, PluginLibraryInfo> it = m_PluginLibraries.begin(); it != end; ++it, ++idx) {
        c->writeEntry("library_" + TQString::number(idx), it.key());
    }

    c->sync();
}


void TDERadioApp::restoreState (TDEConfig *c)
{
    BlockProfiler profiler("TDERadioApp::restoreState - loadLibraries");

    c->setGroup("Plugin Libraries");
    int n_libs = c->readNumEntry("count", 0);

//     KProgressDialog  *progress = new KProgressDialog(NULL, NULL, i18n("Loading Plugin Libraries"));
//     progress->setMinimumWidth(400);
//     progress->setAllowCancel(false);
//     progress->TQWidget::setCaption(i18n("TDERadio - Loading Plugin Libraries"));
//     progress->show();

/*    progress->progressBar()->setTotalSteps(n_libs);*/
    for (int idx = 0; idx < n_libs; ++idx) {
        TQString lib = c->readEntry("library_" + TQString::number(idx), TQString());
        if (lib.length()) {
            LoadLibrary(lib);
//             progress->progressBar()->setProgress(idx+1);
        }
    }

    if (n_libs < 6) {    // this seems to be a meaningful minimum value for a working tderadio setup
        TQStringList libs
            = TDEGlobal::dirs()->findAllResources("lib", "tderadio/plugins/*.so");
        TQValueListIterator<TQString> end = libs.end();
        int idx = 0;
//         progress->progressBar()->setTotalSteps(libs.count());
        for (TQValueListIterator<TQString> it = libs.begin(); it != end; ++it, ++idx) {
            LoadLibrary(*it);
//             progress->progressBar()->setProgress(idx+1);
        }
    }

//     delete progress;

    profiler.stop();

    c->setGroup("Global");

    BlockProfiler rest_profiler("TDERadioApp::restoreState - restore");

    int n = c->readNumEntry("instances", 1);
    if (n < 1 || n > 10)
        n = 1;

    for (int i = 0; i < n; ++i) {
        c->setGroup("Global");
        TQString name = c->readEntry("instance_name_" + TQString::number(i),
                                    n > 1 ? (i18n("Instance") + " " + TQString::number(i+1)) : TQString(""));
        createNewInstance(name)->restoreState(c);
    }
}


PluginManager *TDERadioApp::createNewInstance(const TQString &_name)
{
    BlockProfiler profiler("TDERadioApp::createNewInstance");

    TQString instance_name = _name;
    TQString title_ext = "";
    TQString id = TQString::number(m_Instances.count()+1);
    if (instance_name.length() == 0) {
        instance_name = "Instance " + id;
    }
    if (_name.length() && m_Instances.count() > 0) {
        title_ext = " " + instance_name;
    }
    PluginManager *pm = new PluginManager ( instance_name,
                                            this,
                                            i18n("TDERadio Configuration")    + title_ext,
                                            i18n("About TDERadio Components") + title_ext
                                          );

    m_Instances.insert(instance_name, pm);

    /* Until we don't have library plugins we must instantiate them hard-wired */
    TDERadioAbout      *about       = new TDERadioAbout     (      "tderadio-about-" + instance_name);
    pm->insertPlugin(about);

    return pm;
}


KLibrary *TDERadioApp::LoadLibrary (const TQString &library)
{
    BlockProfiler profiler("TDERadioApp::LoadLibrary");
    BlockProfiler libprofiler("TDERadioApp::LoadLibrary - " + library);

    PluginLibraryInfo libinfo(library);
    if (libinfo.valid()) {
        m_PluginLibraries.insert(library, libinfo);
        TQMapConstIterator<TQString,TQString> end = libinfo.plugins.end();
        for (TQMapConstIterator<TQString,TQString> it = libinfo.plugins.begin(); it != end; ++it) {
            m_PluginInfos.insert(it.key(), PluginClassInfo (it.key(), *it, libinfo.init_func));
        }
    } else {
        kdDebug() << TQDateTime::currentDateTime().toString(TQt::ISODate)
                  << " "
                  << i18n("Error: Loading Library %1 failed: %2")
                     .arg(library).arg(KLibLoader::self()->lastErrorMessage())
                  << endl;
    }

    for (TQDictIterator<PluginManager> it_managers(m_Instances); it_managers.current(); ++it_managers) {
        it_managers.current()->noticeLibrariesChanged();
    }

    return libinfo.valid() ? libinfo.library : NULL;
}


void TDERadioApp::UnloadLibrary (const TQString &library)
{
    if (!m_PluginLibraries.contains(library))
        return;

    PluginLibraryInfo info = m_PluginLibraries[library];

    TQMapConstIterator<TQString, TQString> end_classes = info.plugins.end();
    for (TQMapConstIterator<TQString, TQString> it_classes = info.plugins.begin(); it_classes != end_classes; ++it_classes) {
        for (TQDictIterator<PluginManager> it_managers(m_Instances); it_managers.current(); ++it_managers) {
            it_managers.current()->unloadPlugins(it_classes.key());
        }
        m_PluginInfos.remove(it_classes.key());
    }
    m_PluginLibraries.remove(library);

    info.libunload_func();
    info.library->unload();

    for (TQDictIterator<PluginManager> it_managers(m_Instances); it_managers.current(); ++it_managers) {
        it_managers.current()->noticeLibrariesChanged();
    }
}


PluginBase *TDERadioApp::CreatePlugin (PluginManager *manager, const TQString &class_name, const TQString &object_name)
{
    BlockProfiler all_profiler  ("TDERadioApp::CreatePlugin");
    BlockProfiler class_profiler("TDERadioApp::CreatePlugin - " + class_name);

    BlockProfiler create_profiler("TDERadioApp::CreatePlugin - create");

    PluginBase *retval = NULL;
    if (m_PluginInfos.contains(class_name)) {
        retval = m_PluginInfos[class_name].CreateInstance(object_name);
        if (!retval) {
            kdDebug() << TQDateTime::currentDateTime().toString(TQt::ISODate)
                    << " "
                    << i18n("Error: Creation of instance \"%1\" of class %2 falied.").arg(object_name).arg(class_name)
                    << endl;
        }
    } else {
        kdDebug() << TQDateTime::currentDateTime().toString(TQt::ISODate)
                  << " "
                  << i18n("Error: Cannot create instance \"%1\" of unknown class %2.").arg(object_name).arg(class_name)
                  << endl;
    }

    create_profiler.stop();

    if (retval) {

        BlockProfiler insert_profiler("TDERadioApp::CreatePlugin - insert");
        manager->insertPlugin(retval);
        insert_profiler.stop();

        //BlockProfiler restore_profiler("TDERadioApp::CreatePlugin - restore");
        //retval->restoreState(TDEGlobal::config());
    }

    return retval;
}

void  TDERadioApp::startPlugins()
{
    TQDictIterator<PluginManager> it(m_Instances);
    for (; it.current(); ++it) {
        it.current()->startPlugins();
    }
}

void  TDERadioApp::slotAboutToQuit()
{
    IErrorLogClient::staticLogDebug("slotAboutToQuit");
    if (!m_quitting) {
        IErrorLogClient::staticLogDebug("slotAboutToQuit, m_quitting = false");
        m_quitting = true;
        saveState();
        TQDictIterator<PluginManager> it(m_Instances);
        for (; it.current(); ++it) {
            it.current()->aboutToQuit();
        }
        m_quitting = false;
    }
}

#include "tderadioapp.moc"
