/***************************************************************************
 * pythoninterpreter.cpp
 * This file is part of the KDE project
 * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
 *
 * 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; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 ***************************************************************************/

#include "pythoninterpreter.h"
#include "pythonscript.h"
#include "pythonmodule.h"
#include "pythonsecurity.h"
//#include "pythonextension.h"
#include "../api/variant.h"

#include <tdeglobal.h>
#include <kstandarddirs.h>

#if defined(TQ_WS_WIN)
  #define PYPATHDELIMITER ";"
#else
  #define PYPATHDELIMITER ":"
#endif

extern "C"
{
    /**
     * Exported and loadable function as entry point to use
     * the \a PythonInterpreter.
     * The krosspython library the \a PythonInterpreter is part
     * will be loaded dynamicly at runtime from e.g.
     * \a Kross::Api::Manager::getInterpreter and this exported
     * function will be used to return an instance of the
     * \a PythonInterpreter implementation.
     */
    void* krossinterpreter(Kross::Api::InterpreterInfo* info)
    {
        try {
            return new Kross::Python::PythonInterpreter(info);
        }
        catch(Kross::Api::Exception::Ptr e) {
            Kross::krosswarning("krossinterpreter(Kross::Api::InterpreterInfo* info): Unhandled exception.");
        }
        return 0;
    }
}

using namespace Kross::Python;

namespace Kross { namespace Python {

    /// \internal
    class PythonInterpreterPrivate
    {
        public:

            /// The __main__ python module.
            PythonModule* mainmodule;

            /// The \a PythonSecurity python module to wrap the RestrictedPython functionality.
            PythonSecurity* security;
    };

}}

PythonInterpreter::PythonInterpreter(Kross::Api::InterpreterInfo* info)
    : Kross::Api::Interpreter(info)
    , d(new PythonInterpreterPrivate())
{
    // Initialize the python interpreter.
    initialize();

    // Set name of the program.
    Py_SetProgramName(const_cast<char*>("Kross"));

    /*
    // Set arguments.
    //char* comm[0];
    const char* comm = const_cast<char*>("kross"); // name.
    PySys_SetArgv(1, comm);
    */

    // In the python sys.path are all module-directories are
    // listed in.
    TQString path;

    // First import the sys-module to remember it's sys.path
    // list in our path TQString.
    Py::Module sysmod( PyImport_ImportModule("sys"), true );
    Py::Dict sysmoddict = sysmod.getDict();
    Py::Object syspath = sysmoddict.getItem("path");
    if(syspath.isList()) {
        Py::List syspathlist = syspath;
        for(Py::List::iterator it = syspathlist.begin(); it != syspathlist.end(); ++it)
            if( (*it).isString() )
                path.append( TQString(Py::String(*it).as_string().c_str()) + PYPATHDELIMITER );
    }
    else
        path = Py_GetPath();

    // Determinate additional module-paths we like to add.
    // First add the global Kross modules-path.
    TQStringList krossdirs = TDEGlobal::dirs()->findDirs("data", "kross/python");
    for(TQStringList::Iterator krossit = krossdirs.begin(); krossit != krossdirs.end(); ++krossit)
        path.append(*krossit + PYPATHDELIMITER);
    // Then add the application modules-path.
    TQStringList appdirs = TDEGlobal::dirs()->findDirs("appdata", "kross/python");
    for(TQStringList::Iterator appit = appdirs.begin(); appit != appdirs.end(); ++appit)
        path.append(*appit + PYPATHDELIMITER);

    // Set the extended sys.path.
    PySys_SetPath( (char*) path.latin1() );

    krossdebug(TQString("Python ProgramName: %1").arg(Py_GetProgramName()));
    krossdebug(TQString("Python ProgramFullPath: %1").arg(Py_GetProgramFullPath()));
    krossdebug(TQString("Python Version: %1").arg(Py_GetVersion()));
    krossdebug(TQString("Python Platform: %1").arg(Py_GetPlatform()));
    krossdebug(TQString("Python Prefix: %1").arg(Py_GetPrefix()));
    krossdebug(TQString("Python ExecPrefix: %1").arg(Py_GetExecPrefix()));
    krossdebug(TQString("Python Path: %1").arg(Py_GetPath()));
    krossdebug(TQString("Python System Path: %1").arg(path));

    // Initialize the main module.
    d->mainmodule = new PythonModule(this);

    // The main dictonary.
    Py::Dict moduledict = d->mainmodule->getDict();
    //TODO moduledict["KrossPythonVersion"] = Py::Int(KROSS_PYTHON_VERSION);

    // Prepare the interpreter.
    TQString s =
        "import sys\n"
        //"sys.setdefaultencoding('latin-1')\n"

        // Dirty hack to get sys.argv defined. Needed for e.g. TKinter.
        "sys.argv = ['']\n"

        // On the try to read something from stdin always return an empty
        // string. That way such reads don't block our script.
        "import cStringIO\n"
        "sys.stdin = cStringIO.StringIO()\n"

        // Class to redirect something. We use this class e.g. to redirect
        // <stdout> and <stderr> to a c++ event.
        "class Redirect:\n"
        "  def __init__(self, target):\n"
        "    self.target = target\n"
        "  def write(self, s):\n"
        "    self.target.call(s)\n"

        // Wrap builtin __import__ method. All import requests are
        // first redirected to our PythonModule.import method and
        // if the call returns None, then we call the original
        // python import mechanism.
        "import __builtin__\n"
        "import __main__\n"
        "class Importer:\n"
        "    def __init__(self):\n"
        "        self.realImporter = __builtin__.__import__\n"
        "        __builtin__.__import__ = self._import\n"
        "    def _import(self, name, globals=None, locals=None, fromlist=[]):\n"
        "        mod = __main__._import(name, globals, locals, fromlist)\n"
        "        if mod != None: return mod\n"
        "        return self.realImporter(name, globals, locals, fromlist)\n"
        "Importer()\n"
        ;

    PyObject* pyrun = PyRun_String(s.latin1(), Py_file_input, moduledict.ptr(), moduledict.ptr());
    if(! pyrun) {
        Py::Object errobj = Py::value(Py::Exception()); // get last error
        throw Kross::Api::Exception::Ptr( new Kross::Api::Exception(TQString("Failed to prepare the __main__ module: %1").arg(errobj.as_string().c_str())) );
    }
    Py_XDECREF(pyrun); // free the reference.

    // Initialize the RestrictedPython module.
    d->security = new PythonSecurity(this);
}

