/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

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 3 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, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
//////////////////////////////////////////////////////////////////////
//
// PythonInterface   - The Python interface to the Python dll
//
// NOTE: Eventually, this will be made into a separate dll, because there should
//       only be one instance of this interface. 
//
#include "cyPythonInterface.h"
#include "plPythonPack.h"

#include "compile.h"
#include "marshal.h"
#include "eval.h"

#include "pyEnum.h"

#include "pyKey.h"
#include "cyDraw.h"
#include "cyPhysics.h"
#include "pySceneObject.h"
#include "cyMisc.h"
#include "cyCamera.h"
#include "pyNotify.h"
#include "cyAvatar.h"
#include "pyGeometry3.h"
#include "pyMatrix44.h"
#include "pyColor.h"
#include "pyDynamicText.h"
#include "cyAnimation.h"
#include "pyPlayer.h"
#include "pyImage.h"
#include "pyDniCoordinates.h"
#include "cyInputInterface.h"
#include "pySDL.h"
#include "cyAccountManagement.h"

// GUIDialog and its controls
#include "pyGUIDialog.h"
#include "pyGUIControlButton.h"
#include "pyGUIControlDragBar.h"
#include "pyGUIControlCheckBox.h"
#include "pyGUIControlListBox.h"
#include "pyGUIControlEditBox.h"
#include "pyGUIControlMultiLineEdit.h"
#include "pyGUIControlRadioGroup.h"
#include "pyGUIControlTextBox.h"
#include "pyGUIControlValue.h"
#include "pyGUIControlDynamicText.h"
#include "pyGUIControlClickMap.h"
#include "pyGUIControlDraggable.h"
#include "pyGUIPopUpMenu.h"
#include "pyGUISkin.h"

#include "plPythonSDLModifier.h"

// For printing to the log
#include "plStatusLog/plStatusLog.h"
#include "plNetGameLib/plNetGameLib.h"

// vault
#include "pyVaultNode.h"
#include "pyVaultFolderNode.h"
#include "pyVaultPlayerInfoListNode.h"
#include "pyVaultImageNode.h"
#include "pyVaultTextNoteNode.h"
#include "pyVaultAgeLinkNode.h"
#include "pyVaultChronicleNode.h"
#include "pyVaultPlayerInfoNode.h"
#include "pyVaultAgeInfoNode.h"
#include "pyVaultAgeInfoListNode.h"
#include "pyVaultSDLNode.h"
#include "pyVaultNodeRef.h"
#include "pyVaultMarkerGameNode.h"
#include "pyVaultSystemNode.h"

// player vault
#include "pyVault.h"
// age vault
#include "pyAgeVault.h"

// net linking mgr
#include "pyNetLinkingMgr.h"
#include "pyAgeInfoStruct.h"
#include "pyAgeLinkStruct.h"

// dni info source
#include "pyDniInfoSource.h"

// audio setting stuff
#include "pyAudioControl.h"

//CCR stufff
#include "pyCCRMgr.h"

// spawn point def
#include "pySpawnPointInfo.h"

#include "pyMarkerMgr.h"
#include "pyStatusLog.h"

// Guess what this is for :P
#include "pyJournalBook.h"

#include "pyKeyMap.h"
#include "pyStream.h"

#include "pyMoviePlayer.h"
#include "pyDrawControl.h"

#include "pyWaveSet.h"
#include "pySwimCurrentInterface.h"

#include "pyCluster.h"
#include "pyGrassShader.h"

#include "pyScoreMgr.h"
#include "pyGameScore.h"

#include "pyCritterBrain.h"

// Game manager stuff
#include "Games/pyGameMgrMsg.h"
#include "Games/pyGameCliMsg.h"
#include "Games/pyGameCli.h"
#include "Games/TicTacToe/pyTTTMsg.h"
#include "Games/TicTacToe/pyTTTGame.h"
#include "Games/Heek/pyHeekMsg.h"
#include "Games/Heek/pyHeekGame.h"
#include "Games/Marker/pyMarkerMsg.h"
#include "Games/Marker/pyMarkerGame.h"
#include "Games/BlueSpiral/pyBlueSpiralMsg.h"
#include "Games/BlueSpiral/pyBlueSpiralGame.h"
#include "Games/ClimbingWall/pyClimbingWallMsg.h"
#include "Games/ClimbingWall/pyClimbingWallGame.h"
#include "Games/VarSync/pyVarSyncMsg.h"
#include "Games/VarSync/pyVarSyncGame.h"

int32_t PythonInterface::initialized = 0;                 // only need to initialize all of Python once
hsBool PythonInterface::FirstTimeInit = true;           // start with "this is the first time"
hsBool PythonInterface::IsInShutdown = false;           // whether we are _really_ in shutdown mode

PyMethodDef* PythonInterface::plasmaMethods = nil;      // the Plasma module's methods
PyObject* PythonInterface::plasmaMod = nil;             // pointer to the Plasma module
PyObject* PythonInterface::plasmaConstantsMod = nil;    // pointer to the PlasmaConstants module
PyObject* PythonInterface::plasmaNetConstantsMod = nil; // pointer to the PlasmaNetConstants module
PyObject* PythonInterface::plasmaVaultConstantsMod = nil; // pointer to the PlasmaVaultConstants module
PyMethodDef* PythonInterface::plasmaGameMethods = nil;  // the PlasmaGame module's methods
PyObject* PythonInterface::plasmaGameMod = nil;         // python object that holds the PlasmaGame module
PyObject* PythonInterface::plasmaGameConstantsMod = nil; // python object that holds the PlasmaGameConstants module
PyObject* PythonInterface::stdOut = nil;                // python object of the stdout file
PyObject* PythonInterface::stdErr = nil;                // python object of the err file

hsBool PythonInterface::debug_initialized = false;      // has the debug been initialized yet?
PyObject* PythonInterface::dbgMod = nil;                // display module for stdout and stderr
PyObject* PythonInterface::dbgOut = nil;
PyObject* PythonInterface::dbgSlice = nil;              // time slice function for the debug window
plStatusLog* PythonInterface::dbgLog = nil;             // output logfile

#if defined(HAVE_CYPYTHONIDE) && !defined(PLASMA_EXTERNAL_RELEASE)
bool PythonInterface::usePythonDebugger = false;
plCyDebServer PythonInterface::debugServer;
bool PythonInterface::requestedExit = false;
#endif

// stupid Windows.h  and who started including that!
#undef DrawText

#if defined(HAVE_CYPYTHONIDE) && !defined(PLASMA_EXTERNAL_RELEASE)
// Special includes for debugging
#include <frameobject.h>

/////////////////////////////////////////////////////////////////////////////
// Our debugger callback class
class DebuggerCallback: public plCyDebServer::IDebServerCallback
{
private:
    plCyDebServer& fServer;

    PyFrameObject* fFrame;
    PyObject* fExceptionInfo;

    std::string IParseCurrentException(); // returns the current exception as a string representation, and clears it

public:
    DebuggerCallback(plCyDebServer& server): fServer(server) {}

    virtual bool MsgReceive(const plCyDebMessage& msg);
    virtual std::string AdjustFilename(const std::string& filename);
    virtual bool CheckBreakpointCondition(const std::string& condition, std::string& error);

    virtual void InitializeBreak();
    virtual std::vector<std::string> GenerateCallstack();
    virtual std::vector<std::pair<std::string, std::string> > GenerateGlobalsList();
    virtual std::vector<std::pair<std::string, std::string> > GenerateLocalsList();
    virtual std::string EvaluateVariable(const std::string& varName);
    virtual void SetVariableValue(const std::string& varName, const std::string& newValue);

    void SetFrame(PyFrameObject* frame) {fFrame = frame;}
    void SetExceptionInfo(PyObject* exceptionInfo) {fExceptionInfo = exceptionInfo;}
};

std::string DebuggerCallback::IParseCurrentException()
{
    std::string error = "";

    if (PyErr_Occurred() == NULL)
        return error; // no error occurred

    PyObject* errType = NULL;
    PyObject* errVal = NULL;
    PyObject* errTraceback = NULL;
    PyErr_Fetch(&errType, &errVal, &errTraceback); // clears the error flag
    PyErr_NormalizeException(&errType, &errVal, &errTraceback);

    if (PyErr_GivenExceptionMatches(errType, PyExc_SyntaxError))
    {
        // we know how to parse out information from syntax errors
        PyObject* message;
        char* filename = NULL;
        int lineNumber = 0;
        int offset = 0;
        char* text = NULL;

        if (PyTuple_Check(errVal))
        {
            // nested tuple, parse out the error information
            PyArg_Parse(errVal, "(O(ziiz))", &message, &filename, &lineNumber, &offset, &text);
            error += PyString_AsString(message);
            if (text)
                error += text;
        }
        else
        {
            // probably just the error class, retrieve the message and text directly
            PyObject* v;
            if ((v = PyObject_GetAttrString(errVal, "msg")))
            {
                error += PyString_AsString(v);
                Py_DECREF(v);
            }
            if ((v == PyObject_GetAttrString(errVal, "text")))
            {
                if (v != Py_None)
                    error += PyString_AsString(v);
                Py_DECREF(v);
            }
        }
    }
    else if (PyClass_Check(errType))
    {
        // otherwise, just return the type of error that occurred
        PyClassObject* exc = (PyClassObject*)errType;
        PyObject* className = exc->cl_name;
        if (className)
            error += PyString_AsString(className);
    }
    else
        error = "Unknown Error";

    // cleanup
    Py_XDECREF(errType);
    Py_XDECREF(errVal);
    Py_XDECREF(errTraceback);

    return error;
}

