/*==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 "pyJournalBook.h"
#include "pyEnum.h"
#include "pyKey.h"
#include "pyImage.h"

#include "../pfJournalBook/pfJournalBook.h"
#include <python.h>

// glue functions
PYTHON_CLASS_DEFINITION(ptBook, pyJournalBook);

PYTHON_DEFAULT_NEW_DEFINITION(ptBook, pyJournalBook)
PYTHON_DEFAULT_DEALLOC_DEFINITION(ptBook)

PYTHON_INIT_DEFINITION(ptBook, args, keywords)
{
	char* kwlist[] = {"esHTMLSource", "coverImage", "callbackKey", "guiName", NULL};
	PyObject* sourceObj = NULL;
	PyObject* coverObj = NULL;
	PyObject* callbackObj = NULL;
	char* guiName = NULL;
	if (!PyArg_ParseTupleAndKeywords(args, keywords, "O|OOs", kwlist, &sourceObj, &coverObj, &callbackObj, &guiName))
	{
		PyErr_SetString(PyExc_TypeError, "__init__ expects a string or unicode string, and optionally a ptImage, ptKey, and string");
		PYTHON_RETURN_INIT_ERROR;
	}

	// convert all the optional arguments
	plKey coverKey = nil;
	if (coverObj)
	{
		if (pyKey::Check(coverObj))
		{
			// this is really the callback key
			if (callbackObj) // callbackObj was already defined, can't have two keys
			{
				PyErr_SetString(PyExc_TypeError, "__init__ expects a string or unicode string, and optionally a ptImage, ptKey, and string");
				PYTHON_RETURN_INIT_ERROR;
			}
			callbackObj = coverObj;
			coverObj = nil;
		}
		else if (!pyImage::Check(coverObj))
		{
			PyErr_SetString(PyExc_TypeError, "__init__ expects a string or unicode string, and optionally a ptImage, ptKey, and string");
			PYTHON_RETURN_INIT_ERROR;
		}
		else
			coverKey = pyImage::ConvertFrom(coverObj)->GetKey();
	}

	plKey callbackKey = nil;
	if (callbackObj)
	{
		if (!pyKey::Check(callbackObj))
		{
			PyErr_SetString(PyExc_TypeError, "__init__ expects a string or unicode string, and optionally a ptImage, ptKey, and string");
			PYTHON_RETURN_INIT_ERROR;
		}
		callbackKey = pyKey::ConvertFrom(callbackObj)->getKey();
	}

	std::string guiNameStr = "";
	if (guiName)
		guiNameStr = guiName;

	// convert the sourcecode object
	if (PyUnicode_Check(sourceObj))
	{
		int len = PyUnicode_GetSize(sourceObj);
		wchar_t* temp = TRACKED_NEW wchar_t[len + 1];
		PyUnicode_AsWideChar((PyUnicodeObject*)sourceObj, temp, len);
		temp[len] = L'\0';

		std::wstring source = temp;
		delete [] temp;

		self->fThis->MakeBook(source, coverKey, callbackKey, guiNameStr);
		PYTHON_RETURN_INIT_OK;
	}
	else if (PyString_Check(sourceObj))
	{
		std::string source = PyString_AsString(sourceObj);

		self->fThis->MakeBook(source, coverKey, callbackKey, guiNameStr);
		PYTHON_RETURN_INIT_OK;
	}

	// source wasn't a string or unicode string
	PyErr_SetString(PyExc_TypeError, "__init__ expects a string or unicode string, and optionally a ptImage, ptKey, and string");
	PYTHON_RETURN_INIT_ERROR;
}

PYTHON_METHOD_DEFINITION(ptBook, show, args)
{
	char startOpened;
	if (!PyArg_ParseTuple(args, "b", &startOpened))
	{
		PyErr_SetString(PyExc_TypeError, "show expects a boolean");
		PYTHON_RETURN_ERROR;
	}
	self->fThis->Show(startOpened != 0);
	PYTHON_RETURN_NONE;
}

PYTHON_BASIC_METHOD_DEFINITION(ptBook, hide, Hide)

PYTHON_METHOD_DEFINITION(ptBook, open, args)
{
	unsigned long startingPage;
	if (!PyArg_ParseTuple(args, "l", &startingPage))
	{
		PyErr_SetString(PyExc_TypeError, "open expects an unsigned long");
		PYTHON_RETURN_ERROR;
	}
	self->fThis->Open(startingPage);
	PYTHON_RETURN_NONE;
}

PYTHON_BASIC_METHOD_DEFINITION(ptBook, close, Close)
PYTHON_BASIC_METHOD_DEFINITION(ptBook, closeAndHide, CloseAndHide)

PYTHON_BASIC_METHOD_DEFINITION(ptBook, nextPage, NextPage)
PYTHON_BASIC_METHOD_DEFINITION(ptBook, previousPage, PreviousPage)

PYTHON_METHOD_DEFINITION(ptBook, goToPage, args)
{
	unsigned long page;
	if (!PyArg_ParseTuple(args, "l", &page))
	{
		PyErr_SetString(PyExc_TypeError, "goToPage expects an unsigned long");
		PYTHON_RETURN_ERROR;
	}
	self->fThis->GoToPage(page);
	PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptBook, setSize, args)
{
	float width, height;
	if (!PyArg_ParseTuple(args, "ff", &width, &height))
	{
		PyErr_SetString(PyExc_TypeError, "setSize expects two floats");
		PYTHON_RETURN_ERROR;
	}
	self->fThis->SetSize(width, height);
	PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION_NOARGS(ptBook, getCurrentPage)
{
	return PyLong_FromUnsignedLong(self->fThis->GetCurrentPage());
}

PYTHON_METHOD_DEFINITION(ptBook, allowPageTurning, args)
{
	char allow;
	if (!PyArg_ParseTuple(args, "b", &allow))
	{
		PyErr_SetString(PyExc_TypeError, "allowPageTurning expects a boolean");
		PYTHON_RETURN_ERROR;
	}
	self->fThis->AllowPageTurning(allow != 0);
	PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptBook, setPageMargin, args)
{
	unsigned long margin;
	if (!PyArg_ParseTuple(args, "l", &margin))
	{
		PyErr_SetString(PyExc_TypeError, "setPageMargin expects an unsigned long");
		PYTHON_RETURN_ERROR;
	}
	self->fThis->SetPageMargin(margin);
	PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptBook, setGUI, args)
{
	char* guiName;
	if (!PyArg_ParseTuple(args, "s", &guiName))
	{
		PyErr_SetString(PyExc_TypeError, "setGUI expects a string");
		PYTHON_RETURN_ERROR;
	}
	self->fThis->SetGUI(guiName);
	PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptBook, getMovie, args)
{
	unsigned char index;
	if (!PyArg_ParseTuple(args, "b", &index))
	{
		PyErr_SetString(PyExc_TypeError, "getMovie expects a unsigned 8-bit int");
		PYTHON_RETURN_ERROR;
	}
	return self->fThis->GetMovie(index);
}

PYTHON_METHOD_DEFINITION(ptBook, setEditable, args)
{
	char editable;
	if (!PyArg_ParseTuple(args, "b", &editable))
	{
		PyErr_SetString(PyExc_TypeError, "setEditable expects a boolean");
		PYTHON_RETURN_ERROR;
	}
	self->fThis->SetEditable(editable != 0);
	PYTHON_RETURN_NONE;
}

PYTHON_METHOD_DEFINITION(ptBook, getEditableText, args)
{
	return PyString_FromString(self->fThis->GetEditableText().c_str());
}

PYTHON_METHOD_DEFINITION(ptBook, setEditableText, args)
{
	char* text;
	if (!PyArg_ParseTuple(args, "s", &text))
	{
		PyErr_SetString(PyExc_TypeError, "setEditableText expects a string");
		PYTHON_RETURN_ERROR;
	}
	self->fThis->SetEditableText(text);
	PYTHON_RETURN_NONE;
}

PYTHON_START_METHODS_TABLE(ptBook)
	PYTHON_METHOD(ptBook, show, "Params: startOpened\nShows the book closed, or open if the the startOpened flag is true"),
	PYTHON_BASIC_METHOD(ptBook, hide, "Hides the book"),
	PYTHON_METHOD(ptBook, open, "Params: startingPage\nOpens the book to the specified page"),
	PYTHON_BASIC_METHOD(ptBook, close, "Closes the book"),
	PYTHON_BASIC_METHOD(ptBook, closeAndHide, "Closes the book and hides it once it finishes animating"),
	PYTHON_BASIC_METHOD(ptBook, nextPage, "Flips the book to the next page"),
	PYTHON_BASIC_METHOD(ptBook, previousPage, "Flips the book to the previous page"),
	PYTHON_METHOD(ptBook, goToPage, "Params: page\nFlips the book to the specified page"),
	PYTHON_METHOD(ptBook, setSize, "Params: width,height\nSets the size of the book (width and height are floats from 0 to 1)"),
	PYTHON_METHOD_NOARGS(ptBook, getCurrentPage, "Returns the currently shown page"),
	PYTHON_METHOD(ptBook, allowPageTurning, "Params: allow\nTurns on and off the ability to flip the pages in a book"),
	PYTHON_METHOD(ptBook, setPageMargin, "Params: margin\nSets the text margin for the book"),
	PYTHON_METHOD(ptBook, setGUI, "Params: guiName\nSets the gui to be used by the book, if the requested gui is not loaded, it will use the default\nDo not call while the book is open!"),
	PYTHON_METHOD(ptBook, getMovie, "Params: index\nGrabs a ptAnimation object representing the movie indexed by index. The index is the index of the movie in the source code"),
	PYTHON_METHOD(ptBook, setEditable, "Params: editable\nTurn book editing on or off. If the book GUI does not support editing, nothing will happen"),
	PYTHON_METHOD(ptBook, getEditableText, "Returns the editable text currently contained in the book."),
	PYTHON_METHOD(ptBook, setEditableText, "Params: text\nSets the book's editable text."),
PYTHON_END_METHODS_TABLE;

// Type structure definition
PLASMA_DEFAULT_TYPE(ptBook, "Params: esHTMLSource,coverImage=None,callbackKey=None,guiName=''\nCreates a new book");

// required functions for PyObject interoperability
PyObject *pyJournalBook::New(std::string htmlSource, plKey coverImageKey /* = nil */, plKey callbackKey /* = nil */, std::string guiName /* = "" */)
{
	ptBook *newObj = (ptBook*)ptBook_type.tp_new(&ptBook_type, NULL, NULL);
	newObj->fThis->MakeBook(htmlSource, coverImageKey, callbackKey, guiName);
	return (PyObject*)newObj;
}

