
/*
 * pykcm_launcher.cpp
 * 
 * Launch Control Centre modules written in Python using an embedded Python
 * interpreter.
 * Based on David Boddie's PyKDE-components.
 */

// pythonize.h must be included first.
#include <pythonize.h>
#include <kcmodule.h>
#include <kglobal.h>
#include <klocale.h>
#include <klibloader.h>
#include <kstandarddirs.h>
#include <ksimpleconfig.h>
#include <qstring.h>
#include <sip.h>

#define MODULE_DIR "/opt/trinity/share/apps/guidance"
#define EXTRA_MODULE_DIR "/opt/trinity/share/python-support/kde-guidance-trinity"
#define EXTRA_MODULE_DIR_TWO "/opt/trinity/share/python-support/guidance-backends-trinity"
#define EXTRA_MODULE_DIR_THREE "/opt/trinity/share/python-support/kde-guidance-powermanager-trinity"
#define MODULE_NAME "wineconfig"
#define FACTORY "create_wineconfig"
#define CPP_FACTORY create_wineconfig
#define LIB_PYTHON "libpython2.5.so"
#define debug 1

static KCModule *report_error(char *msg) {
    if (debug) printf ("error: %s\n", msg);
    return NULL;
}

static KCModule* return_instance( QWidget *parent, const char *name ) {
    KCModule* kcmodule;
    PyObject *pyKCModuleTuple; 
    PyObject *pyKCModule;
    Pythonize *pyize;  // Pythonize object to manage the Python interpreter.
    int isErr;
    
    // Try to determine what py script we're loading. Note that "name"
    // typically appears to be NULL.
    QString script(MODULE_NAME);

    // Reload libpython, but this time tell the runtime linker to make the
    // symbols global and available for later loaded libraries/module.
    KLibLoader::self()->globalLibrary(LIB_PYTHON);
    
    // Start the interpreter.
    pyize = initialize();
    if (!pyize) {
        return report_error ("***Failed to start interpreter\n");
    }
    
    // Add the path to the python script to the interpreter search path.
    QString path = QString(MODULE_DIR);
    if(path == QString::null) {
        return report_error ("***Failed to locate script path");
    }
    if(!pyize->appendToSysPath (path.latin1 ())) {
        return report_error ("***Failed to set sys.path\n");
    }
    QString extrapath = QString(EXTRA_MODULE_DIR);
    if(!pyize->appendToSysPath (extrapath.latin1 ())) {
        return report_error ("***Failed to set extra sys.path\n");
    }
    QString extrapath_two = QString(EXTRA_MODULE_DIR_TWO);
    if(!pyize->appendToSysPath (extrapath_two.latin1 ())) {
        return report_error ("***Failed to set extra 2 sys.path\n");
    }
    QString extrapath_three = QString(EXTRA_MODULE_DIR_THREE);
    if(!pyize->appendToSysPath (extrapath_three.latin1 ())) {
        return report_error ("***Failed to set extra 3 sys.path\n");
    }
    
    // Load the Python script.
    PyObject *pyModule = pyize->importModule ((char *)script.latin1 ());
    if(!pyModule) {
        PyErr_Print();
        return report_error ("***failed to import module\n");
    }

    // Inject a helper function
    QString bridge = QString("import sip\n" 
                            "import qt\n"
                            "def kcontrol_bridge_" FACTORY "(parent,name):\n"
                             "    if parent!=0:\n"
#if SIP_VERSION >= 0x040200
                             "        wparent = sip.wrapinstance(parent,qt.QWidget)\n"
#else                             
                             "        wparent = sip.wrapinstance(parent,'QWidget')\n"
#endif
                             "    else:\n"
                             "        wparent = None\n"
                             "    inst = " FACTORY "(wparent, name)\n"
                             "    return (inst,sip.unwrapinstance(inst))\n");
    PyRun_String(bridge.latin1(),Py_file_input,PyModule_GetDict(pyModule),PyModule_GetDict(pyModule));

    // Get the Python module's factory function.
    PyObject *kcmFactory = pyize->getNewObjectRef(pyModule, "kcontrol_bridge_" FACTORY);
    if(!kcmFactory) {
        return report_error ("***failed to find module factory\n");
    }
    
    // Call the factory function. Set up the args.
    PyObject *pyParent = PyLong_FromVoidPtr(parent);
    PyObject *pyName = PyString_FromString(MODULE_NAME);
        // Using NN here is effect gives our references to the arguement away.
    PyObject *args = Py_BuildValue ("NN", pyParent, pyName);
    if(pyName && pyParent && args) {
        // run the factory function
        pyKCModuleTuple = pyize->runFunction(kcmFactory, args);
        if(!pyKCModuleTuple) {
            PyErr_Print();
            return report_error ("*** runFunction failure\n;");
        }
    } else {
        return report_error ("***failed to create args\n");
    }
    // cleanup a bit
    pyize->decref(args);
    pyize->decref(kcmFactory);

    // Stop this from getting garbage collected.
    Py_INCREF(PyTuple_GET_ITEM(pyKCModuleTuple,0));
    
    // convert the KCModule PyObject to a real C++ KCModule *.
    isErr = 0;
    pyKCModule = PyTuple_GET_ITEM(pyKCModuleTuple,1);
    kcmodule = (KCModule *)PyLong_AsVoidPtr(pyKCModule);
    if(!kcmodule) {
        return report_error ("***failed sip conversion to C++ pointer\n");
    }
    pyize->decref(pyKCModuleTuple);
    
    // PyKDE can't run the module without this - Pythonize
    // grabs the lock at initialization and we have to give
    // it back before exiting. At this point, we no longer need
    // it.
    //pyize->releaseLock ();

    // take care of any translation info
    KGlobal::locale()->insertCatalogue(script);

    // Return the pointer to our new KCModule
    return kcmodule;
}

extern "C" {
    // Factory function that kcontrol will call.
    KCModule* CPP_FACTORY(QWidget *parent, const char *name) {
        return return_instance(parent, name);
    }
}