bool DebuggerCallback::MsgReceive(const plCyDebMessage& msg)
{
    switch (msg.GetMsgType())
    {
    case plCyDebMessage::kMsgExit:
        PythonInterface::DebuggerRequestedExit(true);
        break;
    }
    return false; // let default handling take over
}

std::string DebuggerCallback::AdjustFilename(const std::string& filename)
{
    // python doesn't deal with paths, so we strip out all path information
    std::string retVal = filename;
    std::string::size_type slashPos = filename.rfind('\\');
    if (slashPos != std::string::npos)
        retVal = filename.substr(slashPos + 1);
    else // no back-slashes, look for forward ones
    {
        slashPos = filename.rfind('/');
        if (slashPos != std::string::npos)
            retVal = filename.substr(slashPos + 1);
    }
    return retVal;
}

bool DebuggerCallback::CheckBreakpointCondition(const std::string& condition, std::string& error)
{
    if (!fFrame)
        return true; // just break, we have no current frame?

    if (condition == "")
        return true; // empty condition, break (python doesn't like empty strings)

    // initialize locals, since InitializeBreak isn't called til we break
    PyFrame_FastToLocals(fFrame);

    // run the string in the current context
    PyObject* result = PyRun_String(const_cast<char*>(condition.c_str()), Py_eval_input, fFrame->f_globals, fFrame->f_locals);
    if (result)
    {
        // is the result true?
        int retVal = PyObject_IsTrue(result);
        Py_DECREF(result);

        return (retVal == 1);
    }

    // error occurred, translate it and return
    error = IParseCurrentException();
    return true;
}

void DebuggerCallback::InitializeBreak()
{
    // do a little initialization of our frame (ensuring we get all local data)
    PyFrame_FastToLocals(fFrame);
}

std::vector<std::string> DebuggerCallback::GenerateCallstack()
{
    std::vector<std::string> retVal;

    // we use the frame stored for us by the trace function
    PyFrameObject* curFrame = fFrame;
    while (curFrame)
    {
        std::string filename = PyString_AsString(curFrame->f_code->co_filename);
        int lineNumber = PyCode_Addr2Line(curFrame->f_code, curFrame->f_lasti); // python uses base-1 numbering, we use base-0, but for display we want base-1
        std::string functionName = PyString_AsString(curFrame->f_code->co_name);

        functionName += "(";
        if (curFrame->f_code->co_argcount)
        {
            // we have arguments!
            int argCount = __min(PyTuple_Size(curFrame->f_code->co_varnames), curFrame->f_code->co_argcount);

            for (int curArg = 0; curArg < argCount; ++curArg)
            {
                PyObject* argName = PyTuple_GetItem(curFrame->f_code->co_varnames, curArg);
                if (argName)
                {
                    std::string arg = PyString_AsString(argName);
                    if (arg == "self")
                        continue; // skip self, for readability

                    functionName += arg;

                    if (curFrame->f_locals)
                    {
                        // grab value, if our locals dictionary exists
                        PyObject* val = PyDict_GetItemString(curFrame->f_locals, arg.c_str());
                        if (val)
                        {
                            functionName += "=";
                            functionName += PyString_AsString(PyObject_Str(val));
                        }
                    }
                }

                if (curArg < argCount - 1)
                    functionName += ", ";
            }
        }
        functionName += ")";

        // add it to the callstack
        retVal.push_back(fServer.ConstructCallstackLine(filename, lineNumber, functionName));

        // and step back one frame
        curFrame = curFrame->f_back;
    }

    return retVal;
}

std::vector<std::pair<std::string, std::string> > DebuggerCallback::GenerateGlobalsList()
{
    std::vector<std::pair<std::string, std::string> > retVal;
    if (fFrame && fFrame->f_globals)
    {
        int pos = 0;
        PyObject* key;
        PyObject* value;
        while (PyDict_Next(fFrame->f_globals, &pos, &key, &value))
        {
            // leave modules out of the globals display
            if (key && value && !PyModule_Check(value))
            {
                // leave out glue functions
                if (PyObject_Compare((PyObject*)&PyCFunction_Type, PyObject_Type(value)) == 0)
                    continue;

                std::string keyStr = PyString_AsString(PyObject_Str(key));
                if (keyStr == "__builtins__")
                    continue; // skip builtins

                bool addQuotes = (PyString_Check(value) || PyUnicode_Check(value));

                std::string valueStr = "";
                if (addQuotes)
                    valueStr += "\"";
                valueStr += PyString_AsString(PyObject_Str(value));
                if (addQuotes)
                    valueStr += "\"";

                // add it to the list of pairs
                retVal.push_back(std::pair<std::string, std::string>(keyStr, valueStr));
            }
        }
    }
    return retVal;
}

std::vector<std::pair<std::string, std::string> > DebuggerCallback::GenerateLocalsList()
{
    std::vector<std::pair<std::string, std::string> > retVal;
    if (fFrame && fFrame->f_locals)
    {
        int pos = 0;
        PyObject* key;
        PyObject* value;
        while (PyDict_Next(fFrame->f_locals, &pos, &key, &value))
        {
            // leave modules and instances out of the globals display
            if (key && value && !PyModule_Check(value) && !PyInstance_Check(value))
            {
                // leave out functions, classes, and types
                if (PyObject_Compare((PyObject*)&PyFunction_Type, PyObject_Type(value)) == 0)
                    continue;
                if (PyObject_Compare((PyObject*)&PyClass_Type, PyObject_Type(value)) == 0)
                    continue;
                if (PyObject_Compare((PyObject*)&PyType_Type, PyObject_Type(value)) == 0)
                    continue;

                std::string keyStr = PyString_AsString(PyObject_Str(key));
                if (keyStr == "__builtins__")
                    continue; // skip builtins

                bool addQuotes = (PyString_Check(value) || PyUnicode_Check(value));

                std::string valueStr = "";
                if (addQuotes)
                    valueStr += "\"";
                valueStr += PyString_AsString(PyObject_Str(value));
                if (addQuotes)
                    valueStr += "\"";

                // add it to the list of pairs
                retVal.push_back(std::pair<std::string, std::string>(keyStr, valueStr));
            }
        }
    }
    return retVal;
}

std::string DebuggerCallback::EvaluateVariable(const std::string& varName)
{
    if (fFrame)
    {
        PyObject* evalResult = PyRun_String(const_cast<char*>(varName.c_str()), Py_eval_input, fFrame->f_globals, fFrame->f_locals);
        std::string retVal = "";
        if (evalResult)
        {
            // convert the result to something readable
            PyObject* reprObj = PyObject_Repr(evalResult);
            if (reprObj)
                retVal = PyString_AsString(reprObj);
            else
                retVal = "<REPR FAIL>";
            Py_XDECREF(reprObj);
        }
        else
            retVal = IParseCurrentException();
        Py_XDECREF(evalResult);
        return retVal;
    }
    else
        return "<NO FRAME>";
}

void DebuggerCallback::SetVariableValue(const std::string& varName, const std::string& newValue)
{
    std::string expression = varName + "=" + newValue;
    if (fFrame)
    {
        PyObject* evalResult = PyRun_String(const_cast<char*>(expression.c_str()), Py_single_input, fFrame->f_globals, fFrame->f_locals);
        if (evalResult)
            PyFrame_LocalsToFast(fFrame, 0); // convert the locals that changed (if any) back to "fast" locals
        else
            PyErr_Print();
        Py_XDECREF(evalResult);
    }
}

DebuggerCallback debServerCallback(*PythonInterface::PythonDebugger());

// python trace function, handles most of the work required for debugging
static int PythonTraceCallback(PyObject*, PyFrameObject* frame, int what, PyObject* arg)
{
    // obj (first parameter) is always NULL for is (it's the parameter passed by the set trace function)

    // update the callback class' stored values
    debServerCallback.SetFrame(frame);
    debServerCallback.SetExceptionInfo(NULL);

    // translate the python what value to the debugger what value
    plCyDebServer::TraceWhat debuggerWhat;
    switch (what)
    {
    case PyTrace_LINE:
        debuggerWhat = plCyDebServer::kTraceLine;
        break;

    case PyTrace_CALL:
        debuggerWhat = plCyDebServer::kTraceCall;
        break;

    case PyTrace_RETURN:
        debuggerWhat = plCyDebServer::kTraceReturn;
        break;

    case PyTrace_EXCEPTION:
        debuggerWhat = plCyDebServer::kTraceException;
        debServerCallback.SetExceptionInfo(arg); // save off the exception information
        break;

    default:
        assert(!"Invalid what for python trace function");
        return 0; // pretty much ignore if they pass us a bad value
    }

    std::string filename = PyString_AsString(frame->f_code->co_filename);
    int line = PyCode_Addr2Line(frame->f_code, frame->f_lasti) - 1; // python uses base-1 numbering, we use base-0

    // now handle the trace call
    PythonInterface::PythonDebugger()->Trace(debuggerWhat, filename, line, frame->f_tstate->recursion_depth);

    return 0;
}
#endif // PLASMA_EXTERNAL_RELEASE

/////////////////////////////////////////////////////////////////////////////
// A small class that is bound to python so we can redirect stdout

class pyOutputRedirector
{
private:
    std::string fData;
    static bool fTypeCreated;

protected:
    pyOutputRedirector() {}

public:
    // required functions for PyObject interoperability
    PYTHON_CLASS_NEW_FRIEND(ptOutputRedirector);
    PYTHON_CLASS_NEW_DEFINITION;
    PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyOutputRedirector object
    PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyOutputRedirector); // converts a PyObject to a pyOutputRedirector (throws error if not correct type)

    void Write(std::string data) {fData += data;}
    void Write(std::wstring data)
    {
        char* strData = hsWStringToString(data.c_str());
        Write(strData);
        delete [] strData;
    }

    // accessor functions for the PyObject*

    // returns the current data stored
    static std::string GetData(PyObject *redirector)
    {
        if (!pyOutputRedirector::Check(redirector))
            return ""; // it's not a redirector object
        pyOutputRedirector *obj = pyOutputRedirector::ConvertFrom(redirector);
        return obj->fData;
    }

    // clears the internal buffer out
    static void ClearData(PyObject *redirector)
    {
        if (!pyOutputRedirector::Check(redirector))
            return; // it's not a redirector object
        pyOutputRedirector *obj = pyOutputRedirector::ConvertFrom(redirector);
        obj->fData = "";
    }
};

