nakedmud-mod/
nakedmud-mod/html/tutorials/
nakedmud-mod/html/tutorials/building_extras/
nakedmud-mod/html/tutorials/c/
nakedmud-mod/html/tutorials/reference/
nakedmud-mod/html/tutorials/scripting/
nakedmud-mod/html/tutorials/scripting_extras/
nakedmud-mod/lib/
nakedmud-mod/lib/help/A/
nakedmud-mod/lib/help/B/
nakedmud-mod/lib/help/C/
nakedmud-mod/lib/help/D/
nakedmud-mod/lib/help/G/
nakedmud-mod/lib/help/H/
nakedmud-mod/lib/help/J/
nakedmud-mod/lib/help/L/
nakedmud-mod/lib/help/M/
nakedmud-mod/lib/help/O/
nakedmud-mod/lib/help/P/
nakedmud-mod/lib/help/R/
nakedmud-mod/lib/help/S/
nakedmud-mod/lib/help/W/
nakedmud-mod/lib/logs/
nakedmud-mod/lib/misc/
nakedmud-mod/lib/players/
nakedmud-mod/lib/pymodules/polc/
nakedmud-mod/lib/txt/
nakedmud-mod/lib/world/
nakedmud-mod/lib/world/zones/examples/
nakedmud-mod/lib/world/zones/examples/mproto/
nakedmud-mod/lib/world/zones/examples/oproto/
nakedmud-mod/lib/world/zones/examples/reset/
nakedmud-mod/lib/world/zones/examples/rproto/
nakedmud-mod/lib/world/zones/examples/trigger/
nakedmud-mod/lib/world/zones/limbo/
nakedmud-mod/lib/world/zones/limbo/room/
nakedmud-mod/lib/world/zones/limbo/rproto/
nakedmud-mod/src/alias/
nakedmud-mod/src/dyn_vars/
nakedmud-mod/src/editor/
nakedmud-mod/src/example_module/
nakedmud-mod/src/help2/
nakedmud-mod/src/set_val/
nakedmud-mod/src/socials/
nakedmud-mod/src/time/
//*****************************************************************************
//
// pyolc.c
//
// Provides wrapper functions for Python to interface with OLC2.
//
//*****************************************************************************
#include "../mud.h"

#ifdef MODULE_OLC2
#include "scripts.h"
#include "pyplugs.h"
#include "pysocket.h"
#include "pyolc.h"



//*****************************************************************************
// mandatory modules
//*****************************************************************************
#include "../olc2/olc.h"
#include "../olc2/olc_extender.h"
#include "../items/iedit.h"



//*****************************************************************************
// local variables and functions
//*****************************************************************************
// a list of methods to add to the module
LIST  *pyolc_methods = NULL;

// a dictionary for the OLC functions of our various item types
PyObject *pyitem_olc = NULL;



//*****************************************************************************
// raw pyolc methods, used by both items and the olc wrapper functions
//*****************************************************************************
void pyolc_do_menu(PyObject *menu, SOCKET_DATA *sock, PyObject *data) {
  PyObject    *pysock = socketGetPyFormBorrowed(sock);
  PyObject       *ret = PyObject_CallFunction(menu, "OO", pysock, data);
  if(ret == NULL)
    log_pyerr("pyolc_do_menu failed");
  Py_XDECREF(ret);
}

int pyolc_do_chooser(PyObject *chooser, SOCKET_DATA *sock, PyObject *data,
		     const char *option) {
  PyObject    *pysock = socketGetPyFormBorrowed(sock);
  PyObject     *pyret = PyObject_CallFunction(chooser, "OOs", pysock, data,
					      option);
  int             ret = MENU_CHOICE_INVALID;
  if(pyret == NULL)
    log_pyerr("pyolc_do_chooser failed: %s", option);
  else if(PyInt_Check(pyret))
    ret = (int)PyInt_AsLong(pyret);
  Py_XDECREF(pyret);
  return ret;
}

bool pyolc_do_parser(PyObject *parser, SOCKET_DATA *sock, PyObject *data,
		     int choice, const char *arg) {
  PyObject    *pysock = socketGetPyFormBorrowed(sock);
  PyObject     *pyret = PyObject_CallFunction(parser, "OOis", pysock, data,
					      choice, arg);
  bool            ret = FALSE;
  if(pyret == NULL)
    log_pyerr("pyolc_do_parser failed: %d, %s", choice, arg);
  else if(PyObject_IsTrue(pyret))
    ret = TRUE;
  Py_XDECREF(pyret);
  return ret;
}

