/*==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/>.

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 "pyColor.h"
#include "pyPlayer.h"
#include "pyEnum.h"

// for enums
#include "plNetCommon\plNetCommon.h"
#include "plResMgr\plLocalization.h"
#include "plMessage\plLOSRequestMsg.h"

#include <python.h>

PYTHON_GLOBAL_METHOD_DEFINITION(PtYesNoDialog, args, "Params: selfkey,dialogMessage\nThis will display a Yes/No dialog to the user with the text dialogMessage\n"
            "This dialog _has_ to be answered by the user.\n"
            "And their answer will be returned in a Notify message.")
{
    PyObject* keyObj = NULL;
    PyObject* dialogMsgObj = NULL;
    if (!PyArg_ParseTuple(args, "OO", &keyObj, &dialogMsgObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtYesNoDialog expects a ptKey and a string or unicode string");
        PYTHON_RETURN_ERROR;
    }
    if (!pyKey::Check(keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtYesNoDialog expects a ptKey and a string or unicode string");
        PYTHON_RETURN_ERROR;
    }
    pyKey* key = pyKey::ConvertFrom(keyObj);
    if (PyUnicode_Check(dialogMsgObj))
    {
        int len = PyUnicode_GetSize(dialogMsgObj);
        wchar_t* text = TRACKED_NEW wchar_t[len + 1];
        PyUnicode_AsWideChar((PyUnicodeObject*)dialogMsgObj, text, len);
        text[len] = L'\0';
        cyMisc::YesNoDialog(*key, text);
        delete [] text;
        PYTHON_RETURN_NONE;
    }
    else if (PyString_Check(dialogMsgObj))
    {
        char* text = PyString_AsString(dialogMsgObj);
        cyMisc::YesNoDialog(*key, text);
        PYTHON_RETURN_NONE;
    }
    PyErr_SetString(PyExc_TypeError, "PtYesNoDialog expects a ptKey and a string or unicode string");
    PYTHON_RETURN_ERROR;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtRateIt, args, "Params: chronicleName,dialogPrompt,onceFlag\nShows a dialog with dialogPrompt and stores user input rating into chronicleName")
{
    char* chronicleName;
    char* dialogPrompt;
    char onceFlag;
    if (!PyArg_ParseTuple(args, "ssb", &chronicleName, &dialogPrompt, &onceFlag))
    {
        PyErr_SetString(PyExc_TypeError, "PtRateIt expects two strings and a boolean");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::RateIt(chronicleName, dialogPrompt, onceFlag != 0);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtExcludeRegionSet, args, "Params: senderKey,regionKey,state\nThis will set the state of an exclude region\n"
            "- 'senderKey' is a ptKey of the PythonFile component\n"
            "- 'regionKey' is a ptKey of the exclude region\n"
            "- 'state' is either kExRegRelease or kExRegClear")
{
    PyObject* senderObj = NULL;
    PyObject* regionObj = NULL;
    unsigned short stateVal;
    if (!PyArg_ParseTuple(args, "OOh", &senderObj, &regionObj, &stateVal))
    {
        PyErr_SetString(PyExc_TypeError, "PtExcludeRegionSet expects two ptKeys and a short");
        PYTHON_RETURN_ERROR;
    }
    if ((!pyKey::Check(senderObj)) || (!pyKey::Check(regionObj)))
    {
        PyErr_SetString(PyExc_TypeError, "PtExcludeRegionSet expects two ptKeys and a short");
        PYTHON_RETURN_ERROR;
    }
    pyKey* sender = pyKey::ConvertFrom(senderObj);
    pyKey* region = pyKey::ConvertFrom(regionObj);
    cyMisc::ExcludeRegionSet(*sender, *region, stateVal);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtExcludeRegionSetNow, args, "Params: senderKey,regionKey,state\nThis will set the state of an exclude region immediately on the server\n"
            "- 'senderKey' is a ptKey of the PythonFile component\n"
            "- 'regionKey' is a ptKey of the exclude region\n"
            "- 'state' is either kExRegRelease or kExRegClear")
{
    PyObject* senderObj = NULL;
    PyObject* regionObj = NULL;
    unsigned short stateVal;
    if (!PyArg_ParseTuple(args, "OOh", &senderObj, &regionObj, &stateVal))
    {
        PyErr_SetString(PyExc_TypeError, "PtExcludeRegionSetNow expects two ptKeys and a short");
        PYTHON_RETURN_ERROR;
    }
    if ((!pyKey::Check(senderObj)) || (!pyKey::Check(regionObj)))
    {
        PyErr_SetString(PyExc_TypeError, "PtExcludeRegionSetNow expects two ptKeys and a short");
        PYTHON_RETURN_ERROR;
    }
    pyKey* sender = pyKey::ConvertFrom(senderObj);
    pyKey* region = pyKey::ConvertFrom(regionObj);
    cyMisc::ExcludeRegionSetNow(*sender, *region, stateVal);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtAcceptInviteInGame, args, "Params: friendName,inviteKey\nSends a VaultTask to the server to perform the invite")
{
    char* friendName;
    char* inviteKey;
    if (!PyArg_ParseTuple(args, "ss", &friendName, &inviteKey))
    {
        PyErr_SetString(PyExc_TypeError, "PtAcceptInviteInGame expects two strings");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::AcceptInviteInGame(friendName, inviteKey);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetTime, "Returns the number of seconds since the game was started.")
{
    return PyFloat_FromDouble(cyMisc::GetSeconds());
}

PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetGameTime, "Returns the system game time (frame based) in seconds.")
{
    return PyFloat_FromDouble(cyMisc::GetSysSeconds());
}

PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtGetFrameDeltaTime, "Returns the amount of time that has elapsed since last frame.")
{
    return PyFloat_FromDouble(cyMisc::GetDelSysSeconds());
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtPageInNode, args, "Params: nodeName, ageName=\"\"\nPages in node, or a list of nodes")
{
    PyObject* nodeNameObj = NULL;
    char* ageName = NULL;
    if (!PyArg_ParseTuple(args, "O|s", &nodeNameObj, &ageName))
    {
        PyErr_SetString(PyExc_TypeError, "PtPageInNode expects a string or list of strings, and optionally a string");
        PYTHON_RETURN_ERROR;
    }
    std::vector<std::string> nodeNames;
    if (PyString_Check(nodeNameObj))
    {
        nodeNames.push_back(PyString_AsString(nodeNameObj));
    }
    else if (PyList_Check(nodeNameObj))
    {
        int num = PyList_Size(nodeNameObj);
        for (int i = 0; i < num; i++)
        {
            PyObject* listItem = PyList_GetItem(nodeNameObj, i);
            if (!PyString_Check(listItem))
            {
                PyErr_SetString(PyExc_TypeError, "PtPageInNode expects a string or list of strings, and optionally a string");
                PYTHON_RETURN_ERROR;
            }
            nodeNames.push_back(PyString_AsString(listItem));
        }
    }
    else
    {
        PyErr_SetString(PyExc_TypeError, "PtPageInNode expects a string or list of strings, and optionally a string");
        PYTHON_RETURN_ERROR;
    }

    cyMisc::PageInNodes(nodeNames, ageName);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtPageOutNode, args, "Params: nodeName\nPages out a node")
{
    char* nodeName;
    if (!PyArg_ParseTuple(args, "s", &nodeName))
    {
        PyErr_SetString(PyExc_TypeError, "PtPageOutNode expects a string");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::PageOutNode(nodeName);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtLimitAvatarLOD, args, "Params: LODlimit\nSets avatar's LOD limit")
{
    int lodLimit;
    if (!PyArg_ParseTuple(args, "i", &lodLimit))
    {
        PyErr_SetString(PyExc_TypeError, "PtLimitAvatarLOD expects an integer");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::LimitAvatarLOD(lodLimit);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtFogSetDefColor, args, "Params: color\nSets default fog color")
{
    PyObject* colorObj = NULL;
    if (!PyArg_ParseTuple(args, "O", &colorObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtFogSetDefColor expects a ptColor object");
        PYTHON_RETURN_ERROR;
    }
    if (!pyColor::Check(colorObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtFogSetDefColor expects a ptColor object");
        PYTHON_RETURN_ERROR;
    }
    pyColor* color = pyColor::ConvertFrom(colorObj);
    cyMisc::FogSetDefColor(*color);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtFogSetDefLinear, args, "Params: start,end,density\nSet linear fog values")
{
    float start, end, density;
    if (!PyArg_ParseTuple(args, "fff", &start, &end, &density))
    {
        PyErr_SetString(PyExc_TypeError, "PtFogSetDefLinear expects three floats");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::FogSetDefLinear(start, end, density);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtFogSetDefExp, args, "Params: end,density\nSet exp fog values")
{
    float end, density;
    if (!PyArg_ParseTuple(args, "ff", &end, &density))
    {
        PyErr_SetString(PyExc_TypeError, "PtFogSetDefExp expects three floats");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::FogSetDefExp(end, density);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtFogSetDefExp2, args, "Params: end,density\nSet exp2 fog values")
{
    float end, density;
    if (!PyArg_ParseTuple(args, "ff", &end, &density))
    {
        PyErr_SetString(PyExc_TypeError, "PtFogSetDefExp2 expects three floats");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::FogSetDefExp2(end, density);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtLoadDialog, args, "Params: dialogName,selfKey=None,ageName=\"\"\nLoads a GUI dialog by name and optionally set the Notify proc key\n"
            "If the dialog is already loaded then it won't load it again")
{
    char* dialogName;
    PyObject* keyObj = NULL;
    char* ageName = NULL;
    if (!PyArg_ParseTuple(args, "s|Os", &dialogName, &keyObj, &ageName))
    {
        PyErr_SetString(PyExc_TypeError, "PtLoadDialog expects a string, and optionally a ptKey and second string");
        PYTHON_RETURN_ERROR;
    }
    if (keyObj)
    {
        if (!pyKey::Check(keyObj))
        {
            PyErr_SetString(PyExc_TypeError, "PtLoadDialog expects a string, and optionally a ptKey and second string");
            PYTHON_RETURN_ERROR;
        }
        pyKey* key = pyKey::ConvertFrom(keyObj);
        if (ageName)
            cyMisc::LoadDialogKA(dialogName, *key, ageName);
        else
            cyMisc::LoadDialogK(dialogName, *key);
    }
    else
        cyMisc::LoadDialog(dialogName);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtUnloadDialog, args, "Params: dialogName\nThis will unload the GUI dialog by name. If not loaded then nothing will happen")
{
    char* dialogName;
    if (!PyArg_ParseTuple(args, "s", &dialogName))
    {
        PyErr_SetString(PyExc_TypeError, "PtUnloadDialog expects a string");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::UnloadDialog(dialogName);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtIsDialogLoaded, args, "Params: dialogName\nTest to see if a GUI dialog is loaded, by name")
{
    char* dialogName;
    if (!PyArg_ParseTuple(args, "s", &dialogName))
    {
        PyErr_SetString(PyExc_TypeError, "PtIsDialogLoaded expects a string");
        PYTHON_RETURN_ERROR;
    }
    PYTHON_RETURN_BOOL(cyMisc::IsDialogLoaded(dialogName));
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtShowDialog, args, "Params: dialogName\nShow a GUI dialog by name (does not load dialog)")
{
    char* dialogName;
    if (!PyArg_ParseTuple(args, "s", &dialogName))
    {
        PyErr_SetString(PyExc_TypeError, "PtShowDialog expects a string");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::ShowDialog(dialogName);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtHideDialog, args, "Params: dialogName\nHide a GUI dialog by name (does not unload dialog)")
{
    char* dialogName;
    if (!PyArg_ParseTuple(args, "s", &dialogName))
    {
        PyErr_SetString(PyExc_TypeError, "PtHideDialog expects a string");
        PYTHON_RETURN_ERROR;
    }
    cyMisc::HideDialog(dialogName);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtGetDialogFromTagID, args, "Params: tagID\nReturns the dialog associated with the tagID")
{
    unsigned long tagID;
    if (!PyArg_ParseTuple(args, "l", &tagID))
    {
        PyErr_SetString(PyExc_TypeError, "PtGetDialogFromTagID expects a long");
        PYTHON_RETURN_ERROR;
    }
    return cyMisc::GetDialogFromTagID(tagID);
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtGetDialogFromString, args, "Params: dialogName\nGet a ptGUIDialog from its name")
{
    char* dialogName;
    if (!PyArg_ParseTuple(args, "s", &dialogName))
    {
        PyErr_SetString(PyExc_TypeError, "PtHideDialog expects a string");
        PYTHON_RETURN_ERROR;
    }
    return cyMisc::GetDialogFromString(dialogName);
}

PYTHON_GLOBAL_METHOD_DEFINITION_NOARGS(PtIsGUIModal, "Returns true if the GUI is displaying a modal dialog and blocking input")
{
    PYTHON_RETURN_BOOL(cyMisc::IsGUIModal());
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtSendPrivateChatList, args, "Params: chatList\nLock the local avatar into private vox messaging, and / or add new members to his chat list")
{
    PyObject* chatListObj = NULL;
    if (!PyArg_ParseTuple(args, "O", &chatListObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtSendPrivateChatList expects a list of ptPlayers");
        PYTHON_RETURN_ERROR;
    }

    std::vector<pyPlayer*> chatList;
    if (PyList_Check(chatListObj))
    {
        int listSize = PyList_Size(chatListObj);
        for (int i = 0; i < listSize; i++)
        {
            PyObject* listItem = PyList_GetItem(chatListObj, i);
            if (!pyPlayer::Check(listItem))
            {
                PyErr_SetString(PyExc_TypeError, "PtSendPrivateChatList expects a list of ptPlayers");
                PYTHON_RETURN_ERROR;
            }
            chatList.push_back(pyPlayer::ConvertFrom(listItem));
        }
    }
    else
    {
        PyErr_SetString(PyExc_TypeError, "PtSendPrivateChatList expects a list of ptPlayers");
        PYTHON_RETURN_ERROR;
    }

    cyMisc::SetPrivateChatList(chatList);
    PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtClearPrivateChatList, args, "Params: memberKey\nRemove the local avatar from private vox messaging, and / or clear members from his chat list")
{
    PyObject* keyObj = NULL;
    if (!PyArg_ParseTuple(args, "O", &keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtClearPrivateChatList expects a ptKey");
        PYTHON_RETURN_ERROR;
    }
    if (!pyKey::Check(keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "PtClearPrivateChatList expects a ptKey");
        PYTHON_RETURN_ERROR;
    }
    pyKey* key = pyKey::ConvertFrom(keyObj);
    cyMisc::ClearPrivateChatList(*key);
    PYTHON_RETURN_NONE;
}

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

void cyMisc::AddPlasmaMethods2(std::vector<PyMethodDef> &methods)
{
    PYTHON_GLOBAL_METHOD(methods, PtYesNoDialog);
    PYTHON_GLOBAL_METHOD(methods, PtRateIt);
    
    PYTHON_GLOBAL_METHOD(methods, PtExcludeRegionSet);
    PYTHON_GLOBAL_METHOD(methods, PtExcludeRegionSetNow);

    PYTHON_GLOBAL_METHOD(methods, PtAcceptInviteInGame);

    PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetTime);
    PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetGameTime);
    PYTHON_GLOBAL_METHOD_NOARGS(methods, PtGetFrameDeltaTime);

    PYTHON_GLOBAL_METHOD(methods, PtPageInNode);
    PYTHON_GLOBAL_METHOD(methods, PtPageOutNode);
    
    PYTHON_GLOBAL_METHOD(methods, PtLimitAvatarLOD);

    PYTHON_GLOBAL_METHOD(methods, PtFogSetDefColor);
    PYTHON_GLOBAL_METHOD(methods, PtFogSetDefLinear);
    PYTHON_GLOBAL_METHOD(methods, PtFogSetDefExp);
    PYTHON_GLOBAL_METHOD(methods, PtFogSetDefExp2);

    PYTHON_GLOBAL_METHOD(methods, PtLoadDialog);
    PYTHON_GLOBAL_METHOD(methods, PtUnloadDialog);
    PYTHON_GLOBAL_METHOD(methods, PtIsDialogLoaded);
    PYTHON_GLOBAL_METHOD(methods, PtShowDialog);
    PYTHON_GLOBAL_METHOD(methods, PtHideDialog);
    PYTHON_GLOBAL_METHOD(methods, PtGetDialogFromTagID);
    PYTHON_GLOBAL_METHOD(methods, PtGetDialogFromString);
    PYTHON_GLOBAL_METHOD_NOARGS(methods, PtIsGUIModal);

    PYTHON_GLOBAL_METHOD(methods, PtSendPrivateChatList);
    PYTHON_GLOBAL_METHOD(methods, PtClearPrivateChatList);
}

void cyMisc::AddPlasmaConstantsClasses(PyObject *m)
{
    PYTHON_ENUM_START(PtCCRPetitionType);
    PYTHON_ENUM_ELEMENT(PtCCRPetitionType, kGeneralHelp,plNetCommon::PetitionTypes::kGeneralHelp);
    PYTHON_ENUM_ELEMENT(PtCCRPetitionType, kBug,        plNetCommon::PetitionTypes::kBug);
    PYTHON_ENUM_ELEMENT(PtCCRPetitionType, kFeedback,   plNetCommon::PetitionTypes::kFeedback);
    PYTHON_ENUM_ELEMENT(PtCCRPetitionType, kExploit,    plNetCommon::PetitionTypes::kExploit);
    PYTHON_ENUM_ELEMENT(PtCCRPetitionType, kHarass,     plNetCommon::PetitionTypes::kHarass);
    PYTHON_ENUM_ELEMENT(PtCCRPetitionType, kStuck,      plNetCommon::PetitionTypes::kStuck);
    PYTHON_ENUM_ELEMENT(PtCCRPetitionType, kTechnical,  plNetCommon::PetitionTypes::kTechnical);
    PYTHON_ENUM_END(m, PtCCRPetitionType);
    
    PYTHON_ENUM_START(PtLanguage);
    PYTHON_ENUM_ELEMENT(PtLanguage, kEnglish,       plLocalization::kEnglish);
    PYTHON_ENUM_ELEMENT(PtLanguage, kFrench,        plLocalization::kFrench);
    PYTHON_ENUM_ELEMENT(PtLanguage, kGerman,        plLocalization::kGerman);
    PYTHON_ENUM_ELEMENT(PtLanguage, kSpanish,       plLocalization::kSpanish);
    PYTHON_ENUM_ELEMENT(PtLanguage, kItalian,       plLocalization::kItalian);
    PYTHON_ENUM_ELEMENT(PtLanguage, kJapanese,      plLocalization::kJapanese);
    PYTHON_ENUM_ELEMENT(PtLanguage, kNumLanguages,  plLocalization::kNumLanguages);
    PYTHON_ENUM_END(m, PtLanguage);
    
    PYTHON_ENUM_START(PtLOSReportType);
    PYTHON_ENUM_ELEMENT(PtLOSReportType, kReportHit,        plLOSRequestMsg::kReportHit);
    PYTHON_ENUM_ELEMENT(PtLOSReportType, kReportMiss,       plLOSRequestMsg::kReportMiss);
    PYTHON_ENUM_ELEMENT(PtLOSReportType, kReportHitOrMiss,  plLOSRequestMsg::kReportHitOrMiss);
    PYTHON_ENUM_END(m, PtLOSReportType);
    
    PYTHON_ENUM_START(PtLOSObjectType);
    PYTHON_ENUM_ELEMENT(PtLOSObjectType, kClickables,       kClickables);
    PYTHON_ENUM_ELEMENT(PtLOSObjectType, kCameraBlockers,   kCameraBlockers);
    PYTHON_ENUM_ELEMENT(PtLOSObjectType, kCustom,           kCustom);
    PYTHON_ENUM_ELEMENT(PtLOSObjectType, kShootable,        kShootable);
    PYTHON_ENUM_END(m, PtLOSObjectType);
}