bool pyOutputRedirector::fTypeCreated = false;

// Now for the glue for the redirector
PYTHON_CLASS_DEFINITION(ptOutputRedirector, pyOutputRedirector);

PYTHON_DEFAULT_NEW_DEFINITION(ptOutputRedirector, pyOutputRedirector)
PYTHON_DEFAULT_DEALLOC_DEFINITION(ptOutputRedirector)

PYTHON_INIT_DEFINITION(ptOutputRedirector, args, keywords)
{
    PYTHON_RETURN_INIT_OK;
}

PYTHON_METHOD_DEFINITION(ptOutputRedirector, write, args)
{
    PyObject* textObj;
    if (!PyArg_ParseTuple(args, "O", &textObj))
    {
        PyErr_SetString(PyExc_TypeError, "write expects a string or unicode string");
        PYTHON_RETURN_ERROR;
    }
    if (PyUnicode_Check(textObj))
    {
        int strLen = PyUnicode_GetSize(textObj);
        wchar_t* text = new wchar_t[strLen + 1];
        PyUnicode_AsWideChar((PyUnicodeObject*)textObj, text, strLen);
        text[strLen] = L'\0';
        self->fThis->Write(text);
        delete [] text;
        PYTHON_RETURN_NONE;
    }
    else if (PyString_Check(textObj))
    {
        char* text = PyString_AsString(textObj);
        self->fThis->Write(text);
        PYTHON_RETURN_NONE;
    }
    PyErr_SetString(PyExc_TypeError, "write expects a string or unicode string");
    PYTHON_RETURN_ERROR;
}

PYTHON_START_METHODS_TABLE(ptOutputRedirector)
    PYTHON_METHOD(ptOutputRedirector, write, "Adds text to the output object"),
PYTHON_END_METHODS_TABLE;

// Type structure definition
PLASMA_DEFAULT_TYPE(ptOutputRedirector, "A class that is used to redirect stdout and stderr");

// required functions for PyObject interoperability
PyObject *pyOutputRedirector::New()
{
    if (!fTypeCreated)
    {
        if (PyType_Ready(&ptOutputRedirector_type) < 0)
            return NULL;
        fTypeCreated = true;
    }
    ptOutputRedirector *newObj = (ptOutputRedirector*)ptOutputRedirector_type.tp_new(&ptOutputRedirector_type, NULL, NULL);
    return (PyObject*)newObj;
}

PYTHON_CLASS_CHECK_IMPL(ptOutputRedirector, pyOutputRedirector)
PYTHON_CLASS_CONVERT_FROM_IMPL(ptOutputRedirector, pyOutputRedirector)


/////////////////////////////////////////////////////////////////////////////
// A small class that is bound to python so we can redirect stderr

class pyErrorRedirector
{
private:
    static bool fTypeCreated;

    std::string fData;
    bool        fLog;

protected:
    pyErrorRedirector() : fLog(true) {}

public:
    // required functions for PyObject interoperability
    PYTHON_CLASS_NEW_FRIEND(ptErrorRedirector);
    PYTHON_CLASS_NEW_DEFINITION;
    PYTHON_CLASS_CHECK_DEFINITION; // returns true if the PyObject is a pyOutputRedirector object
    PYTHON_CLASS_CONVERT_FROM_DEFINITION(pyErrorRedirector); // converts a PyObject to a pyOutputRedirector (throws error if not correct type)

    void SetLogging(bool log)
    {
        fLog = log;
    }

    void Write(std::string data)
    {
        PyObject* stdOut = PythonInterface::GetStdOut();

        if (stdOut && pyOutputRedirector::Check(stdOut))
        {
            pyOutputRedirector *obj = pyOutputRedirector::ConvertFrom(stdOut);
            obj->Write(data);
        }

        if (fLog)
            fData += data;
    }

    void Write(std::wstring data)
    {
        char* strData = hsWStringToString(data.c_str());
        Write(strData);
        delete [] strData;
    }

    void ExceptHook(PyObject* except, PyObject* val, PyObject* tb)
    {
        PyErr_Display(except, val, tb);

        // Send to the log server
        wchar_t* wdata = hsStringToWString(fData.c_str());
        NetCliAuthLogPythonTraceback(wdata);
        delete [] wdata;

        if (fLog)
            fData.clear();
    }
};

bool pyErrorRedirector::fTypeCreated = false;

// Now for the glue for the redirector
PYTHON_CLASS_DEFINITION(ptErrorRedirector, pyErrorRedirector);

PYTHON_DEFAULT_NEW_DEFINITION(ptErrorRedirector, pyErrorRedirector)
PYTHON_DEFAULT_DEALLOC_DEFINITION(ptErrorRedirector)

PYTHON_INIT_DEFINITION(ptErrorRedirector, args, keywords)
{
    PYTHON_RETURN_INIT_OK;
}

PYTHON_METHOD_DEFINITION(ptErrorRedirector, write, args)
{
    PyObject* textObj;
    if (!PyArg_ParseTuple(args, "O", &textObj))
    {
        PyErr_SetString(PyExc_TypeError, "write expects a string or unicode string");
        PYTHON_RETURN_ERROR;
    }
    if (PyUnicode_Check(textObj))
    {
        int strLen = PyUnicode_GetSize(textObj);
        wchar_t* text = new wchar_t[strLen + 1];
        PyUnicode_AsWideChar((PyUnicodeObject*)textObj, text, strLen);
        text[strLen] = L'\0';
        self->fThis->Write(text);
        delete [] text;
        PYTHON_RETURN_NONE;
    }
    else if (PyString_Check(textObj))
    {
        char* text = PyString_AsString(textObj);
        self->fThis->Write(text);
        PYTHON_RETURN_NONE;
    }
    PyErr_SetString(PyExc_TypeError, "write expects a string or unicode string");
    PYTHON_RETURN_ERROR;
}

PYTHON_METHOD_DEFINITION(ptErrorRedirector, excepthook, args)
{
    PyObject *exc, *value, *tb;
    if (!PyArg_ParseTuple(args, "OOO", &exc, &value, &tb))
        PYTHON_RETURN_ERROR;

    self->fThis->ExceptHook(exc, value, tb);

    PYTHON_RETURN_NONE;
}

PYTHON_START_METHODS_TABLE(ptErrorRedirector)
    PYTHON_METHOD(ptErrorRedirector, write, "Adds text to the output object"),
    PYTHON_METHOD(ptErrorRedirector, excepthook, "Handles exceptions"),
PYTHON_END_METHODS_TABLE;

// Type structure definition
PLASMA_DEFAULT_TYPE(ptErrorRedirector, "A class that is used to redirect stdout and stderr");

// required functions for PyObject interoperability
PyObject *pyErrorRedirector::New()
{
    if (!fTypeCreated)
    {
        if (PyType_Ready(&ptErrorRedirector_type) < 0)
            return NULL;
        fTypeCreated = true;
    }
    ptErrorRedirector *newObj = (ptErrorRedirector*)ptErrorRedirector_type.tp_new(&ptErrorRedirector_type, NULL, NULL);
    return (PyObject*)newObj;
}

PYTHON_CLASS_CHECK_IMPL(ptErrorRedirector, pyErrorRedirector)
PYTHON_CLASS_CONVERT_FROM_IMPL(ptErrorRedirector, pyErrorRedirector)

/////////////////////////////////////////////////////////////////////////////
// PEP 302 Import Hook
/////////////////////////////////////////////////////////////////////////////
#ifndef BUILDING_PYPLASMA
struct ptImportHook
{
    PyObject_HEAD
};

// First three functions are just so I can be lazy
// and use the already existing macros to do my dirty
// work. I'm seriously lazy.

static PyObject* ptImportHook_new(PyTypeObject* type, PyObject* args, PyObject*)
{
    ptImportHook* self = (ptImportHook*)type->tp_alloc(type, 0);
    return (PyObject*)self;
}

PYTHON_NO_INIT_DEFINITION(ptImportHook)

static void ptImportHook_dealloc(ptImportHook *self)
{
    self->ob_type->tp_free((PyObject*)self);
}

PYTHON_METHOD_DEFINITION(ptImportHook, find_module, args)
{
    char* module_name;
    PyObject* module_path;

    if (!PyArg_ParseTuple(args, "s|O", &module_name, &module_path))
    {
        PyErr_SetString(PyExc_TypeError, "find_module expects string, string");
        PYTHON_RETURN_ERROR;
    }

    // If this is set, we can't do it.
    if (PyString_Check(module_path))
        PYTHON_RETURN_NONE;

    std::string package_module_name = module_name;
    package_module_name += ".__init__";
    if (PythonPack::IsItPythonPacked(module_name))
    {
        Py_INCREF(self);
        return (PyObject*)self;
    } else if (PythonPack::IsItPythonPacked(package_module_name.c_str()))
    {
        Py_INCREF(self);
        return (PyObject*)self;
    }
    else
        PYTHON_RETURN_NONE;
}