PythonInterpreter::~PythonInterpreter()
{
    // Free the zope security module.
    delete d->security; d->security = 0;
    // Free the main module.
    delete d->mainmodule; d->mainmodule = 0;
    // Finalize the python interpreter.
    finalize();
    // Delete the private d-pointer.
    delete d;
}

void PythonInterpreter::initialize()
{
    // Initialize python.
    Py_Initialize();

    /* Not needed cause we use the >= Python 2.3 GIL-mechanism.
    PyThreadState* d->globalthreadstate, d->threadstate;
    // First we have to initialize threading if python supports it.
    PyEval_InitThreads();
    // The main thread. We don't use it later.
    d->globalthreadstate = PyThreadState_Swap(NULL);
    d->globalthreadstate = PyEval_SaveThread();
    // We use an own sub-interpreter for each thread.
    d->threadstate = Py_NewInterpreter();
    // Note that this application has multiple threads.
    // It maintains a separate interp (sub-interpreter) for each thread.
    PyThreadState_Swap(d->threadstate);
    // Work done, release the lock.
    PyEval_ReleaseLock();
    */
}

void PythonInterpreter::finalize()
{
    /* Not needed cause we use the >= Python 2.3 GIL-mechanism.
    // Lock threads.
    PyEval_AcquireLock();
    // Free the used thread.
    PyEval_ReleaseThread(d->threadstate);
    // Set back to rememberd main thread.
    PyThreadState_Swap(d->globalthreadstate);
    // Work done, unlock.
    PyEval_ReleaseLock();
    */

    // Finalize python.
    Py_Finalize();
}

Kross::Api::Script* PythonInterpreter::createScript(Kross::Api::ScriptContainer* scriptcontainer)
{
    return new PythonScript(this, scriptcontainer);
}

PythonModule* PythonInterpreter::mainModule()
{
    return d->mainmodule;
}

PythonSecurity* PythonInterpreter::securityModule()
{
    return d->security;
}

