/*==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 "pyKey.h"
#include "pyDynamicText.h"
#include "pyEnum.h"
#include "pyColor.h"
#include "pyImage.h"
#include "plGImage/plDynamicTextMap.h"

#include <python.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 = NULL;
    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 = NULL;
    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 = NULL;
    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 = NULL;
    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 = NULL;
    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)
{
    char* faceName;
    short fontSize;
    if (!PyArg_ParseTuple(args, "sh", &faceName, &fontSize))
    {
        PyErr_SetString(PyExc_TypeError, "setFont expects a string and a short int");
        PYTHON_RETURN_ERROR;
    }
    self->fThis->SetFont(faceName, fontSize);
    PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptDynamicMap, fillRect, args)
{
    unsigned short left, top, right, bottom;
    PyObject* colorObj = NULL;
    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 = NULL;
    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;
    char* text;
    if (!PyArg_ParseTuple(args, "hhs", &x, &y, &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, drawTextW, args)
{
    short x, y;
    wchar_t* text;
    if (!PyArg_ParseTuple(args, "hhu", &x, &y, &text))
    {
        PyErr_SetString(PyExc_TypeError, "drawTextW expects two short ints and a unicode string");
        PYTHON_RETURN_ERROR;
    }
    self->fThis->DrawTextW(x, y, text);
    PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptDynamicMap, drawImage, args)
{
    short x, y;
    PyObject* imageObj = NULL;
    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 = NULL;
    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 PyInt_FromLong(self->fThis->GetWidth());
}

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

PYTHON_METHOD_DEFINITION(ptDynamicMap, calcTextExtents, args)
{
    PyObject* textObj = NULL;
    if (!PyArg_ParseTuple(args, "O", &textObj))
    {
        PyErr_SetString(PyExc_TypeError, "calcTextExtents expects a string");
        PYTHON_RETURN_ERROR;
    }

    std::wstring wText;
    if (PyUnicode_Check(textObj))
    {
        int strLen = PyUnicode_GetSize(textObj);
        wchar_t* text = TRACKED_NEW wchar_t[strLen + 1];
        PyUnicode_AsWideChar((PyUnicodeObject*)textObj, text, strLen);
        text[strLen] = L'\0';
        wText = text;
        delete [] text;
    }
    else if (PyString_Check(textObj))
    {
        // we'll allow this, just in case something goes weird
        char* text = PyString_AsString(textObj);
        wchar_t* temp = hsStringToWString(text);
        wText = temp;
        delete [] temp;
    }
    else
    {
        PyErr_SetString(PyExc_TypeError, "calcTextExtents expects a string");
        PYTHON_RETURN_ERROR;
    }

    unsigned height, width;
    self->fThis->CalcTextExtents(wText, width, height);
    PyObject* retVal = PyTuple_New(2);
    PyTuple_SetItem(retVal, 0, PyInt_FromLong(width));
    PyTuple_SetItem(retVal, 1, PyInt_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, drawTextW, "Params: x,y,text\nUnicode version of drawText"),
    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(pyKey& key)
{
    ptDynamicMap *newObj = (ptDynamicMap*)ptDynamicMap_type.tp_new(&ptDynamicMap_type, NULL, NULL);
    newObj->fThis->fReceivers.Append(key.getKey());
    return (PyObject*)newObj;
}

PyObject *pyDynamicText::New(plKey key)
{
    ptDynamicMap *newObj = (ptDynamicMap*)ptDynamicMap_type.tp_new(&ptDynamicMap_type, NULL, NULL);
    newObj->fThis->fReceivers.Append(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);
}