PyObject *ptImportHook_load_module_detail(ptImportHook *self, char* module_name, char* packed_name, bool isPackage, bool& found)
{
    // We want to check sys.modules for the module first
    // If it's not in there, we have to load the module
    // and add it to the sys.modules dict for future reference,
    // otherwise reload() will not work properly.
    PyObject* result = nil;
    PyObject* modules = PyImport_GetModuleDict();
    hsAssert(PyDict_Check(modules), "sys.modules is not a dict");

    if (result = PyDict_GetItemString(modules, module_name))
    {
        if (!PyModule_Check(result))
        {
            hsAssert(false, "PEP 302 hook found module in sys.modules, but it isn't a module! O.o");
            result = nil;
            PyErr_SetString(PyExc_TypeError, "module in sys.modules isn't a module");
        }
    }
    else
    {
        if (PyObject* pyc = PythonPack::OpenPythonPacked(packed_name))
        {
            result = PyImport_AddModule(module_name);
            if(!result)
                return nil;
            PyObject* d = PyModule_GetDict(result);
            PyDict_SetItemString(d, "__builtins__", PyEval_GetBuiltins());
            PyObject *file = PyString_FromString(packed_name);
            PyModule_AddObject(result, "__file__", file);
            PyDict_SetItemString(d, "__loader__", (PyObject*)self);
            if(isPackage) {
                PyObject *path = PyString_FromString(module_name);
                PyObject *l = PyList_New(1);
                PyList_SetItem(l, 0, path);
                PyDict_SetItemString(d, "__path__", l);
                Py_DECREF(l);
            }
            PyObject* v = PyEval_EvalCode((PyCodeObject *)pyc, d, d);
            if(!v) 
            {
                PyDict_DelItemString(modules, module_name);
                return nil;
            }
            Py_INCREF(result);
        }
        else {
            found = false;
            PyErr_SetString(PyExc_ImportError, "module not found in python.pak");
        }
    }

    return result;
}

PYTHON_METHOD_DEFINITION(ptImportHook, load_module, args)
{
    char* module_name;
    if (!PyArg_ParseTuple(args, "s", &module_name))
    {
        PyErr_SetString(PyExc_TypeError, "load_module expects string");
        PYTHON_RETURN_ERROR;
    }
    bool found = true;
    PyObject *result = ptImportHook_load_module_detail(self, module_name, module_name, false, found);
    if (!found)
    {
        PyErr_Clear();
        std::string package_module_name = module_name;
        package_module_name += ".__init__";
        result = ptImportHook_load_module_detail(self, module_name, (char*)package_module_name.c_str(), true, found);
    }
    return result;
}

PYTHON_START_METHODS_TABLE(ptImportHook)
    PYTHON_METHOD(ptImportHook, find_module, "Params: module_name,package_path\nChecks to see if a given module exists (NOTE: package_path is not used!)"),
    PYTHON_METHOD(ptImportHook, load_module, "Params: module_name \\nReturns the module given by module_name, if it exists in python.pak"),
PYTHON_END_METHODS_TABLE;

PYTHON_TYPE_START(ptImportHook)
    0,
    "Plasma.ptImportHook",
    sizeof(ptImportHook),                       /* tp_basicsize */
    0,                                          /* tp_itemsize */
    (destructor)ptImportHook_dealloc,           /* tp_dealloc */
    0,                                          /* tp_print */
    0,                                          /* tp_getattr */
    0,                                          /* tp_setattr */
    0,                                          /* tp_compare */
    0,                                          /* tp_repr */
    0,                                          /* tp_as_number */
    0,                                          /* tp_as_sequence */
    0,                                          /* tp_as_mapping */
    0,                                          /* tp_hash */
    0,                                          /* tp_call */
    0,                                          /* tp_str */
    0,                                          /* tp_getattro */
    0,                                          /* tp_setattro */
    0,                                          /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,   /* tp_flags */
    "PEP 302 Import Hook",                      /* tp_doc */
    0,                                          /* tp_traverse */
    0,                                          /* tp_clear */
    0,                                          /* tp_richcompare */
    0,                                          /* tp_weaklistoffset */
    0,                                          /* tp_iter */
    0,                                          /* tp_iternext */
    PYTHON_DEFAULT_METHODS_TABLE(ptImportHook), /* tp_methods */
    0,                                          /* tp_members */
    0,                                          /* tp_getset */
    0,                                          /* tp_base */
    0,                                          /* tp_dict */
    0,                                          /* tp_descr_get */
    0,                                          /* tp_descr_set */
    0,                                          /* tp_dictoffset */
    PYTHON_DEFAULT_INIT(ptImportHook),          /* tp_init */
    0,                                          /* tp_alloc */
    ptImportHook_new                            /* tp_new */
PYTHON_TYPE_END;