PyObject *pyJournalBook::New(std::wstring htmlSource, plKey coverImageKey /* = nil */, plKey callbackKey /* = nil */, std::string guiName /* = "" */)
{
	ptBook *newObj = (ptBook*)ptBook_type.tp_new(&ptBook_type, NULL, NULL);
	newObj->fThis->MakeBook(htmlSource, coverImageKey, callbackKey, guiName);
	return (PyObject*)newObj;
}

PYTHON_CLASS_CHECK_IMPL(ptBook, pyJournalBook)
PYTHON_CLASS_CONVERT_FROM_IMPL(ptBook, pyJournalBook)

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

PYTHON_GLOBAL_METHOD_DEFINITION(PtLoadBookGUI, args, "Params: guiName\nLoads the gui specified, a gui must be loaded before it can be used. If the gui is already loaded, doesn't do anything")
{
	char* guiName;
	if (!PyArg_ParseTuple(args, "s", &guiName))
	{
		PyErr_SetString(PyExc_TypeError, "PtLoadBookGUI expects a string");
		PYTHON_RETURN_ERROR;
	}
	pyJournalBook::LoadGUI(guiName);
	PYTHON_RETURN_NONE;
}

PYTHON_GLOBAL_METHOD_DEFINITION(PtUnloadBookGUI, args, "Params: guiName\nUnloads the gui specified. If the gui isn't loaded, doesn't do anything")
{
	char* guiName;
	if (!PyArg_ParseTuple(args, "s", &guiName))
	{
		PyErr_SetString(PyExc_TypeError, "PtUnloadBookGUI expects a string");
		PYTHON_RETURN_ERROR;
	}
	pyJournalBook::UnloadGUI(guiName);
	PYTHON_RETURN_NONE;
}

