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

#include <string_theory/string>
#include <utility>

#include "plGImage/plDynamicTextMap.h"

#include "pyColor.h"
#include "pyEnum.h"
#include "pyGlueHelpers.h"
#include "pyImage.h"
#include "pyKey.h"

// glue functions
PYTHON_CLASS_DEFINITION(ptDynamicMap, pyDynamicText);

PYTHON_DEFAULT_NEW_DEFINITION(ptDynamicMap, pyDynamicText)
PYTHON_DEFAULT_DEALLOC_DEFINITION(ptDynamicMap)

PYTHON_INIT_DEFINITION(ptDynamicMap, args, keywords)
{
    PyObject* keyObj = nullptr;
    if (!PyArg_ParseTuple(args, "|O", &keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "__init__ expects an optional ptKey");
        PYTHON_RETURN_INIT_ERROR;
    }
    if (!keyObj)
        PYTHON_RETURN_INIT_OK; // nothing to do
    if (!pyKey::Check(keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "__init__ expects an optional ptKey");
        PYTHON_RETURN_INIT_ERROR;
    }
    pyKey* key = pyKey::ConvertFrom(keyObj);
    self->fThis->AddReceiver(*key);
    PYTHON_RETURN_INIT_OK;
}

PYTHON_METHOD_DEFINITION(ptDynamicMap, sender, args)
{
    PyObject* senderObj = nullptr;
    if (!PyArg_ParseTuple(args, "O", &senderObj))
    {
        PyErr_SetString(PyExc_TypeError, "sender expects a ptKey");
        PYTHON_RETURN_ERROR;
    }
    if (!pyKey::Check(senderObj))
    {
        PyErr_SetString(PyExc_TypeError, "sender expects a ptKey");
        PYTHON_RETURN_ERROR;
    }
    pyKey* sender = pyKey::ConvertFrom(senderObj);
    self->fThis->SetSender(*sender);
    PYTHON_RETURN_NONE;
}

PYTHON_BASIC_METHOD_DEFINITION(ptDynamicMap, clearKeys, ClearReceivers)

PYTHON_METHOD_DEFINITION(ptDynamicMap, addKey, args)
{
    PyObject* keyObj = nullptr;
    if (!PyArg_ParseTuple(args, "O", &keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "addKey expects a ptKey");
        PYTHON_RETURN_ERROR;
    }
    if (!pyKey::Check(keyObj))
    {
        PyErr_SetString(PyExc_TypeError, "addKey expects a ptKey");
        PYTHON_RETURN_ERROR;
    }
    pyKey* key = pyKey::ConvertFrom(keyObj);
    self->fThis->AddReceiver(*key);
    PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptDynamicMap, netPropagate, args)
{
    char propagateFlag;
    if (!PyArg_ParseTuple(args, "b", &propagateFlag))
    {
        PyErr_SetString(PyExc_TypeError, "netPropagate expects a boolean");
        PYTHON_RETURN_ERROR;
    }
    self->fThis->SetNetPropagate(propagateFlag != 0);
    PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptDynamicMap, netForce, args)
{
    char forceFlag;
    if (!PyArg_ParseTuple(args, "b", &forceFlag))
    {
        PyErr_SetString(PyExc_TypeError, "netForce expects a boolean");
        PYTHON_RETURN_ERROR;
    }
    self->fThis->SetNetForce(forceFlag != 0);
    PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptDynamicMap, clearToColor, args)
{
    PyObject* colorObj = nullptr;
    if (!PyArg_ParseTuple(args, "O", &colorObj))
    {
        PyErr_SetString(PyExc_TypeError, "clearToColor expects a ptColor");
        PYTHON_RETURN_ERROR;
    }
    if (!pyColor::Check(colorObj))
    {
        PyErr_SetString(PyExc_TypeError, "clearToColor expects a ptColor");
        PYTHON_RETURN_ERROR;
    }
    pyColor* color = pyColor::ConvertFrom(colorObj);
    self->fThis->ClearToColor(*color);
    PYTHON_RETURN_NONE;
}