void ptImportHook_AddPlasmaClasses(PyObject* m)
{
    PYTHON_CLASS_IMPORT_START(m);
    PYTHON_CLASS_IMPORT(m, ptImportHook);
    PYTHON_CLASS_IMPORT_END(m);
}
#endif // BUILDING_PYPLASMA

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : initPython
//  PARAMETERS : none
//
//  PURPOSE    : Initialize the Python dll
//
void PythonInterface::initPython()
{
    // if haven't been initialized then do it
    if ( FirstTimeInit && Py_IsInitialized() == 0 )
    {
        FirstTimeInit = false;
        // initialize the Python stuff
        // let Python do some initialization...
        Py_SetProgramName("plasma");
        Py_NoSiteFlag = 1;
        Py_IgnoreEnvironmentFlag = 1;
        Py_Initialize();

#if defined(HAVE_CYPYTHONIDE) && !defined(PLASMA_EXTERNAL_RELEASE)
        if (usePythonDebugger)
        {
            debugServer.SetCallbackClass(&debServerCallback);
            debugServer.Init();
            PyEval_SetTrace((Py_tracefunc)PythonTraceCallback, NULL);
        }
#endif

        if (!dbgLog)
        {
            dbgLog = plStatusLogMgr::GetInstance().CreateStatusLog( 30, "Python.log", 
                plStatusLog::kFilledBackground | plStatusLog::kAlignToTop | plStatusLog::kTimestamp );
        }

        // create the output redirector for the stdout and stderr file
        stdOut = pyOutputRedirector::New();
        stdErr = pyErrorRedirector::New();

        // if we need the builtins then find the builtin module
        PyObject* sysmod = PyImport_ImportModule("sys");
        // then add the builtin dictionary to our module's dictionary
        // get the sys's dictionary to find the stdout and stderr
        PyObject* sys_dict = PyModule_GetDict(sysmod);
        Py_INCREF(sys_dict);
        if (stdOut != nil)
        {
            if (PyDict_SetItemString(sys_dict,"stdout", stdOut))
                dbgLog->AddLine("Could not redirect stdout, Python output may not appear in the log\n");
        }
        else
            dbgLog->AddLine("Could not create python redirector, Python output will not appear in the log\n");
        
        if (stdErr != nil)
        {
            if (!PyDict_SetItemString(sys_dict,"stderr", stdErr))
            {
                bool dontLog = false;

                // Find the excepthook
                PyObject* stdErrExceptHook = PyObject_GetAttrString(stdErr, "excepthook");
                if (stdErrExceptHook)
                {
                    if (!PyCallable_Check(stdErrExceptHook) || PyDict_SetItemString(sys_dict,"excepthook", stdErrExceptHook))
                    {
                        dbgLog->AddLine("Could not redirect excepthook, Python error output will not get to the log server\n");
                        dontLog = true;
                    }
                    Py_DECREF(stdErrExceptHook);
                }
                else
                {
                    dbgLog->AddLine("Could not find stdErr excepthook, Python error output will not get to the log server\n");
                    dontLog = true;
                }

                if (dontLog)
                {
                    if (pyErrorRedirector::Check(stdErr))
                    {
                        pyErrorRedirector* redir = pyErrorRedirector::ConvertFrom(stdErr);
                        redir->SetLogging(false);
                    }
                }
            }
            else
            {
                dbgLog->AddLine("Could not redirect stderr, Python error output may not appear in the log or on the log server\n");
            }
        }
        else
        {
            dbgLog->AddLine("Could not create python redirector, Python error output will not appear in the log\n");
        }

        // NOTE: we will reset the path to not include paths
        // that Python may have found in the registry
        PyObject* path_list = PyList_New(3);
        if (PyList_SetItem(path_list, 0, PyString_FromString(".\\python")))
        {
            Py_DECREF(sys_dict);
            Py_DECREF(path_list);
            dbgLog->AddLine("Error while creating python path:\n");
            getOutputAndReset();
            return;
        }
        // make sure that our plasma libraries are gotten before the system ones
        if (PyList_SetItem(path_list, 1, PyString_FromString(".\\python\\plasma")))
        {
            Py_DECREF(sys_dict);
            Py_DECREF(path_list);
            dbgLog->AddLine("Error while creating python path:\n");
            getOutputAndReset();
            return;
        }
        if (PyList_SetItem(path_list, 2, PyString_FromString(".\\python\\system")))
        {
            Py_DECREF(sys_dict);
            Py_DECREF(path_list);
            dbgLog->AddLine("Error while creating python path:\n");
            getOutputAndReset();
            return;
        }

        // set the path to be this one
        if (PyDict_SetItemString(sys_dict,"path",path_list))
        {
            Py_DECREF(sys_dict);
            Py_DECREF(path_list);
            dbgLog->AddLine("Error while setting python path:\n");
            getOutputAndReset();
            return;
        }

        Py_DECREF(path_list);

        std::vector<PyMethodDef> methods; // this is temporary, for easy addition of new methods
        AddPlasmaMethods(methods);

        // now copy the data to our real method definition structure
        plasmaMethods = new PyMethodDef[methods.size() + 1];
        for (int curMethod = 0; curMethod < methods.size(); curMethod++)
            plasmaMethods[curMethod] = methods[curMethod];
        PyMethodDef terminator = {NULL};
        plasmaMethods[methods.size()] = terminator; // add the terminator

        // now set up the module with the method data
        plasmaMod = Py_InitModule("Plasma", plasmaMethods);
        if (plasmaMod == NULL)
        {
            dbgLog->AddLine("Could not setup the Plasma module\n");
            return;
        }
        if (PyErr_Occurred())
        {
            dbgLog->AddLine("Python error while setting up Plasma:\n");
            getOutputAndReset();
        }
        Py_INCREF(plasmaMod); // make sure python doesn't get rid of it

        AddPlasmaClasses(); // now add the classes to the module
        if (PyErr_Occurred())
        {
            dbgLog->AddLine("Python error while adding classes to Plasma:\n");
            std::string error;
            getOutputAndReset(&error);
        }

 #ifndef BUILDING_PYPLASMA
        // Begin PEP 302 Import Hook stuff
        // We need to create a ptImportHook object
        ptImportHook* hook = PyObject_New(ptImportHook, &ptImportHook_type);
        PyObject* metapath = PyDict_GetItemString(sys_dict, "meta_path");
        Py_INCREF(metapath);

        // Since PEP 302 is insane, let's be sure things are the way
        // that we expect them to be. Silent failures != cool.
        hsAssert(metapath != nil, "PEP 302: sys.__dict__['meta_path'] missing!");
        hsAssert(PyList_Check(metapath), "PEP 302: sys.__dict__['meta_path'] is not a list!");

        // Now that we have meta_path, add our hook to the list
        PyList_Append(metapath, (PyObject*)hook);
        Py_DECREF(metapath);
        // And we're done!
#endif // BUILDING_PYPLASMA

        Py_DECREF(sys_dict);

        // initialize the PlasmaConstants module
        PyMethodDef noMethods = {NULL};
        plasmaConstantsMod = Py_InitModule("PlasmaConstants", &noMethods); // it has no methods, just values
        if (plasmaConstantsMod == NULL)
        {
            dbgLog->AddLine("Could not setup the PlasmaConstants module\n");
            return;
        }
        if (PyErr_Occurred())
        {
            dbgLog->AddLine("Python error while setting up PlasmaConstants:\n");
            std::string error;
            getOutputAndReset(&error);
        }
        Py_INCREF(plasmaConstantsMod);

        AddPlasmaConstantsClasses();

        if (PyErr_Occurred())
        {
            dbgLog->AddLine("Python error while adding classes to PlasmaConstants:\n");
            std::string error;
            getOutputAndReset(&error);
        }

        // initialize the PlasmaNetConstants module
        plasmaNetConstantsMod = Py_InitModule("PlasmaNetConstants", &noMethods); // it has no methods, just values
        if (plasmaNetConstantsMod == NULL)
        {
            dbgLog->AddLine("Could not setup the PlasmaNetConstants module\n");
            return;
        }
        if (PyErr_Occurred())
        {
            dbgLog->AddLine("Python error while setting up PlasmaNetConstants:\n");
            std::string error;
            getOutputAndReset(&error);
        }
        Py_INCREF(plasmaNetConstantsMod);

        AddPlasmaNetConstantsClasses();

        if (PyErr_Occurred())
        {
            dbgLog->AddLine("Python error while adding classes to PlasmaNetConstants:\n");
            std::string error;
            getOutputAndReset(&error);
        }

        // initialize the PlasmaVaultConstants module
        plasmaVaultConstantsMod = Py_InitModule("PlasmaVaultConstants", &noMethods); // it has no methods, just values
        if (plasmaVaultConstantsMod == NULL)
        {
            dbgLog->AddLine("Could not setup the PlasmaVaultConstants module\n");
            return;
        }
        if (PyErr_Occurred())
        {
            dbgLog->AddLine("Python error while setting up PlasmaVaultConstants:\n");
            std::string error;
            getOutputAndReset(&error);
        }
        Py_INCREF(plasmaVaultConstantsMod);

        AddPlasmaVaultConstantsClasses();

        if (PyErr_Occurred())
        {
            dbgLog->AddLine("Python error while adding classes to PlasmaVaultConstants:\n");
            std::string error;
            getOutputAndReset(&error);
        }

        // setup the global methods for the PlasmaGame module
        methods.clear();
        AddPlasmaGameMethods(methods);

        // now copy the data to our real method definition structure
        plasmaGameMethods = new PyMethodDef[methods.size() + 1];
        for (int curMethod = 0; curMethod < methods.size(); curMethod++)
            plasmaGameMethods[curMethod] = methods[curMethod];
        plasmaGameMethods[methods.size()] = terminator; // add the terminator

        // initialize the PlasmaGame module
        plasmaGameMod = Py_InitModule("PlasmaGame", plasmaGameMethods);
        if (plasmaGameMod == NULL)
        {
            dbgLog->AddLine("Could not setup the PlasmaGame module\n");
            return;
        }
        if (PyErr_Occurred())
        {
            dbgLog->AddLine("Python error while setting up PlasmaGame:\n");
            std::string error;
            getOutputAndReset(&error);
        }
        Py_INCREF(plasmaGameMod);

        AddPlasmaGameClasses();

        if (PyErr_Occurred())
        {
            dbgLog->AddLine("Python error while adding classes to PlasmaGame:\n");
            std::string error;
            getOutputAndReset(&error);
        }

        // initialize the PlasmaGameConstants module
        plasmaGameConstantsMod = Py_InitModule("PlasmaGameConstants", &noMethods); // it has no methods, just values
        if (plasmaGameConstantsMod == NULL)
        {
            dbgLog->AddLine("Could not setup the PlasmaGameConstants module\n");
            return;
        }
        if (PyErr_Occurred())
        {
            dbgLog->AddLine("Python error while setting up PlasmaGameConstants:\n");
            std::string error;
            getOutputAndReset(&error);
        }
        Py_INCREF(plasmaGameConstantsMod);

        AddPlasmaGameConstantsClasses();

        if (PyErr_Occurred())
        {
            dbgLog->AddLine("Python error while adding classes to PlasmaGameConstants:\n");
            std::string error;
            getOutputAndReset(&error);
        }
    }
    initialized++;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : initDebugInterface
//  PARAMETERS : none
//
//  PURPOSE    : Initialize the Python to Plasma 
//
void PythonInterface::initDebugInterface()
{
    if ( !debug_initialized )
    {
        // bring up the debug window
        dbgMod = PyImport_ImportModule("cydebug");
        // was there a debug module?
        if ( dbgMod != nil )
        {
            PyObject *dict;
            // get the dictionary for this module
            dict = PyModule_GetDict(dbgMod);
            dbgOut = PyDict_GetItemString(dict, "writeout");
            dbgSlice = PyDict_GetItemString(dict, "timeslice");
        }
    }
    debug_initialized = true;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddPlasmaMethods
//  PARAMETERS : none
//
//  PURPOSE    : Add global methods to the Plasma module
//
void PythonInterface::AddPlasmaMethods(std::vector<PyMethodDef> &methods)
{
    cyMisc::AddPlasmaMethods(methods);
    cyAvatar::AddPlasmaMethods(methods);
    cyAccountManagement::AddPlasmaMethods(methods);

    pyDrawControl::AddPlasmaMethods(methods);
    pyGUIDialog::AddPlasmaMethods(methods);
    pyImage::AddPlasmaMethods(methods);
    pyJournalBook::AddPlasmaMethods(methods);
    pySDLModifier::AddPlasmaMethods(methods);
    pySpawnPointInfo::AddPlasmaMethods(methods);
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddPlasmaClasses
//  PARAMETERS : none
//
//  PURPOSE    : Add classes to the Plasma module
//
void PythonInterface::AddPlasmaClasses()
{
    pyKey::AddPlasmaClasses(plasmaMod);
    pySceneObject::AddPlasmaClasses(plasmaMod);

    pyAgeInfoStruct::AddPlasmaClasses(plasmaMod);
    pyAgeInfoStructRef::AddPlasmaClasses(plasmaMod);
    pyAgeLinkStruct::AddPlasmaClasses(plasmaMod);
    pyAgeLinkStructRef::AddPlasmaClasses(plasmaMod);
    pySpawnPointInfo::AddPlasmaClasses(plasmaMod);
    pySpawnPointInfoRef::AddPlasmaClasses(plasmaMod);

    pyColor::AddPlasmaClasses(plasmaMod);
    pyMatrix44::AddPlasmaClasses(plasmaMod);
    pyPoint3::AddPlasmaClasses(plasmaMod);
    pyVector3::AddPlasmaClasses(plasmaMod);

    cyAnimation::AddPlasmaClasses(plasmaMod);
    cyAvatar::AddPlasmaClasses(plasmaMod);
    cyCamera::AddPlasmaClasses(plasmaMod);
    cyDraw::AddPlasmaClasses(plasmaMod);
    cyInputInterface::AddPlasmaClasses(plasmaMod);
    cyParticleSys::AddPlasmaClasses(plasmaMod);
    cyPhysics::AddPlasmaClasses(plasmaMod);

    pyAudioControl::AddPlasmaClasses(plasmaMod);
    pyCluster::AddPlasmaClasses(plasmaMod);
    pyDniCoordinates::AddPlasmaClasses(plasmaMod);
    pyDniInfoSource::AddPlasmaClasses(plasmaMod);
    pyDynamicText::AddPlasmaClasses(plasmaMod);
    pyImage::AddPlasmaClasses(plasmaMod);
    pyJournalBook::AddPlasmaClasses(plasmaMod);
    pyKeyMap::AddPlasmaClasses(plasmaMod);
    pyMarkerMgr::AddPlasmaClasses(plasmaMod);
    pyMoviePlayer::AddPlasmaClasses(plasmaMod);
    pyNetLinkingMgr::AddPlasmaClasses(plasmaMod);
    pyNotify::AddPlasmaClasses(plasmaMod);
    pyPlayer::AddPlasmaClasses(plasmaMod);
    pyStatusLog::AddPlasmaClasses(plasmaMod);
    pyStream::AddPlasmaClasses(plasmaMod);
    pySwimCurrentInterface::AddPlasmaClasses(plasmaMod);
    pyWaveSet::AddPlasmaClasses(plasmaMod);

    // SDL
    pySDLModifier::AddPlasmaClasses(plasmaMod);
    pySDLStateDataRecord::AddPlasmaClasses(plasmaMod);
    pySimpleStateVariable::AddPlasmaClasses(plasmaMod);

    // GUI objects
    pyGUIDialog::AddPlasmaClasses(plasmaMod);
    pyGUISkin::AddPlasmaClasses(plasmaMod);
    pyGUIPopUpMenu::AddPlasmaClasses(plasmaMod);
    // GUI base classes
    pyGUIControl::AddPlasmaClasses(plasmaMod);
    pyGUIControlValue::AddPlasmaClasses(plasmaMod);
    // GUI derived classes
    pyGUIControlButton::AddPlasmaClasses(plasmaMod);
    pyGUIControlCheckBox::AddPlasmaClasses(plasmaMod);
    pyGUIControlClickMap::AddPlasmaClasses(plasmaMod);
    pyGUIControlDragBar::AddPlasmaClasses(plasmaMod);
    pyGUIControlDraggable::AddPlasmaClasses(plasmaMod);
    pyGUIControlDynamicText::AddPlasmaClasses(plasmaMod);
    pyGUIControlEditBox::AddPlasmaClasses(plasmaMod);
    pyGUIControlKnob::AddPlasmaClasses(plasmaMod);
    pyGUIControlListBox::AddPlasmaClasses(plasmaMod);
    pyGUIControlMultiLineEdit::AddPlasmaClasses(plasmaMod);
    pyGUIControlProgress::AddPlasmaClasses(plasmaMod);
    pyGUIControlRadioGroup::AddPlasmaClasses(plasmaMod);
    pyGUIControlTextBox::AddPlasmaClasses(plasmaMod);
    pyGUIControlUpDownPair::AddPlasmaClasses(plasmaMod);

    // Vault objects
    pyAgeVault::AddPlasmaClasses(plasmaMod);
    pyVault::AddPlasmaClasses(plasmaMod);
    // Vault node base classes
    pyVaultNode::AddPlasmaClasses(plasmaMod);
    pyVaultNodeRef::AddPlasmaClasses(plasmaMod);
    pyVaultFolderNode::AddPlasmaClasses(plasmaMod);
    // Vault node derived classes
    pyVaultAgeInfoListNode::AddPlasmaClasses(plasmaMod);
    pyVaultAgeInfoNode::AddPlasmaClasses(plasmaMod);
    pyVaultAgeLinkNode::AddPlasmaClasses(plasmaMod);
    pyVaultChronicleNode::AddPlasmaClasses(plasmaMod);
    pyVaultImageNode::AddPlasmaClasses(plasmaMod);
    pyVaultMarkerGameNode::AddPlasmaClasses(plasmaMod);
    pyVaultPlayerInfoListNode::AddPlasmaClasses(plasmaMod);
    pyVaultPlayerInfoNode::AddPlasmaClasses(plasmaMod);
    pyVaultSDLNode::AddPlasmaClasses(plasmaMod);
    pyVaultSystemNode::AddPlasmaClasses(plasmaMod);
    pyVaultTextNoteNode::AddPlasmaClasses(plasmaMod);
    
    // Shaders
    pyGrassShader::AddPlasmaClasses(plasmaMod);

    // Game Scores
    pyScoreMgr::AddPlasmaClasses(plasmaMod);
    pyGameScore::AddPlasmaClasses(plasmaMod);

    // AI
    pyCritterBrain::AddPlasmaClasses(plasmaMod);

    // Stupid thing
    ptImportHook_AddPlasmaClasses(plasmaMod);
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddPlasmaConstantsClasses
//  PARAMETERS : none
//
//  PURPOSE    : Initialize the PlasmaConstants module
//
void PythonInterface::AddPlasmaConstantsClasses()
{
    pyEnum::AddPlasmaConstantsClasses(plasmaConstantsMod);

    cyAvatar::AddPlasmaConstantsClasses(plasmaConstantsMod);
    cyMisc::AddPlasmaConstantsClasses(plasmaConstantsMod);
    cyAccountManagement::AddPlasmaConstantsClasses(plasmaConstantsMod);
    
    //pyDrawControl::AddPlasmaConstantsClasses(plasmaConstantsMod);
    pyDynamicText::AddPlasmaConstantsClasses(plasmaConstantsMod);
    pyGUIControlButton::AddPlasmaConstantsClasses(plasmaConstantsMod);
    pyGUIControlMultiLineEdit::AddPlasmaConstantsClasses(plasmaConstantsMod);
    pyJournalBook::AddPlasmaConstantsClasses(plasmaConstantsMod);
    pyMarkerMgr::AddPlasmaConstantsClasses(plasmaConstantsMod);
    pyMoviePlayer::AddPlasmaConstantsClasses(plasmaConstantsMod);
    pyNotify::AddPlasmaConstantsClasses(plasmaConstantsMod);
    pySDL::AddPlasmaConstantsClasses(plasmaConstantsMod);
    pyStatusLog::AddPlasmaConstantsClasses(plasmaConstantsMod);
    pyScoreMgr::AddPlasmaConstantsClasses(plasmaConstantsMod);

    pyAIMsg::AddPlasmaConstantsClasses(plasmaConstantsMod);

    // TODO: put these constants here. remove them from below.
    //pyNetLinkingMgr::AddPlasmaConstantsClasses(plasmaConstantsMod);
    //pyVault::AddPlasmaConstantsClasses(plasmaConstantsMod);
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddPlasmaNetConstantsClasses
//  PARAMETERS : none
//
//  PURPOSE    : Initialize the PlasmaNetConstants module
//
void PythonInterface::AddPlasmaNetConstantsClasses()
{
    pyNetLinkingMgr::AddPlasmaConstantsClasses(plasmaNetConstantsMod);
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddPlasmaVaultConstantsClasses
//  PARAMETERS : none
//
//  PURPOSE    : Initialize the PlasmaVaultConstants module
//
void PythonInterface::AddPlasmaVaultConstantsClasses()
{
    pyVault::AddPlasmaConstantsClasses(plasmaVaultConstantsMod);
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddPlasmaGameMethods
//  PARAMETERS : none
//
//  PURPOSE    : Add global methods to the PlasmaGame module
//
void PythonInterface::AddPlasmaGameMethods(std::vector<PyMethodDef> &methods)
{
    // General
    pyGameCli::AddPlasmaMethods(methods);

    // TicTacToe game
    pyTTTGame::AddPlasmaMethods(methods);

    // Heek game
    pyHeekGame::AddPlasmaMethods(methods);

    // Marker game
    pyMarkerGame::AddPlasmaMethods(methods);

    // Blue Spiral game
    pyBlueSpiralGame::AddPlasmaMethods(methods);

    // Climbing Wall game
    pyClimbingWallGame::AddPlasmaMethods(methods);

    // Variable Sync game
    pyVarSyncGame::AddPlasmaMethods(methods);
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddPlasmaGameClasses
//  PARAMETERS : none
//
//  PURPOSE    : Initialize the PlasmaGame module
//
void PythonInterface::AddPlasmaGameClasses()
{
    // General
    pyGameMgrMsg::AddPlasmaClasses(plasmaGameMod);
    pyGameMgrInviteReceivedMsg::AddPlasmaClasses(plasmaGameMod);
    pyGameMgrInviteRevokedMsg::AddPlasmaClasses(plasmaGameMod);
    pyGameCliMsg::AddPlasmaClasses(plasmaGameMod);
    pyGameCliPlayerJoinedMsg::AddPlasmaClasses(plasmaGameMod);
    pyGameCliPlayerLeftMsg::AddPlasmaClasses(plasmaGameMod);
    pyGameCliInviteFailedMsg::AddPlasmaClasses(plasmaGameMod);
    pyGameCliOwnerChangeMsg::AddPlasmaClasses(plasmaGameMod);
    pyGameCli::AddPlasmaClasses(plasmaGameMod);

    // TicTacToe game
    pyTTTMsg::AddPlasmaClasses(plasmaGameMod);
    pyTTTGameStartedMsg::AddPlasmaClasses(plasmaGameMod);
    pyTTTGameOverMsg::AddPlasmaClasses(plasmaGameMod);
    pyTTTMoveMadeMsg::AddPlasmaClasses(plasmaGameMod);
    pyTTTGame::AddPlasmaClasses(plasmaGameMod);

    // Heek game
    pyHeekMsg::AddPlasmaClasses(plasmaGameMod);
    pyHeekPlayGameMsg::AddPlasmaClasses(plasmaGameMod);
    pyHeekGoodbyeMsg::AddPlasmaClasses(plasmaGameMod);
    pyHeekWelcomeMsg::AddPlasmaClasses(plasmaGameMod);
    pyHeekDropMsg::AddPlasmaClasses(plasmaGameMod);
    pyHeekSetupMsg::AddPlasmaClasses(plasmaGameMod);
    pyHeekLightStateMsg::AddPlasmaClasses(plasmaGameMod);
    pyHeekInterfaceStateMsg::AddPlasmaClasses(plasmaGameMod);
    pyHeekCountdownStateMsg::AddPlasmaClasses(plasmaGameMod);
    pyHeekWinLoseMsg::AddPlasmaClasses(plasmaGameMod);
    pyHeekGameWinMsg::AddPlasmaClasses(plasmaGameMod);
    pyHeekPointUpdateMsg::AddPlasmaClasses(plasmaGameMod);
    pyHeekGame::AddPlasmaClasses(plasmaGameMod);

    // Marker game
    pyMarkerMsg::AddPlasmaClasses(plasmaGameMod);
    pyMarkerTemplateCreatedMsg::AddPlasmaClasses(plasmaGameMod);
    pyMarkerTeamAssignedMsg::AddPlasmaClasses(plasmaGameMod);
    pyMarkerGameTypeMsg::AddPlasmaClasses(plasmaGameMod);
    pyMarkerGameStartedMsg::AddPlasmaClasses(plasmaGameMod);
    pyMarkerGamePausedMsg::AddPlasmaClasses(plasmaGameMod);
    pyMarkerGameResetMsg::AddPlasmaClasses(plasmaGameMod);
    pyMarkerGameOverMsg::AddPlasmaClasses(plasmaGameMod);
    pyMarkerGameNameChangedMsg::AddPlasmaClasses(plasmaGameMod);
    pyMarkerTimeLimitChangedMsg::AddPlasmaClasses(plasmaGameMod);
    pyMarkerGameDeletedMsg::AddPlasmaClasses(plasmaGameMod);
    pyMarkerMarkerAddedMsg::AddPlasmaClasses(plasmaGameMod);
    pyMarkerMarkerDeletedMsg::AddPlasmaClasses(plasmaGameMod);
    pyMarkerMarkerNameChangedMsg::AddPlasmaClasses(plasmaGameMod);
    pyMarkerMarkerCapturedMsg::AddPlasmaClasses(plasmaGameMod);
    pyMarkerGame::AddPlasmaClasses(plasmaGameMod);

    // Blue Spiral game
    pyBlueSpiralMsg::AddPlasmaClasses(plasmaGameMod);
    pyBlueSpiralClothOrderMsg::AddPlasmaClasses(plasmaGameMod);
    pyBlueSpiralSuccessfulHitMsg::AddPlasmaClasses(plasmaGameMod);
    pyBlueSpiralGameWonMsg::AddPlasmaClasses(plasmaGameMod);
    pyBlueSpiralGameOverMsg::AddPlasmaClasses(plasmaGameMod);
    pyBlueSpiralGameStartedMsg::AddPlasmaClasses(plasmaGameMod);
    pyBlueSpiralGame::AddPlasmaClasses(plasmaGameMod);

    // Climbing Wall game
    pyClimbingWallMsg::AddPlasmaClasses(plasmaGameMod);
    pyClimbingWallNumBlockersChangedMsg::AddPlasmaClasses(plasmaGameMod);
    pyClimbingWallReadyMsg::AddPlasmaClasses(plasmaGameMod);
    pyClimbingWallBlockersChangedMsg::AddPlasmaClasses(plasmaGameMod);
    pyClimbingWallPlayerEnteredMsg::AddPlasmaClasses(plasmaGameMod);
    pyClimbingWallSuitMachineLockedMsg::AddPlasmaClasses(plasmaGameMod);
    pyClimbingWallGameOverMsg::AddPlasmaClasses(plasmaGameMod);
    pyClimbingWallGame::AddPlasmaClasses(plasmaGameMod);

    // Variable Sync game
    pyVarSyncMsg::AddPlasmaClasses(plasmaGameMod);
    pyVarSyncStringVarChangedMsg::AddPlasmaClasses(plasmaGameMod);
    pyVarSyncNumericVarChangedMsg::AddPlasmaClasses(plasmaGameMod);
    pyVarSyncAllVarsSentMsg::AddPlasmaClasses(plasmaGameMod);
    pyVarSyncStringVarCreatedMsg::AddPlasmaClasses(plasmaGameMod);
    pyVarSyncNumericVarCreatedMsg::AddPlasmaClasses(plasmaGameMod);
    pyVarSyncGame::AddPlasmaClasses(plasmaGameMod);
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : AddPlasmaGameConstantsClasses
//  PARAMETERS : none
//
//  PURPOSE    : Initialize the PlasmaGameConstants module
//
void PythonInterface::AddPlasmaGameConstantsClasses()
{
    // General
    pyGameMgrMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod);
    pyGameCliMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod);
    pyGameCliInviteFailedMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod);

    // TicTacToe game
    pyTTTMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod);
    pyTTTGame::AddPlasmaConstantsClasses(plasmaGameConstantsMod);

    // Heek game
    pyHeekMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod);
    pyHeekLightStateMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod);
    pyHeekCountdownStateMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod);
    pyHeekGame::AddPlasmaConstantsClasses(plasmaGameConstantsMod);

    // Marker game
    pyMarkerMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod);
    pyMarkerGame::AddPlasmaConstantsClasses(plasmaGameConstantsMod);

    // Blue Spiral game
    pyBlueSpiralMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod);

    // Climbing Wall game
    pyClimbingWallMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod);
    pyClimbingWallGame::AddPlasmaConstantsClasses(plasmaGameConstantsMod);

    // Variable Sync game
    pyVarSyncMsg::AddPlasmaConstantsClasses(plasmaGameConstantsMod);
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : finiPython
//  PARAMETERS : none
//
//  PURPOSE    : Finalize the Python dll, ie. get ready to shut down
//
void PythonInterface::finiPython()
{
    // decrement the number of initializations, on last one do the finalization
    initialized--;
    if ( initialized < 1 && Py_IsInitialized() != 0 && IsInShutdown )
    {
#if defined(HAVE_CYPYTHONIDE) && !defined(PLASMA_EXTERNAL_RELEASE)
        if (usePythonDebugger)
            debugServer.Disconnect();
#endif
        // remove debug module if used
        if ( dbgMod )
        {
            Py_DECREF(dbgMod);
            dbgMod = nil;
        }

        if ( stdOut )
        {
            Py_DECREF(stdOut);
            stdOut = nil;
        }

        if ( stdErr )
        {
            Py_DECREF(stdErr);
            stdErr = nil;
        }

        if ( plasmaMod )
        {
            Py_DECREF(plasmaMod);   // get rid of our reference
            plasmaMod = nil;
        }

        if ( plasmaConstantsMod )
        {
            Py_DECREF(plasmaConstantsMod);
            plasmaConstantsMod = nil;
        }

        if ( plasmaNetConstantsMod )
        {
            Py_DECREF(plasmaNetConstantsMod);
            plasmaNetConstantsMod = nil;
        }

        if ( plasmaVaultConstantsMod )
        {
            Py_DECREF(plasmaVaultConstantsMod);
            plasmaVaultConstantsMod = nil;
        }

        if ( plasmaGameMod )
        {
            Py_DECREF(plasmaGameMod);
            plasmaGameMod = nil;
        }

        if ( plasmaGameConstantsMod )
        {
            Py_DECREF(plasmaGameConstantsMod);
            plasmaGameConstantsMod = nil;
        }

        // let Python clean up after itself
        Py_Finalize();

        if (plasmaMethods)
        {
            delete [] plasmaMethods;
            plasmaMethods = nil;
        }

        if (plasmaGameMethods)
        {
            delete [] plasmaGameMethods;
            plasmaGameMethods = nil;
        }

        // close done the log file, if we created one
        if ( dbgLog != nil )
        {
            delete dbgLog;
            dbgLog = nil;
        }

        initialized = 0;
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : debugTimeSlice
//  PARAMETERS : none
//
//  PURPOSE    : give the debug window a time slice
//
void PythonInterface::debugTimeSlice()
{
    // check to see if the debug python module is loaded
    if ( dbgSlice != nil )
    {
        // then send it the new text
        PyObject* retVal = PyObject_CallFunction(dbgSlice,nil);
        if ( retVal == nil )
        {
            // for some reason this function didn't, remember that and not call it again
            dbgSlice = nil;
            // if there was an error make sure that the stderr gets flushed so it can be seen
            PyErr_Print();      // make sure the error is printed
            PyErr_Clear();      // clear the error
        }
        Py_XDECREF(retVal);
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetStdOut
//  PARAMETERS : none
//
//  PURPOSE    : get the stdOut python object
//
PyObject* PythonInterface::GetStdOut()
{
    return stdOut;
}

PyObject* PythonInterface::GetStdErr()
{
    return stdErr;
}
    
/////////////////////////////////////////////////////////////////////////////
//
//  Function   : getOutputAndReset
//  PARAMETERS : none
//
//  PURPOSE    : get the Output to the error file to be displayed
//
int PythonInterface::getOutputAndReset(std::string *output)
{
    if (stdOut != nil)
    {
        std::string strVal = pyOutputRedirector::GetData(stdOut);
        int size = strVal.length();
        dbgLog->AddLine(strVal.c_str());

        // reset the file back to zero
        pyOutputRedirector::ClearData(stdOut);

        // tell python debugger
#if defined(HAVE_CYPYTHONIDE) && !defined(PLASMA_EXTERNAL_RELEASE)
        if (UsePythonDebugger())
            PythonInterface::PythonDebugger()->StdOut(strVal);
#endif

        // check to see if the debug python module is loaded
        if ( dbgOut != nil )
        {
            // then send it the new text
            PyObject* retVal = PyObject_CallFunction(dbgOut,"s",strVal.c_str());
            if ( retVal == nil )
            {
                // for some reason this function didn't, remember that and not call it again
                dbgOut = nil;
                // if there was an error make sure that the stderr gets flushed so it can be seen
                PyErr_Print();      // make sure the error is printed
                PyErr_Clear();      // clear the error
            }
            Py_XDECREF(retVal);
        }

        if (output)
            (*output) = strVal;
        return size;
    }
    return 0;
}

void PythonInterface::WriteToLog(const char* text)
{
    dbgLog->AddLine(text);
}

void PythonInterface::WriteToStdErr(const char* text)
{
    PyObject* stdErr = PythonInterface::GetStdErr();
    if (stdErr && pyErrorRedirector::Check(stdErr))
    {
        pyErrorRedirector *obj = pyErrorRedirector::ConvertFrom(stdErr);
        obj->Write(text);
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : FindModule
//  PARAMETERS : module    - module name to find
//
//  PURPOSE    : Find module. If it doesn't exist then don't create, return nil.
//
PyObject* PythonInterface::FindModule(const char* module)
{
    PyObject *m;
    // first we must get rid of any old modules of the same name, we'll replace it
    PyObject *modules = PyImport_GetModuleDict();
    if ((m = PyDict_GetItemString(modules, module)) != NULL && PyModule_Check(m))
        // just return what we found
        return m;

    // couldn't find the module, return None (sorta)
    return nil;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : IsModuleNameUnique
//  PARAMETERS : module    - module name to create
//
//  PURPOSE    : Test to see if the module name is unique
//
//  Returns    : True if unique , otherwise returns False
//
hsBool PythonInterface::IsModuleNameUnique(char* module)
{
    PyObject *m;
    // first we must get rid of any old modules of the same name, we'll replace it
    PyObject *modules = PyImport_GetModuleDict();
    if ((m = PyDict_GetItemString(modules, module)) != NULL && PyModule_Check(m))
    {
        return false;
    }
    return true;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : CreateModule
//  PARAMETERS : module    - module name to create
//
//  PURPOSE    : create a new module with built-ins
//
PyObject* PythonInterface::CreateModule(const char* module)
{
    PyObject *m, *d;
    // first we must get rid of any old modules of the same name, we'll replace it
    PyObject *modules = PyImport_GetModuleDict();
    if ((m = PyDict_GetItemString(modules, module)) != NULL && PyModule_Check(m))
    {
        // clear it
        char message[256];
        sprintf(message,"ERROR! Creating a python module of the same name - %s",module);
        hsAssert(false,message);
        _PyModule_Clear(m);
    }

    // create the module
    m = PyImport_AddModule(module);
    if (m == NULL)
        return nil;
    d = PyModule_GetDict(m);
    // add in the built-ins
    // first make sure that we don't already have the builtins
    if (PyDict_GetItemString(d, "__builtins__") == NULL)
    {
        // if we need the builtins then find the builtin module
        PyObject *bimod = PyImport_ImportModule("__builtin__");
        // then add the builtin dicitionary to our module's dictionary
        if (bimod == NULL || PyDict_SetItemString(d, "__builtins__", bimod) != 0)
            return nil;
        Py_DECREF(bimod);
    }
    return m;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetPlasmaItem
//  PARAMETERS : item    - what item in the plasma module to get
//
//  PURPOSE    : get an item (probably a function) from the Plasma module
//
PyObject* PythonInterface::GetPlasmaItem(char* item)
{
    if ( plasmaMod )
    {
        PyObject* d = PyModule_GetDict(plasmaMod);
        return PyDict_GetItemString(d, item);
    }
    return nil;
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetModuleItem
//  PARAMETERS : item    - what item in the plasma module to get
//
//  PURPOSE    : get an item (probably a function) from a specific module
//
PyObject* PythonInterface::GetModuleItem(char* item, PyObject* module)
{
    if ( module )
    {
        PyObject* d = PyModule_GetDict(module);
        return PyDict_GetItemString(d, item);
    }

    return nil;
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : CheckModuleForFunctions
//  PARAMETERS : module    - module to check for
//
//  PURPOSE    : checks to see if a specific function is defined in this module
//
void PythonInterface::CheckModuleForFunctions(PyObject* module, char** funcNames, PyObject** funcTable)
{
    PyObject *dict;
    // get the dictionary for this module
    dict = PyModule_GetDict(module);
    // start looking for the functions
    int i=0;
    while ( funcNames[i] != nil )
    {
        PyObject* func = PyDict_GetItemString(dict, funcNames[i]);
        if ( func != NULL && PyCallable_Check(func)>0 )
        {
            // if it is defined then mark the funcTable
            funcTable[i] = func;
        }
        else    // else we couldn't find the funtion
        {
            funcTable[i] = nil;
        }
        i++;
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : CheckInstanceForFunctions
//  PARAMETERS : instance    - instance of a class to check
//
//  PURPOSE    : checks to see if a specific function is defined in this instance of a class
//             : and will fill out the funcTable with object instances of where the funciton is
//
void PythonInterface::CheckInstanceForFunctions(PyObject* instance, char** funcNames, PyObject** funcTable)
{
    // start looking for the functions
    int i=0;
    while ( funcNames[i] != nil )
    {
        PyObject* func = PyObject_GetAttrString(instance, funcNames[i]);
        if ( func != NULL )
        {
            if ( PyCallable_Check(func)>0 )
            {
                // if it is defined then mark the funcTable
                funcTable[i] = instance;
            }
            Py_DECREF(func);
        }
        i++;
    }
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : CompileString
//  PARAMETERS : command       - string of commands to execute in the...
//             : filename      - filename to say where to code came from
//
//  PURPOSE    : run a python string in a specific module name
//
PyObject* PythonInterface::CompileString(char *command, char* filename)
{
    PyObject* pycode = Py_CompileString(command, filename, Py_file_input);
    return pycode;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : DumpObject
//  PARAMETERS : pyobject       - string of commands to execute in the...
//
//  PURPOSE    : marshals an object into a char string
//
hsBool PythonInterface::DumpObject(PyObject* pyobj, char** pickle, int32_t* size)
{
    PyObject *s;        // the python string object where the marsalled object wil go
    // convert object to a marshalled string python object
#if (PY_MAJOR_VERSION == 2) && (PY_MINOR_VERSION < 4)
    s = PyMarshal_WriteObjectToString(pyobj);
#else
    s = PyMarshal_WriteObjectToString(pyobj, Py_MARSHAL_VERSION);
#endif

    // did it actually do it?
    if ( s != NULL )
    {
        // yes, then get the size and the string address
        *size = PyString_Size(s);
        *pickle =  PyString_AsString(s);
        return true;
    }
    else  // otherwise, there was an error
    {
        // Yikes! errors!
        PyErr_Print();  // FUTURE: we may have to get the string to display in max...later
        return false;
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : LoadObject
//  PARAMETERS : pickle    - the pickled object in char string form
//             : size      - size of the guts to load into an object
//
//  PURPOSE    : Load a python object from a pickled object
//
PyObject* PythonInterface::LoadObject(char* pickle, int32_t size)
{
    return PyMarshal_ReadObjectFromString(pickle, size);
}


/////////////////////////////////////////////////////////////////////////////
//
//  Function   : RunStringInteractive
//  PARAMETERS : command   - string of commands to execute in the...
//             : module    - module name to run 'command' in
//
//  PURPOSE    : run a python string in a specific module name
//
//  RETURNS    : pointer to PyObject that is the result of the command
//
hsBool PythonInterface::RunStringInteractive(char *command, PyObject* module)
{
    PyObject *d, *v;
    // make sure that we're given a good module... or at least one with an address
    if ( !module )
    {
        // if no module was given then use just use the main module
        module = PyImport_AddModule("__main__");
        if (module == NULL)
            return false;
    }
    // get the dictionaries for this module
    d = PyModule_GetDict(module);
    // run the string
    v = PyRun_String(command, Py_single_input, d, d);
    // check for errors and print them
    if (v == NULL)
    {
        // Yikes! errors!
        PyErr_Print();
        return false;
    }
    Py_DECREF(v);
    if (Py_FlushLine())
        PyErr_Clear();
    return true;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : RunString
//  PARAMETERS : command   - string of commands to execute in the...
//             : module    - module name to run 'command' in
//
//  PURPOSE    : run a python string in a specific module name
//
hsBool PythonInterface::RunString(char *command, PyObject* module)
{
    PyObject *d, *v;
    // make sure that we're given a good module... or at least one with an address
    if ( !module )
    {
        // if no module was given then use just use the main module
        module = PyImport_AddModule("__main__");
        if (module == NULL)
            return false;
    }
    // get the dictionaries for this module
    d = PyModule_GetDict(module);
    // run the string
    v = PyRun_String(command, Py_file_input, d, d);
    // check for errors and print them
    if (v == NULL)
    {
        // Yikes! errors!
        PyErr_Print();
        return false;
    }
    Py_DECREF(v);
    if (Py_FlushLine())
        PyErr_Clear();
    return true;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : RunPYC
//  PARAMETERS : code      - compiled code
//             : module    - module name to run the code in
//
//  PURPOSE    : run a compiled python code in a specific module name
//
hsBool PythonInterface::RunPYC(PyObject* code, PyObject* module)
{
    PyObject *d, *v;
    // make sure that we're given a good module... or at least one with an address
    if ( !module )
    {
        // if no module was given then use just use the main module
        module = PyImport_AddModule("__main__");
        if (module == NULL)
            return false;
    }
    // get the dictionaries for this module
    d = PyModule_GetDict(module);
    // run the string
    v = PyEval_EvalCode((PyCodeObject*)code, d, d);
    // check for errors and print them
    if (v == NULL)
    {
        // Yikes! errors!
        PyErr_Print();
        return false;
    }
    Py_DECREF(v);
    if (Py_FlushLine())
        PyErr_Clear();
    return true;
}

/////////////////////////////////////////////////////////////////////////////
//
//  Function   : GetpyKeyFromPython
//  PARAMETERS : pkey      - python object that is a pyKey (ptKey) class
//
//  PURPOSE    : turn a PyObject* into a pyKey*
//
pyKey* PythonInterface::GetpyKeyFromPython(PyObject* pkey)
{
    if (!pyKey::Check(pkey))
        return nil;
    return pyKey::ConvertFrom(pkey);
}