void pyolc_do_from_proto(PyObject *from_proto, PyObject *data) {
  if(from_proto != NULL) {
    PyObject *ret = PyObject_CallFunction(from_proto, "O", data);
    if(ret == NULL)
      log_pyerr("Error in pyolc_do_from_proto");
    Py_XDECREF(ret);
  }
}

void pyolc_do_to_proto(PyObject *to_proto, PyObject *data, BUFFER *buf) {
  if(to_proto != NULL) {
      PyObject *ret = PyObject_CallFunction(to_proto, "O", data);
      if(ret == NULL)
	log_pyerr("Error in pyolc_do_to_proto");
      else if(PyString_Check(ret))
	bufferCat(buf, PyString_AsString(ret));
      Py_XDECREF(ret);
  }
}

void pyolc_do_copyto(PyObject *from, PyObject *to) {
  PyObject *ret = PyObject_CallMethod(from, "copyTo", "O", to);
  if(ret == NULL)
    log_pyerr("Error in pyolc_do_copyto");
  Py_XDECREF(ret);
}

void pyolc_do_save(PyObject *saver, PyObject *data) {
  if(saver != NULL && saver != Py_None) {
    PyObject *ret = PyObject_CallFunction(saver, "O", data);
    if(ret == NULL)
      log_pyerr("Error in pyolc_do_save");
    Py_XDECREF(ret);    
  }
}

void pyolc_do_delete(PyObject *pyolc) {
  Py_XDECREF(pyolc);
}



//*****************************************************************************
// Python OLC wrapper
//*****************************************************************************
void pyolc_menu(SOCKET_DATA *sock, PyObject *olc) {
  PyObject *working_copy = PyDict_GetItemString(olc, "working_copy");
  PyObject         *menu = PyDict_GetItemString(olc, "menu");
  pyolc_do_menu(menu, sock, working_copy);
}

int pyolc_chooser(SOCKET_DATA *sock, PyObject *olc, const char *option) {
  PyObject *working_copy = PyDict_GetItemString(olc, "working_copy");
  PyObject      *chooser = PyDict_GetItemString(olc, "chooser");
  return pyolc_do_chooser(chooser, sock, working_copy, option);
}

bool pyolc_parser(SOCKET_DATA *sock,PyObject *olc,int option,const char *arg) {
  PyObject *working_copy = PyDict_GetItemString(olc, "working_copy");
  PyObject       *parser = PyDict_GetItemString(olc, "parser");
  return pyolc_do_parser(parser, sock, working_copy, option, arg);
}

void pyolc_saver(PyObject *olc) {
  PyObject *working_copy = PyDict_GetItemString(olc, "working_copy");
  PyObject    *orig_copy = PyDict_GetItemString(olc, "orig_copy");
  PyObject        *saver = PyDict_GetItemString(olc, "saver");

  // do we need to copy from our working copy to our original?
  if(orig_copy != working_copy)
    pyolc_do_copyto(working_copy, orig_copy);

  // do we need to save this info to disk?
  if(saver != Py_None)
    pyolc_do_save(saver, orig_copy);
}

void pyolc_deleter(PyObject *olc) {
  Py_DECREF(olc);
}

PyObject *pyolc_do_olc(PyObject *self, PyObject *args) {
  PyObject      *pymenu = NULL;
  PyObject   *pychooser = NULL;
  PyObject    *pyparser = NULL;
  PyObject     *pysaver = NULL;
  PyObject      *pydata = NULL;
  PyObject      *pysock = NULL;
  SOCKET_DATA     *sock = NULL;
  bool         autosave = FALSE;

  // parse out our socket and functions
  if(!PyArg_ParseTuple(args, "OOOOOO|b", 
		       &pysock, &pymenu, &pychooser, &pyparser,
		       &pysaver, &pydata, &autosave)) {
    PyErr_Format(PyExc_TypeError,"Invalid number arguments supplied to do_olc");
    return NULL;
  }

  // make sure our socket is in fact a socket
  if(PySocket_Check(pysock))
    sock = PySocket_AsSocket(pysock);
  else {
    PyErr_Format(PyExc_TypeError, "First argument must be a socket.");
    return NULL;
  }

  // figure out what our C functions will be
  void    *menu = pyolc_menu;
  void *chooser = pyolc_chooser;
  void  *parser = pyolc_parser;
  void   *saver = pyolc_saver;
  void *deleter = pyolc_deleter;
  bool can_copy = (PyObject_HasAttrString(pydata, "copy") && 
		   PyObject_HasAttrString(pydata, "copyTo"));

  // figure out our remaining C OLC functions, if any?
  if(pyparser == Py_None)
    parser = NULL;

  // we do save if we have a save function, or we have a copy and a copyTo func
  if(autosave == TRUE || (pysaver == Py_None && !can_copy))
    saver = NULL;

  // build up our OLC Wrapper
  PyObject *wrapper = PyDict_New();
  PyDict_SetItemString(wrapper, "menu",      pymenu);
  PyDict_SetItemString(wrapper, "chooser",   pychooser);
  PyDict_SetItemString(wrapper, "parser",    pyparser);
  PyDict_SetItemString(wrapper, "saver",     pysaver);

  // set our original copy
  PyDict_SetItemString(wrapper, "orig_copy", pydata);

  // do we want to work on the original copy directly, or no?
  if(autosave == TRUE || !can_copy)
    PyDict_SetItemString(wrapper, "working_copy", pydata);
  else {
    PyObject *working_copy = PyObject_CallMethod(pydata, "copy", NULL);
    if(working_copy != NULL)
      PyDict_SetItemString(wrapper, "working_copy", working_copy);
    // work on the original copy instead
    else {
      log_pyerr("Error calling copy in pyolc_do_olc");
      PyDict_SetItemString(wrapper, "working_copy", pydata);
    }
    Py_XDECREF(working_copy);
  }

  // add the OLC functions
  do_olc(sock, menu, chooser, parser, NULL, NULL, deleter, saver, wrapper);
  return Py_BuildValue("O", Py_None);
}