PYTHON_BASIC_METHOD_DEFINITION(ptDynamicMap, flush, Flush)
PYTHON_BASIC_METHOD_DEFINITION(ptDynamicMap, purgeImage, PurgeImage)

PYTHON_METHOD_DEFINITION(ptDynamicMap, setTextColor, args)
{
    PyObject* colorObj = nullptr;
    char blockRGB = 0;
    if (!PyArg_ParseTuple(args, "O|b", &colorObj, &blockRGB))
    {
        PyErr_SetString(PyExc_TypeError, "setTextColor expects a ptColor and an optional boolean");
        PYTHON_RETURN_ERROR;
    }
    if (!pyColor::Check(colorObj))
    {
        PyErr_SetString(PyExc_TypeError, "setTextColor expects a ptColor and an optional boolean");
        PYTHON_RETURN_ERROR;
    }
    pyColor* color = pyColor::ConvertFrom(colorObj);
    self->fThis->SetTextColor2(*color, blockRGB != 0);
    PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptDynamicMap, setFont, args)
{
    ST::string faceName;
    short fontSize;
    if (!PyArg_ParseTuple(args, "O&h", PyUnicode_STStringConverter, &faceName, &fontSize))
    {
        PyErr_SetString(PyExc_TypeError, "setFont expects a string and a short int");
        PYTHON_RETURN_ERROR;
    }
    self->fThis->SetFont(std::move(faceName), fontSize);
    PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptDynamicMap, fillRect, args)
{
    unsigned short left, top, right, bottom;
    PyObject* colorObj = nullptr;
    if (!PyArg_ParseTuple(args, "hhhhO", &left, &top, &right, &bottom, &colorObj))
    {
        PyErr_SetString(PyExc_TypeError, "fillRect expects four unsigned short ints and a ptColor");
        PYTHON_RETURN_ERROR;
    }
    if (!pyColor::Check(colorObj))
    {
        PyErr_SetString(PyExc_TypeError, "fillRect expects four unsigned short ints and a ptColor");
        PYTHON_RETURN_ERROR;
    }
    pyColor* color = pyColor::ConvertFrom(colorObj);
    self->fThis->FillRect(left, top, right, bottom, *color);
    PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptDynamicMap, frameRect, args)
{
    unsigned short left, top, right, bottom;
    PyObject* colorObj = nullptr;
    if (!PyArg_ParseTuple(args, "hhhhO", &left, &top, &right, &bottom, &colorObj))
    {
        PyErr_SetString(PyExc_TypeError, "frameRect expects four unsigned short ints and a ptColor");
        PYTHON_RETURN_ERROR;
    }
    if (!pyColor::Check(colorObj))
    {
        PyErr_SetString(PyExc_TypeError, "frameRect expects four unsigned short ints and a ptColor");
        PYTHON_RETURN_ERROR;
    }
    pyColor* color = pyColor::ConvertFrom(colorObj);
    self->fThis->FrameRect(left, top, right, bottom, *color);
    PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptDynamicMap, setClipping, args)
{
    unsigned short left, top, right, bottom;
    if (!PyArg_ParseTuple(args, "hhhh", &left, &top, &right, &bottom))
    {
        PyErr_SetString(PyExc_TypeError, "setClipping expects four unsigned short ints");
        PYTHON_RETURN_ERROR;
    }
    self->fThis->SetClipping(left, top, right, bottom);
    PYTHON_RETURN_NONE;
}

PYTHON_BASIC_METHOD_DEFINITION(ptDynamicMap, unsetClipping, UnsetClipping)

PYTHON_METHOD_DEFINITION(ptDynamicMap, setWrapping, args)
{
    unsigned short wrapWidth, wrapHeight;
    if (!PyArg_ParseTuple(args, "hh", &wrapWidth, &wrapHeight))
    {
        PyErr_SetString(PyExc_TypeError, "setWrapping expects two unsigned short ints");
        PYTHON_RETURN_ERROR;
    }
    self->fThis->SetWrapping(wrapWidth, wrapHeight);
    PYTHON_RETURN_NONE;
}