PYTHON_BASIC_GLOBAL_METHOD_DEFINITION(PtUnloadAllBookGUIs, pyJournalBook::UnloadAllGUIs, "Unloads all loaded guis except for the default one")

void pyJournalBook::AddPlasmaMethods(std::vector<PyMethodDef> &methods)
{
	PYTHON_GLOBAL_METHOD(methods, PtLoadBookGUI);
	PYTHON_GLOBAL_METHOD(methods, PtUnloadBookGUI);
	PYTHON_BASIC_GLOBAL_METHOD(methods, PtUnloadAllBookGUIs);
}

void pyJournalBook::AddPlasmaConstantsClasses(PyObject *m)
{
	PYTHON_ENUM_START(PtBookEventTypes);
	PYTHON_ENUM_ELEMENT(PtBookEventTypes, kNotifyImageLink,			pfJournalBook::kNotifyImageLink);
	PYTHON_ENUM_ELEMENT(PtBookEventTypes, kNotifyShow,				pfJournalBook::kNotifyShow);
	PYTHON_ENUM_ELEMENT(PtBookEventTypes, kNotifyHide,				pfJournalBook::kNotifyHide);
	PYTHON_ENUM_ELEMENT(PtBookEventTypes, kNotifyNextPage,			pfJournalBook::kNotifyNextPage);
	PYTHON_ENUM_ELEMENT(PtBookEventTypes, kNotifyPreviousPage,		pfJournalBook::kNotifyPreviousPage);
	PYTHON_ENUM_ELEMENT(PtBookEventTypes, kNotifyCheckUnchecked,	pfJournalBook::kNotifyCheckUnchecked);
	PYTHON_ENUM_ELEMENT(PtBookEventTypes, kNotifyClose,				pfJournalBook::kNotifyClose);
	PYTHON_ENUM_END(m, PtBookEventTypes);
}