/****************************************************************************
*
* guimodule.c -- GUI module for Amiga Python Example
*
*****************************************************************************
*
*/

#include <interfaces/exec.h>
#include <interfaces/intuition.h>
#include <classes/requester.h>

#include "Python.h"  // Includes everything you need for Python

/***************************************************************************/

// These are declared static to keep their scope limited to just
// this file alone.

static struct ExecIFace *IExec            = NULL;
static struct IntuitionIFace *IIntuition  = NULL;
static struct ClassLibrary *RequesterBase = NULL;
static Class *RequesterClass              = NULL;

/***************************************************************************/

// These functions are boilerplate AmigaOS functions to make it
// easier to handle the libraries and interfaces.

static BOOL OpenLibs(void);
static void CloseLibs(void);
static struct Interface *OpenInterface(CONST_STRPTR libname, uint32 libver);
static void CloseInterface(struct Interface *iface);

// This is the function we are creating to access from Python.

static PyObject *gui_Notify(PyObject self, PyObject* args);

/***************************************************************************/

// These are the Python documentation strings that will be displayed
// when the user uses "help(x)" from Python. They should be much more
// descriptive than this.

PyDoc_STRVAR(gui_module_doc,
"AmigaOS GUI Module\n\n"
"This module contains AmigaOS GUI elements.\n");

PyDoc_STRVAR(gui_Notify_doc,
"	Notify(title, message)\n");

/***************************************************************************/

// Here is our list of methods that will be visible from Python.

static PyMethodDef GuiMethods[] =
{
	{"Notify", (PyCFunction)gui_Notify, METH_VARARGS, gui_Notify_doc},
	{NULL, NULL, 0, NULL}
};

/***************************************************************************/

// This function is called by Python when the module is imported.

PyMODINIT_FUNC
initgui(void)
{
	// We need to open our AmigaOS resources before our module will
	// work so do that here.

	if (!OpenLibs())
	{
		CloseLibs();
		return;
	}

	// Make sure any AmigaOS specific resources are freed at exit.
	// We don't have control over when our module exits.

	Py_AtExit(CloseLibs);

	// Link the module name to our array of methods.

	(void)Py_InitModule3("gui", GuiMethods, gui_module_doc);
}

/***************************************************************************/

BOOL
OpenLibs(void)
{
	// We need access to IExec because it is not passed in from Python.

	IExec = (struct ExecIFace *)((*(struct ExecBase **)4)->MainInterface);

	// Obtain all the interfaces we need.

	IIntuition = (struct IntuitionIFace *)OpenInterface("intuition.library", 53);

	if (IIntuition == NULL)
	{
		return FALSE;
	}

	// Open all the BOOPSI classes we need.

	RequesterBase = IIntuition->OpenClass("requester.class", 53, &RequesterClass);

	if (RequesterBase == NULL)
	{
		return FALSE;
	}

	return TRUE;
}

/***************************************************************************/

void
CloseLibs(void)
{
	if (IIntuition != NULL)
	{
		IIntuition->CloseClass(RequesterBase);
		RequesterBase = NULL;
	}

	CloseInterface((struct Interface*)IIntuition);
	IIntuition = NULL;
}

/***************************************************************************/

struct Interface *
OpenInterface(CONST_STRPTR libname, uint32 libver)
{
	struct Library *base = IExec->OpenLibrary(libname, libver);
	struct Interface *iface = IExec->GetInterface(base, "main", 1, NULL);
	if (iface == NULL)
	{
		// We should probably post some kind of error message here.

		IExec->CloseLibrary(base);
	}

	return iface;
}

/***************************************************************************/

void
CloseInterface(struct Interface *iface)
{
	if (iface != NULL)
	{
		struct Library *base = iface->Data.LibBase;
		IExec->DropInterface(iface);
		IExec->CloseLibrary(base);
	}
}

/***************************************************************************/

PyObject *
gui_Notify(PyObject self, PyObject* args)
{
	// First parse the Python arguments.
	// In this case it is two string objects.

	const char *title = NULL;
	const char *message = NULL;

	if (!PyArg_ParseTuple(args, "ss", &title, &message))
	{
		return NULL;
	}

	// Create a new BOOPSI object for notifying the user.

	Object *reqobj = IIntuition->NewObject(RequesterClass, NULL,
		REQ_Type, REQTYPE_INFO,
		REQ_TitleText, title,
		REQ_BodyText, message,
		REQ_GadgetText, "Ok",
		TAG_END);

	if (reqobj != NULL)
	{
		struct Screen *screen = IIntuition->LockPubScreen(NULL);
		if (screen != NULL)
		{
			// Prepare the BOOPSI message explicitly on the stack.

			struct orRequest msg;

			msg.MethodID  = RM_OPENREQ;
			msg.or_Attrs  = NULL;
			msg.or_Window = NULL;
			msg.or_Screen = screen;

			// This allows Python's threading to continue while we
			// block and wait for the user.

			Py_BEGIN_ALLOW_THREADS

			// This call opens the GUI requester and blocks until the
			// user makes a selection.

			(void)IIntuition->IDoMethodA(reqobj, (APTR)&msg);

			// Block threading again while we finish up.
			Py_END_ALLOW_THREADS

			IIntuition->UnlockPubScreen(NULL, screen);
			screen = NULL;
		}

		IIntuition->DisposeObject(reqobj);
		reqobj = NULL;
	}

	// In this example we don't return a complex object so just
	// return Python None.

	Py_INCREF(Py_None);
	return Py_None;
}

