/*==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==*/
#include "cyMisc.h"
#include "pyGlueHelpers.h"
#include "pyKey.h"
#include "pySceneObject.h"

#include <Python.h>

PYTHON_GLOBAL_METHOD_DEFINITION(PtSendPetitionToCCR, args, "Params: message,reason=0,title=\"\"\nSends a petition with a message to the CCR group")
{
    char* message;
    unsigned char reason = 0;
    char* title = nil;
    if (!PyArg_ParseTuple(args, "s|bs", &message, &reason, &title))
    {
        PyErr_SetString(PyExc_TypeError, "PtSendPetitionToCCR expects a string, and an optional unsigned 8-bit int and optional string");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::SendPetitionToCCRI(message, reason, title);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtSendChatToCCR, args, "Params: message,CCRPlayerID\nSends a chat message to a CCR that has contacted this player")
{
    char* message;
    long CCRPlayerID;
    if (!PyArg_ParseTuple(args, "sl", &message, &CCRPlayerID))
    {
        PyErr_SetString(PyExc_TypeError, "PtSendChatToCCR expects a string and a long");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::SendChatToCCR(message, CCRPlayerID);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetPythonLoggingLevel, "Returns the current level of python logging")
{
    return PyLong_FromUnsignedLong(cyMisc::GetPythonLoggingLevel());
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtSetPythonLoggingLevel, args, "Params: level\nSets the current level of python logging")
{
    unsigned long level;
    if (!PyArg_ParseTuple(args, "l", &level))
    {
        PyErr_SetString(PyExc_TypeError, "PtSetPythonLoggingLevel expects an unsigned long");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::SetPythonLoggingLevel(level);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtConsole, args, "Params: command\nThis will execute 'command' as if it were typed into the Plasma console.")
{
    char* command;
    if (!PyArg_ParseTuple(args, "s", &command))
    {
        PyErr_SetString(PyExc_TypeError, "PtConsole expects a string");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::Console(command);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtConsoleNet, args, "Params: command,netForce\nThis will execute 'command' on the console, over the network, on all clients.\n"
            "If 'netForce' is true then force command to be sent over the network.")
{
    char* command;
    char netForce;
    if (!PyArg_ParseTuple(args, "sb", &command, &netForce))
    {
        PyErr_SetString(PyExc_TypeError, "PtConsoleNet expects a string and a boolean");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::ConsoleNet(command, netForce != 0);
    PYTHON_RETURN_NONE;
}

#if 1
// TEMP
PYTHON_GLOBAL_METHOD_DEFINITION(PtPrintToScreen, args, "Params: message\nPrints 'message' to the status log, for debug only.")
{
    char* message;
    if (!PyArg_ParseTuple(args, "s", &message))
    {
        PyErr_SetString(PyExc_TypeError, "PtPrintToScreen expects a string");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::PrintToScreen(message);
    PYTHON_RETURN_NONE;
}
#endif

PYTHON_GLOBAL_METHOD_DEFINITION(PtAtTimeCallback, args, "Params: selfkey,time,id\nThis will create a timer callback that will call OnTimer when complete\n"
            "- 'selfkey' is the ptKey of the PythonFile component\n"
            "- 'time' is how much time from now (in seconds) to call back\n"
            "- 'id' is an integer id that will be returned in the OnTimer call")
{
    PyObject* keyObj = NULL;
    float time;
    unsigned long id;
    if (!PyArg_ParseTuple(args, "Ofl", &keyObj, &time, &id))
    {
        PyErr_SetString(PyExc_TypeError, "PtAtTimeCallback expects a ptKey, a float, and an unsigned long");
        PYTHON_RETURN_ERROR;
    }
    if (!pyKey::Check(keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtAtTimeCallback expects a ptKey, a float, and an unsigned long");
        PYTHON_RETURN_ERROR;
    }
    pyKey* key = pyKey::ConvertFrom(keyObj);
    cyMisc::TimerCallback(*key, time, id);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtClearTimerCallbacks, args, "Params: key\nThis will remove timer callbacks to the specified key")
{
    PyObject* keyObj = NULL;
    if (!PyArg_ParseTuple(args, "O", &keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtClearTimerCallbacks expects a ptKey");
        PYTHON_RETURN_ERROR;
    }
    if (!pyKey::Check(keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtClearTimerCallbacks expects a ptKey");
        PYTHON_RETURN_ERROR;
    }
    pyKey* key = pyKey::ConvertFrom(keyObj);
    cyMisc::ClearTimerCallbacks(*key);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtFindSceneobject, args, "Params: name,ageName\nThis will try to find a sceneobject based on its name and what age its in\n"
            "- it will return a ptSceneObject if found"
            "- if not found then a NameError exception will happen")
{
    char* name;
    char* ageName;
    if (!PyArg_ParseTuple(args, "ss", &name, &ageName))
    {
        PyErr_SetString(PyExc_TypeError, "PtFindSceneobject expects two strings");
        PYTHON_RETURN_ERROR;
    }
    return cyMisc::FindSceneObject(plString::FromUtf8(name), ageName);
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtFindActivator, args, "Params: name\nThis will try to find an activator based on its name\n"
            "- it will return a ptKey if found"
            "- it will return None if not found")
{
    char* name;
    if (!PyArg_ParseTuple(args, "s", &name))
    {
        PyErr_SetString(PyExc_TypeError, "PtFindActivator expects a string");
        PYTHON_RETURN_ERROR;
    }

    return cyMisc::FindActivator(plString::FromUtf8(name));
}

PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtClearCameraStack, cyMisc::ClearCameraStack, "Clears the camera stack")

PYTHON_GLOBAL_METHOD_DEFINITION(PtWasLocallyNotified, args, "Params: selfKey\nReturns 1 if the last notify was local or 0 if the notify originated on the network")
{
    PyObject* keyObj = NULL;
    if (!PyArg_ParseTuple(args, "O", &keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtWasLocallyNotified expects a ptKey");
        PYTHON_RETURN_NONE;
    }
    if (!pyKey::Check(keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtWasLocallyNotified expects a ptKey");
        PYTHON_RETURN_NONE;
    }
    pyKey* key = pyKey::ConvertFrom(keyObj);
    PYTHON_RETURN_BOOL(cyMisc::WasLocallyNotified(*key));
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtAttachObject, args, "Params: child,parent\nAttach child to parent based on ptKey or ptSceneobject\n"
            "- childKey is the ptKey or ptSceneobject of the one being attached\n"
            "- parentKey is the ptKey or ptSceneobject of the one being attached to\n"
            "(both arguments must be ptKeys or ptSceneobjects, you cannot mix types)")
{
    PyObject* childObj = NULL;
    PyObject* parentObj = NULL;
    if (!PyArg_ParseTuple(args, "OO", &childObj, &parentObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtAttachObject expects either two ptKeys or two ptSceneobjects");
        PYTHON_RETURN_ERROR;
    }
    if ((pyKey::Check(childObj)) && (pyKey::Check(parentObj)))
    {
        pyKey* child = pyKey::ConvertFrom(childObj);
        pyKey* parent = pyKey::ConvertFrom(parentObj);
        cyMisc::AttachObject(*child, *parent);
    }
    else if ((pySceneObject::Check(childObj)) && (pySceneObject::Check(parentObj)))
    {
        pySceneObject* child = pySceneObject::ConvertFrom(childObj);
        pySceneObject* parent = pySceneObject::ConvertFrom(parentObj);
        cyMisc::AttachObjectSO(*child, *parent);
    }
    else
    {
        PyErr_SetString(PyExc_TypeError, "PtAttachObject expects either two ptKeys or two ptSceneobjects");
        PYTHON_RETURN_ERROR;
    }
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtDetachObject, args, "Params: child,parent\nDetach child from parent based on ptKey or ptSceneobject\n"
            "- child is the ptKey or ptSceneobject of the one being detached\n"
            "- parent is the ptKey or ptSceneobject of the one being detached from\n"
            "(both arguments must be ptKeys or ptSceneobjects, you cannot mix types)")
{
    PyObject* childObj = NULL;
    PyObject* parentObj = NULL;
    if (!PyArg_ParseTuple(args, "OO", &childObj, &parentObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtDetachObject expects either two ptKeys or two ptSceneobjects");
        PYTHON_RETURN_ERROR;
    }
    if ((pyKey::Check(childObj)) && (pyKey::Check(parentObj)))
    {
        pyKey* child = pyKey::ConvertFrom(childObj);
        pyKey* parent = pyKey::ConvertFrom(parentObj);
        cyMisc::DetachObject(*child, *parent);
    }
    else if ((pySceneObject::Check(childObj)) && (pySceneObject::Check(parentObj)))
    {
        pySceneObject* child = pySceneObject::ConvertFrom(childObj);
        pySceneObject* parent = pySceneObject::ConvertFrom(parentObj);
        cyMisc::DetachObjectSO(*child, *parent);
    }
    else
    {
        PyErr_SetString(PyExc_TypeError, "PtDetachObject expects either two ptKeys or two ptSceneobjects");
        PYTHON_RETURN_ERROR;
    }
    PYTHON_RETURN_NONE;
}

/*PYTHON_GLOBAL_METHOD_DEFINITION(PtLinkToAge, args, "Params: selfKey,ageName,spawnPointName\nDEPRECIATED: Links you to the specified age and spawnpoint")
{
    PyObject* keyObj = NULL;
    char* ageName;
    char* spawnPointName;
    if (!PyArg_ParseTuple(args, "Oss", &keyObj, &ageName, &spawnPointName))
    {
        PyErr_SetString(PyExc_TypeError, "PtLinkToAge expects a ptKey, and two strings");
        PYTHON_RETURN_ERROR;
    }
    if (!pyKey::Check(keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtLinkToAge expects a ptKey, and two strings");
        PYTHON_RETURN_ERROR;
    }
    pyKey* key = pyKey::ConvertFrom(keyObj);
    cyMisc::LinkToAge(*key, ageName, spawnPointName);
    PYTHON_RETURN_NONE;
}*/

PYTHON_GLOBAL_METHOD_DEFINITION(PtDirtySynchState, args, "Params: selfKey,SDLStateName,flags\nDO NOT USE - handled by ptSDL")
{
    PyObject* keyObj = NULL;
    char* SDLStateName;
    unsigned long flags;
    if (!PyArg_ParseTuple(args, "Osl", &keyObj, &SDLStateName, &flags))
    {
        PyErr_SetString(PyExc_TypeError, "PtDirtySynchState expects a ptKey, a string, and an unsigned long");
        PYTHON_RETURN_ERROR;
    }
    if (!pyKey::Check(keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtDirtySynchState expects a ptKey, a string, and an unsigned long");
        PYTHON_RETURN_ERROR;
    }
    pyKey* key = pyKey::ConvertFrom(keyObj);
    cyMisc::SetDirtySyncState(*key, SDLStateName, flags);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtDirtySynchClients, args, "Params: selfKey,SDLStateName,flags\nDO NOT USE - handled by ptSDL")
{
    PyObject* keyObj = NULL;
    char* SDLStateName;
    unsigned long flags;
    if (!PyArg_ParseTuple(args, "Osl", &keyObj, &SDLStateName, &flags))
    {
        PyErr_SetString(PyExc_TypeError, "PtDirtySynchClients expects a ptKey, a string, and an unsigned long");
        PYTHON_RETURN_ERROR;
    }
    if (!pyKey::Check(keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtDirtySynchClients expects a ptKey, a string, and an unsigned long");
        PYTHON_RETURN_ERROR;
    }
    pyKey* key = pyKey::ConvertFrom(keyObj);
    cyMisc::SetDirtySyncStateWithClients(*key, SDLStateName, flags);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtEnableControlKeyEvents, args, "Params: selfKey\nEnable control key events to call OnControlKeyEvent(controlKey,activateFlag)")
{
    PyObject* keyObj = NULL;
    if (!PyArg_ParseTuple(args, "O", &keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtEnableControlKeyEvents expects a ptKey");
        PYTHON_RETURN_ERROR;
    }
    if (!pyKey::Check(keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtEnableControlKeyEvents expects a ptKey");
        PYTHON_RETURN_ERROR;
    }
    pyKey* key = pyKey::ConvertFrom(keyObj);
    cyMisc::EnableControlKeyEvents(*key);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtDisableControlKeyEvents, args, "Params: selfKey\nDisable the control key events from calling OnControlKeyEvent")
{
    PyObject* keyObj = NULL;
    if (!PyArg_ParseTuple(args, "O", &keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtDisableControlKeyEvents expects a ptKey");
        PYTHON_RETURN_ERROR;
    }
    if (!pyKey::Check(keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtDisableControlKeyEvents expects a ptKey");
        PYTHON_RETURN_ERROR;
    }
    pyKey* key = pyKey::ConvertFrom(keyObj);
    cyMisc::DisableControlKeyEvents(*key);
    PYTHON_RETURN_NONE;
}

PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtEnableAvatarCursorFade, cyMisc::EnableAvatarCursorFade, "Enable the avatar cursor fade")
PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtDisableAvatarCursorFade, cyMisc::DisableAvatarCursorFade, "Disable the avatar cursor fade")

PYTHON_GLOBAL_METHOD_DEFINITION(PtFadeLocalAvatar, args, "Params: fade\nFade (or unfade) the local avatar")
{
    char fade;
    if (!PyArg_ParseTuple(args, "b", &fade))
    {
        PyErr_SetString(PyExc_TypeError, "PtFadeLocalAvatar expects a boolean");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::FadeLocalPlayer(fade != 0);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtSetOfferBookMode, args, "Params: selfkey,ageFilename,ageInstanceName\nPut us into the offer book interface")
{
    PyObject* keyObj = NULL;
    char* ageFilename;
    char* ageInstanceName;
    if (!PyArg_ParseTuple(args, "Oss", &keyObj, &ageFilename, &ageInstanceName))
    {
        PyErr_SetString(PyExc_TypeError, "PtSetOfferBookMode expects a ptKey, and two strings");
        PYTHON_RETURN_ERROR;
    }
    if (!pyKey::Check(keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtSetOfferBookMode expects a ptKey, and two strings");
        PYTHON_RETURN_ERROR;
    }
    pyKey* key = pyKey::ConvertFrom(keyObj);
    cyMisc::EnableOfferBookMode(*key, ageFilename, ageInstanceName);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtSetShareSpawnPoint, args, "Params: spawnPoint\nThis sets the desired spawn point for the receiver to link to")
{
    char* spawnPoint;
    if (!PyArg_ParseTuple(args, "s", &spawnPoint))
    {
        PyErr_SetString(PyExc_TypeError, "PtSetShareSpawnPoint expects a string");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::SetShareSpawnPoint(spawnPoint);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtSetShareAgeInstanceGuid, args, "Params: instanceGuid\nThis sets the desired age instance guid for the receiver to link to")
{
    char* guidStr;
    if (!PyArg_ParseTuple(args, "s", &guidStr))
    {
        PyErr_SetString(PyExc_TypeError, "PtSetShareAgeInstanceGuid expects a string");
        PYTHON_RETURN_ERROR;
    }
    Uuid guid;
    if (!GuidFromString(guidStr, &guid))
    {
        PyErr_SetString(PyExc_TypeError, "PtSetShareAgeInstanceGuid string parameter is not a guid string");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::SetShareAgeInstanceGuid(guid);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtNotifyOffererLinkAccepted, args, "Params: offerer\nTell the offerer that we accepted the link offer")
{
    unsigned long offerer;
    if (!PyArg_ParseTuple(args, "l", &offerer))
    {
        PyErr_SetString(PyExc_TypeError, "PtNotifyOffererLinkAccepted expects an unsigned long");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::NotifyOffererPublicLinkAccepted(offerer);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtNotifyOffererLinkRejected, args, "Params: offerer\nTell the offerer that we rejected the link offer")
{
    unsigned long offerer;
    if (!PyArg_ParseTuple(args, "l", &offerer))
    {
        PyErr_SetString(PyExc_TypeError, "PtNotifyOffererLinkRejected expects an unsigned long");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::NotifyOffererPublicLinkRejected(offerer);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtNotifyOffererLinkCompleted, args, "Params: offerer\nTell the offerer that we completed the link")
{
    unsigned long offerer;
    if (!PyArg_ParseTuple(args, "l", &offerer))
    {
        PyErr_SetString(PyExc_TypeError, "PtNotifyOffererLinkCompleted expects an unsigned long");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::NotifyOffererPublicLinkCompleted(offerer);
    PYTHON_RETURN_NONE;
}

PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtClearOfferBookMode, cyMisc::DisableOfferBookMode, "Cancel the offer book interface")

PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetLocalClientID, "Returns our local client ID number")
{
    return PyInt_FromLong(cyMisc::GetLocalClientID());
}

PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtIsCCRAway, "Returns current status of CCR dept")
{
    PYTHON_RETURN_BOOL(cyMisc::IsCCRAwayStatus());
}

PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtAmCCR, "Returns true if local player is a CCR")
{
    PYTHON_RETURN_BOOL(cyMisc::AmCCR());
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtToggleAvatarClickability, args, "Params: on\nTurns on and off our avatar's clickability")
{
    char on;
    if (!PyArg_ParseTuple(args, "b", &on))
    {
        PyErr_SetString(PyExc_TypeError, "PtToggleAvatarClickability expects a boolean");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::ToggleAvatarClickability(on != 0);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtTransferParticlesToObject, args, "Params: objFrom, objTo, num\nTransfers num particles from objFrom to objTo")
{
    PyObject* objFrom = NULL;
    PyObject* objTo = NULL;
    int num;
    if (!PyArg_ParseTuple(args, "OOi", &objFrom, &objTo, &num))
    {
        PyErr_SetString(PyExc_TypeError, "PtTransferParticlesToObject expects two ptKeys and an int");
        PYTHON_RETURN_ERROR;
    }
    if ((!pyKey::Check(objFrom)) || (!pyKey::Check(objTo)))
    {
        PyErr_SetString(PyExc_TypeError, "PtTransferParticlesToObject expects two ptKeys and an int");
        PYTHON_RETURN_ERROR;
    }
    pyKey* from = pyKey::ConvertFrom(objFrom);
    pyKey* to = pyKey::ConvertFrom(objTo);
    cyMisc::TransferParticlesToKey(*from, *to, num);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtSetParticleDissentPoint, args, "Params: x, y, z, particlesys\nSets the dissent point of the particlesys to x,y,z")
{
    float x,y,z;
    PyObject* keyObj = NULL;
    if (!PyArg_ParseTuple(args, "fffO", &x, &y, &z, &keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtSetParticleDissentPoint expects three floats and a ptKey");
        PYTHON_RETURN_ERROR;
    }
    if (!pyKey::Check(keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtSetParticleDissentPoint expects three floats and a ptKey");
        PYTHON_RETURN_ERROR;
    }
    pyKey* key = pyKey::ConvertFrom(keyObj);
    cyMisc::SetParticleDissentPoint(x, y, z, *key);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtGetControlEvents, args, "Params: on, key\nRegisters or unregisters for control event messages")
{
    char on;
    PyObject* keyObj = NULL;
    if (!PyArg_ParseTuple(args, "bO", &on, &keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtGetControlEvents expects a boolean and a ptKey");
        PYTHON_RETURN_ERROR;
    }
    if (!pyKey::Check(keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtGetControlEvents expects a boolean and a ptKey");
        PYTHON_RETURN_ERROR;
    }
    pyKey* key = pyKey::ConvertFrom(keyObj);
    cyMisc::RegisterForControlEventMessages(on != 0, *key);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetLanguage, "Returns the current language as a PtLanguage enum")
{
    return PyInt_FromLong(cyMisc::GetLanguage());
}

PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtUsingUnicode, "Returns true if the current language is a unicode language (like Japanese)")
{
    PYTHON_RETURN_BOOL(cyMisc::UsingUnicode());
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtFakeLinkAvatarToObject, args, "Params: avatar,object\nPseudo-links avatar to object within the same age\n")
{
    PyObject* avatarObj = NULL;
    PyObject* objectObj = NULL;
    if (!PyArg_ParseTuple(args, "OO", &avatarObj, &objectObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtFakeLinkAvatarToObject expects two ptKeys");
        PYTHON_RETURN_ERROR;
    }
    if ((!pyKey::Check(avatarObj)) || (!pyKey::Check(objectObj)))
    {
        PyErr_SetString(PyExc_TypeError, "PtFakeLinkAvatarToObject expects two ptKeys");
        PYTHON_RETURN_ERROR;
    }
    pyKey* avatar = pyKey::ConvertFrom(avatarObj);
    pyKey* object = pyKey::ConvertFrom(objectObj);
    cyMisc::FakeLinkToObject(*avatar, *object);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtWearDefaultClothingType, args, "Params: key,type\nForces the avatar to wear the default clothing of the specified type")
{
    PyObject* keyObj = NULL;
    unsigned long type;
    if (!PyArg_ParseTuple(args, "Ol", &keyObj, &type))
    {
        PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothingType expects a ptKey and an unsigned long");
        PYTHON_RETURN_ERROR;
    }
    if (!pyKey::Check(keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtWearDefaultClothingType expects a ptKey and an unsigned long");
        PYTHON_RETURN_ERROR;
    }
    pyKey* key = pyKey::ConvertFrom(keyObj);
    cyMisc::WearDefaultClothingType(*key, type);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtFileExists, args, "Params: filename\nReturns true if the specified file exists")
{
    PyObject* filenameObj;
    if (!PyArg_ParseTuple(args, "O", &filenameObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtFileExists expects a string");
        PYTHON_RETURN_ERROR;
    }

    if (PyUnicode_Check(filenameObj))
    {
        int strLen = PyUnicode_GetSize(filenameObj);
        wchar_t* text = new wchar_t[strLen + 1];
        PyUnicode_AsWideChar((PyUnicodeObject*)filenameObj, text, strLen);
        text[strLen] = L'\0';
        bool retVal = cyMisc::FileExists(text);
        delete [] text;
        PYTHON_RETURN_BOOL(retVal);
    }
    else if (PyString_Check(filenameObj))
    {
        // we'll allow this, just in case something goes weird
        char* text = PyString_AsString(filenameObj);
        wchar_t* wText = hsStringToWString(text);
        bool retVal = cyMisc::FileExists(wText);
        delete [] wText;
        PYTHON_RETURN_BOOL(retVal);
    }
    else
    {
        PyErr_SetString(PyExc_TypeError, "PtFileExists expects a string");
        PYTHON_RETURN_ERROR;
    }
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtCreateDir, args, "Params: directory\nCreates the directory and all parent folders. Returns false on failure")
{
    PyObject* directoryObj;
    if (!PyArg_ParseTuple(args, "O", &directoryObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtCreateDir expects a string");
        PYTHON_RETURN_ERROR;
    }

    if (PyUnicode_Check(directoryObj))
    {
        int strLen = PyUnicode_GetSize(directoryObj);
        wchar_t* text = new wchar_t[strLen + 1];
        PyUnicode_AsWideChar((PyUnicodeObject*)directoryObj, text, strLen);
        text[strLen] = L'\0';
        bool retVal = cyMisc::CreateDir(text);
        delete [] text;
        PYTHON_RETURN_BOOL(retVal);
    }
    else if (PyString_Check(directoryObj))
    {
        // we'll allow this, just in case something goes weird
        char* text = PyString_AsString(directoryObj);
        wchar_t* wText = hsStringToWString(text);
        bool retVal = cyMisc::CreateDir(wText);
        delete [] wText;
        PYTHON_RETURN_BOOL(retVal);
    }
    else
    {
        PyErr_SetString(PyExc_TypeError, "PtCreateDir expects a string");
        PYTHON_RETURN_ERROR;
    }
}

PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetUserPath, "Returns the unicode path to the client's root user directory. Do NOT convert to a standard string.")
{
    std::wstring val = cyMisc::GetUserPath();
    return PyUnicode_FromWideChar(val.c_str(), val.length());
}

PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetInitPath, "Returns the unicode path to the client's init directory. Do NOT convert to a standard string.")
{
    std::wstring val = cyMisc::GetInitPath();
    return PyUnicode_FromWideChar(val.c_str(), val.length());
}

///////////////////////////////////////////////////////////////////////////
//
// AddPlasmaMethods - the python method definitions
//

void cyMisc::AddPlasmaMethods3(std::vector<PyMethodDef> &methods)
{
    PYTHON_GLOBAL_METHOD(methods, PtSendPetitionToCCR);
    PYTHON_GLOBAL_METHOD(methods, PtSendChatToCCR);

    PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetPythonLoggingLevel);
    PYTHON_GLOBAL_METHOD(methods, PtSetPythonLoggingLevel);

    PYTHON_GLOBAL_METHOD(methods, PtConsole);
    PYTHON_GLOBAL_METHOD(methods, PtConsoleNet);

#if 1
    // TEMP
    PYTHON_GLOBAL_METHOD(methods, PtPrintToScreen);
#endif

    PYTHON_GLOBAL_METHOD(methods, PtAtTimeCallback);
    PYTHON_GLOBAL_METHOD(methods, PtClearTimerCallbacks);
    
    PYTHON_GLOBAL_METHOD(methods, PtFindSceneobject);
    PYTHON_GLOBAL_METHOD(methods, PtFindActivator);
    PYTHON_BASIC_GLOBAL_METHOD(methods, PtClearCameraStack);
    PYTHON_GLOBAL_METHOD(methods, PtWasLocallyNotified);

    PYTHON_GLOBAL_METHOD(methods, PtAttachObject);
    PYTHON_GLOBAL_METHOD(methods, PtDetachObject);
    
    //PYTHON_GLOBAL_METHOD(methods, PtLinkToAge);
    
    PYTHON_GLOBAL_METHOD(methods, PtDirtySynchState);
    PYTHON_GLOBAL_METHOD(methods, PtDirtySynchClients);

    PYTHON_GLOBAL_METHOD(methods, PtEnableControlKeyEvents);
    PYTHON_GLOBAL_METHOD(methods, PtDisableControlKeyEvents);

    PYTHON_BASIC_GLOBAL_METHOD(methods, PtEnableAvatarCursorFade);
    PYTHON_BASIC_GLOBAL_METHOD(methods, PtDisableAvatarCursorFade);
    PYTHON_GLOBAL_METHOD(methods, PtFadeLocalAvatar);

    PYTHON_GLOBAL_METHOD(methods, PtSetOfferBookMode);
    PYTHON_GLOBAL_METHOD(methods, PtSetShareSpawnPoint);
    PYTHON_GLOBAL_METHOD(methods, PtSetShareAgeInstanceGuid);
    PYTHON_GLOBAL_METHOD(methods, PtNotifyOffererLinkAccepted);
    PYTHON_GLOBAL_METHOD(methods, PtNotifyOffererLinkRejected);
    PYTHON_GLOBAL_METHOD(methods, PtNotifyOffererLinkCompleted);
    PYTHON_BASIC_GLOBAL_METHOD(methods, PtClearOfferBookMode);

    PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetLocalClientID);
    
    PYTHON_GLOBAL_METHOD_NOARGS(methods, PtIsCCRAway);
    PYTHON_GLOBAL_METHOD_NOARGS(methods, PtAmCCR);

    PYTHON_GLOBAL_METHOD(methods, PtToggleAvatarClickability);
    
    PYTHON_GLOBAL_METHOD(methods, PtTransferParticlesToObject);
    PYTHON_GLOBAL_METHOD(methods, PtSetParticleDissentPoint);

    PYTHON_GLOBAL_METHOD(methods, PtGetControlEvents);
    
    PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetLanguage);
    PYTHON_GLOBAL_METHOD_NOARGS(methods, PtUsingUnicode);
    
    PYTHON_GLOBAL_METHOD(methods, PtFakeLinkAvatarToObject);
    
    PYTHON_GLOBAL_METHOD(methods, PtWearDefaultClothingType);

    PYTHON_GLOBAL_METHOD(methods, PtFileExists);
    PYTHON_GLOBAL_METHOD(methods, PtCreateDir);

    PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetUserPath);
    PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetInitPath);
}