PYTHON_BASIC_METHOD_DEFINITION(ptDynamicMap, unsetWrapping, UnsetWrapping)

PYTHON_METHOD_DEFINITION(ptDynamicMap, drawText, args)
{
    short x, y;
    ST::string text;
    if (!PyArg_ParseTuple(args, "hhO&", &x, &y, PyUnicode_STStringConverter, &text))
    {
        PyErr_SetString(PyExc_TypeError, "drawText expects two short ints and a string");
        PYTHON_RETURN_ERROR;
    }
    self->fThis->DrawText(x, y, text);
    PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptDynamicMap, drawImage, args)
{
    short x, y;
    PyObject* imageObj = nullptr;
    char respectAlpha;
    if (!PyArg_ParseTuple(args, "hhOb", &x, &y, &imageObj, &respectAlpha))
    {
        PyErr_SetString(PyExc_TypeError, "drawImage expects two shorts, a ptImage, and a boolean");
        PYTHON_RETURN_ERROR;
    }
    if (!pyImage::Check(imageObj))
    {
        PyErr_SetString(PyExc_TypeError, "drawImage expects two shorts, a ptImage, and a boolean");
        PYTHON_RETURN_ERROR;
    }
    pyImage* image = pyImage::ConvertFrom(imageObj);
    self->fThis->DrawImage(x, y, *image, respectAlpha != 0);
    PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptDynamicMap, drawImageClipped, args)
{
    unsigned short x, y;
    PyObject* imageObj = nullptr;
    unsigned short cx, cy, cw, ch;
    char respectAlpha;
    if (!PyArg_ParseTuple(args, "hhOhhhhb", &x, &y, &imageObj, &cx, &cy, &cw, &ch, &respectAlpha))
    {
        PyErr_SetString(PyExc_TypeError, "drawImageClipped expects two shorts, a ptImage, four shorts, and a boolean");
        PYTHON_RETURN_ERROR;
    }
    if (!pyImage::Check(imageObj))
    {
        PyErr_SetString(PyExc_TypeError, "drawImageClipped expects two shorts, a ptImage, four shorts, and a boolean");
        PYTHON_RETURN_ERROR;
    }
    pyImage* image = pyImage::ConvertFrom(imageObj);
    self->fThis->DrawImageClipped(x, y, *image, cx, cy, cw, ch, respectAlpha != 0);
    PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION_NOARGS(ptDynamicMap, getWidth)
{
    return PyLong_FromLong(self->fThis->GetWidth());
}

PYTHON_METHOD_DEFINITION_NOARGS(ptDynamicMap, getHeight)
{
    return PyLong_FromLong(self->fThis->GetHeight());
}

PYTHON_METHOD_DEFINITION(ptDynamicMap, calcTextExtents, args)
{
    ST::string text;
    if (!PyArg_ParseTuple(args, "O&", PyUnicode_STStringConverter, &text))
    {
        PyErr_SetString(PyExc_TypeError, "calcTextExtents expects a string");
        PYTHON_RETURN_ERROR;
    }

    uint16_t height, width;
    self->fThis->CalcTextExtents(text, width, height);
    PyObject* retVal = PyTuple_New(2);
    PyTuple_SetItem(retVal, 0, PyLong_FromLong(width));
    PyTuple_SetItem(retVal, 1, PyLong_FromLong(height));
    return retVal;
}

PYTHON_METHOD_DEFINITION(ptDynamicMap, setJustify, args)
{
    unsigned char justify;
    if (!PyArg_ParseTuple(args, "b", &justify))
    {
        PyErr_SetString(PyExc_TypeError, "setJustify expects a unsigned 8-bit int");
        PYTHON_RETURN_ERROR;
    }
    self->fThis->SetJustify(justify);
    PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptDynamicMap, setLineSpacing, args)
{
    short spacing;
    if (!PyArg_ParseTuple(args, "h", &spacing))
    {
        PyErr_SetString(PyExc_TypeError, "setLineSpacing expects a short int");
        PYTHON_RETURN_ERROR;
    }
    self->fThis->SetLineSpacing(spacing);
    PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION_NOARGS(ptDynamicMap, getImage)
{
    return pyImage::New(self->fThis->GetImage());
}

PYTHON_START_METHODS_TABLE(ptDynamicMap)
    PYTHON_METHOD(ptDynamicMap, sender, "Params: sender\nSet the sender of the message being sent to the DynamicMap"),
    PYTHON_BASIC_METHOD(ptDynamicMap, clearKeys, "Clears the receiver list"),
    PYTHON_METHOD(ptDynamicMap, addKey, "Params: key\nAdd a receiver... in other words a DynamicMap"),
    PYTHON_METHOD(ptDynamicMap, netPropagate, "Params: propagateFlag\nSpecify whether this object needs to use messages that are sent on the network\n"
                "- The default is for this to be false."),
    PYTHON_METHOD(ptDynamicMap, netForce, "Params: forceFlag\nSpecify whether this object needs to use messages that are forced to the network\n"
                "- This is to be used if your Python program is running on only one client\n"
                "Such as a game master, only running on the client that owns a particular object\n"
                "This only applies when NetPropagate is set to true"),
    PYTHON_METHOD(ptDynamicMap, clearToColor, "Params: color\nClear the DynamicMap to the specified color\n"
                "- 'color' is a ptColor object"),
    PYTHON_BASIC_METHOD(ptDynamicMap, flush, "Flush all the commands that were issued since the last flush()"),
    PYTHON_BASIC_METHOD(ptDynamicMap, purgeImage, "Purge the DynamicTextMap images"),
    PYTHON_METHOD(ptDynamicMap, setTextColor, "Params: color, blockRGB=0\nSet the color of the text to be written\n"
                "- 'color' is a ptColor object\n"
                "- 'blockRGB' must be true if you're trying to render onto a transparent or semi-transparent color"),
    PYTHON_METHOD(ptDynamicMap, setFont, "Params: facename,size\nSet the font of the text to be written\n"
                "- 'facename' is a string with the name of the font\n"
                "- 'size' is the point size of the font to use"),
    PYTHON_METHOD(ptDynamicMap, fillRect, "Params: left,top,right,bottom,color\nFill in the specified rectangle with a color\n"
                "- left,top,right,bottom define the rectangle\n"
                "- 'color' is a ptColor object"),   
    PYTHON_METHOD(ptDynamicMap, frameRect, "Params: left,top,right,bottom,color\nFrame a rectangle with a specified color\n"
                "- left,top,right,bottom define the rectangle\n"
                "- 'color' is a ptColor object"),
    PYTHON_METHOD(ptDynamicMap, setClipping, "Params: clipLeft,clipTop,clipRight,clipBottom\nSets the clipping rectangle\n"
                "- All drawtext will be clipped to this until the\n"
                "unsetClipping() is called"),
    PYTHON_BASIC_METHOD(ptDynamicMap, unsetClipping, "Stop the clipping of text"),
    PYTHON_METHOD(ptDynamicMap, setWrapping, "Params: wrapWidth,wrapHeight\nSet where text will be wrapped horizontally and vertically\n"
                "- All drawtext commands will be wrapped until the\n"
                "unsetWrapping() is called"),
    PYTHON_BASIC_METHOD(ptDynamicMap, unsetWrapping, "Stop text wrapping"),
    PYTHON_METHOD(ptDynamicMap, drawText, "Params: x,y,text\nDraw text at a specified location\n"
                "- x,y is the point to start drawing the text\n"
                "- 'text' is a string of the text to be drawn"),
    PYTHON_METHOD(ptDynamicMap, drawImage, "Params: x,y,image,respectAlphaFlag\nDraws a ptImage object on the dynamicTextmap starting at the location x,y"),
    PYTHON_METHOD(ptDynamicMap, drawImageClipped, "Params: x,y,image,cx,cy,cw,ch,respectAlphaFlag\nDraws a ptImage object clipped to cx,cy with cw(width),ch(height)"),
    PYTHON_METHOD_NOARGS(ptDynamicMap, getWidth, "Returns the width of the dynamicTextmap"),
    PYTHON_METHOD_NOARGS(ptDynamicMap, getHeight, "Returns the height of the dynamicTextmap"),
    PYTHON_METHOD(ptDynamicMap, calcTextExtents, "Params: text\nCalculates the extent of the specified text, returns it as a (width, height) tuple"),
    PYTHON_METHOD(ptDynamicMap, setJustify, "Params: justify\nSets the justification of the text. (justify is a PtJustify)"),
    PYTHON_METHOD(ptDynamicMap, setLineSpacing, "Params: spacing\nSets the line spacing (in pixels)"),
    PYTHON_METHOD_NOARGS(ptDynamicMap, getImage, "Returns a pyImage associated with the dynamicTextmap"),
PYTHON_END_METHODS_TABLE;

// Type structure definition
PLASMA_DEFAULT_TYPE(ptDynamicMap, "Params: key=None\nCreates a ptDynamicMap object");

// required functions for PyObject interoperability
PYTHON_CLASS_NEW_IMPL(ptDynamicMap, pyDynamicText)

PyObject *pyDynamicText::New(const pyKey& key)
{
    ptDynamicMap *newObj = (ptDynamicMap*)ptDynamicMap_type.tp_new(&ptDynamicMap_type, nullptr, nullptr);
    newObj->fThis->fReceivers.emplace_back(key.getKey());
    return (PyObject*)newObj;
}

PyObject *pyDynamicText::New(plKey key)
{
    ptDynamicMap *newObj = (ptDynamicMap*)ptDynamicMap_type.tp_new(&ptDynamicMap_type, nullptr, nullptr);
    newObj->fThis->fReceivers.emplace_back(std::move(key));
    return (PyObject*)newObj;
}

PYTHON_CLASS_CHECK_IMPL(ptDynamicMap, pyDynamicText)
PYTHON_CLASS_CONVERT_FROM_IMPL(ptDynamicMap, pyDynamicText)

///////////////////////////////////////////////////////////////////////////
//
// AddPlasmaClasses - the python module definitions
//
void pyDynamicText::AddPlasmaClasses(PyObject *m)
{
    PYTHON_CLASS_IMPORT_START(m);
    PYTHON_CLASS_IMPORT(m, ptDynamicMap);
    PYTHON_CLASS_IMPORT_END(m);
}

void pyDynamicText::AddPlasmaConstantsClasses(PyObject *m)
{
    PYTHON_ENUM_START(PtJustify)
    PYTHON_ENUM_ELEMENT(PtJustify, kCenter, plDynamicTextMap::kCenter)
    PYTHON_ENUM_ELEMENT(PtJustify, kLeftJustify, plDynamicTextMap::kLeftJustify)
    PYTHON_ENUM_ELEMENT(PtJustify, kRightJustify, plDynamicTextMap::kRightJustify)
    PYTHON_ENUM_END(m, PtJustify)

    PYTHON_ENUM_START(PtFontFlags)
    PYTHON_ENUM_ELEMENT(PtFontFlags, kFontBold, plDynamicTextMap::kFontBold)
    PYTHON_ENUM_ELEMENT(PtFontFlags, kFontItalic, plDynamicTextMap::kFontItalic)
    PYTHON_ENUM_ELEMENT(PtFontFlags, kFontShadowed, plDynamicTextMap::kFontShadowed)
    PYTHON_ENUM_END(m, PtFontFlags)
}