//*****************************************************************************
// Item OLC
//*****************************************************************************
void pyitem_olc_menu(SOCKET_DATA *sock, PyObject *data) {
  char *str = PyString_AsString(PyObject_GetAttrString(data, "__item_type__"));
  PyObject *olc_funcs = PyDict_GetItemString(pyitem_olc, str);
  PyObject      *menu = PyDict_GetItemString(olc_funcs, "menu");
  pyolc_do_menu(menu, sock, data);
}

int pyitem_olc_chooser(SOCKET_DATA *sock, PyObject *data, const char *option) {
  char *str = PyString_AsString(PyObject_GetAttrString(data, "__item_type__"));
  PyObject *olc_funcs = PyDict_GetItemString(pyitem_olc, str);
  PyObject   *chooser = PyDict_GetItemString(olc_funcs, "chooser");
  return pyolc_do_chooser(chooser, sock, data, option);
}

bool pyitem_olc_parser(SOCKET_DATA *sock, PyObject *data,
		       int choice, const char *arg) {
  char *str = PyString_AsString(PyObject_GetAttrString(data, "__item_type__"));
  PyObject *olc_funcs = PyDict_GetItemString(pyitem_olc, str);
  PyObject    *parser = PyDict_GetItemString(olc_funcs, "parser");
  return pyolc_do_parser(parser, sock, data, choice, arg);
}

void pyitem_olc_from_proto(PyObject *data) {
  char *str = PyString_AsString(PyObject_GetAttrString(data, "__item_type__"));
  PyObject  *olc_funcs = PyDict_GetItemString(pyitem_olc, str);
  PyObject *from_proto = PyDict_GetItemString(olc_funcs, "from_proto");
  pyolc_do_from_proto(from_proto, data);
}

void pyitem_olc_to_proto(PyObject *data, BUFFER *buf) {
  char *str = PyString_AsString(PyObject_GetAttrString(data, "__item_type__"));
  PyObject  *olc_funcs = PyDict_GetItemString(pyitem_olc, str);
  PyObject   *to_proto = PyDict_GetItemString(olc_funcs, "to_proto");
  pyolc_do_to_proto(to_proto, data, buf);
}

PyObject *pyolc_item_add_olc(PyObject *self, PyObject *args) {
  PyObject       *pymenu = NULL;
  PyObject    *pychooser = NULL;
  PyObject     *pyparser = NULL;
  PyObject *pyfrom_proto = NULL;
  PyObject   *pyto_proto = NULL;
  char             *type = NULL;

  // parse out our socket and functions
  if(!PyArg_ParseTuple(args, "sOOOOO", &type, &pymenu, &pychooser,&pyparser,
		       &pyfrom_proto, &pyto_proto)) {
    PyErr_Format(PyExc_TypeError,"Invalid number arguments supplied to do_olc");
    return NULL;
  }

  // what are all the functions we need to pass to item_add_olc?
  void       *menu = pyitem_olc_menu;
  void    *chooser = pyitem_olc_chooser;
  void     *parser = NULL;
  void *from_proto = NULL;
  void   *to_proto = NULL;

  // figure out our remaining C OLC functions, if any?
  if(pyparser != Py_None)
    parser = pyitem_olc_parser;
  if(pyfrom_proto != Py_None)
    from_proto = pyitem_olc_from_proto;
  if(pyto_proto != Py_None)
    to_proto = pyitem_olc_to_proto;

  // A new dictionary, and add all of our Python OLC functions
  PyObject *item_olc = PyDict_New();
  PyDict_SetItemString(item_olc, "menu",       pymenu);
  PyDict_SetItemString(item_olc, "chooser",    pychooser);
  PyDict_SetItemString(item_olc, "parser",     pyparser);
  PyDict_SetItemString(item_olc, "from_proto", pyfrom_proto);
  PyDict_SetItemString(item_olc, "to_proto",   pyto_proto);
  PyDict_SetItemString(pyitem_olc, type, item_olc);

  // add the OLC functions
  item_add_olc(type, menu, chooser, parser, from_proto, to_proto);
  return Py_BuildValue("O", Py_None);
}


PyObject *pyolc_extend(PyObject *self, PyObject *args) {
  PyObject       *pymenu = NULL;
  PyObject    *pychooser = NULL;
  PyObject     *pyparser = NULL;
  PyObject *pyfrom_proto = NULL;
  PyObject   *pyto_proto = NULL;
  char             *type = NULL;
  char              *opt = NULL;

  // parse out our socket and functions
  if(!PyArg_ParseTuple(args, "ssOO|OOO", &type, &opt, &pymenu, &pychooser,&pyparser,
		       &pyfrom_proto, &pyto_proto)) {
    PyErr_Format(PyExc_TypeError,"Invalid number arguments supplied to do_olc");
    return NULL;
  }

  // What OLC are we trying to extend?
  OLC_EXTENDER *ext = NULL;
  if(!strcmp(type, "medit"))
    ext = medit_extend;
  else if(!strcmp(type, "oedit"))
    ext = oedit_extend;
  else if(!strcmp(type, "redit"))
    ext = redit_extend;
  else {
    PyErr_Format(PyExc_TypeError,"Extend options: medit, oedit, and redit.");
    return NULL;
  }

  extenderRegisterPyOpt(ext, *opt, pymenu, pychooser, pyparser, pyfrom_proto,
			pyto_proto);
  return Py_BuildValue("i", 1);
}



//*****************************************************************************
// PyOLC module
//*****************************************************************************
void PyOLC_addMethod(const char *name, void *f, int flags, const char *doc) {
  // make sure our list of methods is created
  if(pyolc_methods == NULL) pyolc_methods = newList();

  // make the Method def
  PyMethodDef *def = calloc(1, sizeof(PyMethodDef));
  def->ml_name     = strdup(name);
  def->ml_meth     = (PyCFunction)f;
  def->ml_flags    = flags;
  def->ml_doc      = (doc ? strdup(doc) : NULL);
  listPut(pyolc_methods, def);
}

PyMODINIT_FUNC
init_PyOLC(void) {
  // add all of our methods
  PyOLC_addMethod("do_olc", pyolc_do_olc, METH_VARARGS,
    "do_olc(sock, menu_func, chooser_func, parse_func, saver_func, data, autosave=False)\n\n"
    "Entry point to the olc system. See olc2/olc.h for documentation.");
  PyOLC_addMethod("item_add_olc", pyolc_item_add_olc, METH_VARARGS, 
    "item_add_olc(itemtype, menu_func, chooser_func, parse_func,\n"
    "             fromproto_func, toproto_func)\n\n"
    "Register a new olc handler for an item type. See items/iedit.h for\n"
    "documentation.");
  PyOLC_addMethod("extend", pyolc_extend, METH_VARARGS,
    "extend(olc_type, optname, menu_func, chooser_func, parse_func = None,\n"
    "       fromproto_func = None, toproto_func = None)\n\n"
    "Register a new olc menu extender. Types are medit, redit, and oedit. See\n"
    "olc2/olc_extender.h for documentation.");

  // create the module
  PyObject *m = Py_InitModule3("olc", makePyMethods(pyolc_methods),
    "This is the Python wrapper for the online creation module. Allows users\n"
    "to set up new menus and handlers for editing data, such as characters,\n"
    "while online.");

  // set some important OLC values as well
  PyObject_SetAttrString(m, "MENU_NOCHOICE", Py_BuildValue("i", MENU_NOCHOICE));
  PyObject_SetAttrString(m, "MENU_CHOICE_INVALID",
			 Py_BuildValue("i", MENU_CHOICE_INVALID));
  PyObject_SetAttrString(m, "MENU_CHOICE_OK",
			 Py_BuildValue("i", MENU_CHOICE_OK));

  pyitem_olc = PyDict_New();
}
#endif // MODULE_OLC2