/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                        V I E W M O L M O D U L E . C                         *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: viewmolmodule.c,v 1.2 2003/11/07 11:17:17 jrh Exp $
* $Log: viewmolmodule.c,v $
* Revision 1.2  2003/11/07 11:17:17  jrh
* Release 2.4
*
* Revision 1.1  2000/12/10 15:18:35  jrh
* Initial revision
*
*/
#include<unistd.h>
#include<X11/Intrinsic.h>
#include<Xm/DrawingA.h>
#include<Xm/ToggleB.h>
#include<GL/glu.h>
#include "viewmol.h"

static PyObject *viewmol_load(PyObject *, PyObject *);
static PyObject *viewmol_save(PyObject *, PyObject *);
static PyObject *viewmol_delete(PyObject *, PyObject *);
static PyObject *viewmol_getMolecules(PyObject *, PyObject *);
static PyObject *viewmol_getLights(PyObject *, PyObject *);
static PyObject *viewmol_getLabels(PyObject *, PyObject *);
static PyObject *viewmol_model(PyObject *, PyObject *);
static PyObject *viewmol_drawingMode(PyObject *, PyObject *);
static PyObject *viewmol_projection(PyObject *, PyObject *);
static PyObject *viewmol_sphereResolution(PyObject *, PyObject *);
static PyObject *viewmol_lineWidth(PyObject *, PyObject *);
static PyObject *viewmol_groundColor(PyObject *, PyObject *);
static PyObject *viewmol_backgroundColor(PyObject *, PyObject *);
static PyObject *viewmol_labelAtoms(PyObject *, PyObject *);
static PyObject *viewmol_saveDrawing(PyObject *, PyObject *);
static PyObject *viewmol_isosurface(PyObject *, PyObject *);
static PyObject *viewmol_showThermodynamics(PyObject *, PyObject *);
static PyObject *viewmol_redraw(PyObject *, PyObject *);
static PyObject *viewmol_getFramesPerSecond(PyObject *, PyObject *);
static PyObject *viewmol_write(PyObject *, PyObject *);
static PyObject *viewmol_registerMenuItem(PyObject *, PyObject *);
static PyObject *viewmol_showWarnings(PyObject *, PyObject *);
static PyObject *viewmol_getWindowSize(PyObject *, PyObject *);
static PyObject *viewmol_quit(PyObject *, PyObject *);
int checkInterrupt(void);

extern XtAppContext app;
extern PyMoleculeSpecObject *molecule_new(void);
extern int load(Widget, caddr_t, XmAnyCallbackStruct *);
extern void saveMolecule(char *);
extern void setModel(Widget, caddr_t, XmDrawingAreaCallbackStruct *);
extern void setDrawingMode(Widget, caddr_t, XmToggleButtonCallbackStruct *);
extern void setProjection(Widget, caddr_t, XmToggleButtonCallbackStruct *);
extern void switchLight(Widget, caddr_t, XmToggleButtonCallbackStruct *);
extern void thermoDialog(Widget, caddr_t, XmAnyCallbackStruct *);
extern void annotateWavefunction(void);
extern void redraw(int);
extern void ende(Widget, caddr_t, XmAnyCallbackStruct *);
extern void saveDrawing(int, int, char *);
extern void selectScreen(int);
extern Boolean normalMode(XtPointer);
extern int findMolecule(PyMoleculeSpecObject *);
extern void deleteMolecule(Widget, caddr_t, XmAnyCallbackStruct *);
extern void infoSetText(char *);
extern void setShape(int, Dimension, Dimension);
extern PyLightSpecObject *light_new(void);

extern struct WINDOW windows[];
extern struct MOLECULE *molecules;
extern struct OPTION *output;
extern struct ANNOTATION *annotation;
extern double lineWidth, sphereres, level, drawingSpeed;
extern int primitive, projectionMode, nAnnotations;
extern int nmolecule, imol, label, outputType, noutput, animate, pythonInterrupt;
extern int showWarning;
extern char *moduleName;

static PyMethodDef viewmol_methods[] = {
  {"load",                viewmol_load, 1},
  {"save",                viewmol_save, 1},
  {"delete",              viewmol_delete, 1},
  {"getMolecules",        viewmol_getMolecules, 1},
  {"getLights",           viewmol_getLights, 1},
  {"getLabels",           viewmol_getLabels, 1},
  {"model",               viewmol_model, 1},
  {"drawingMode",         viewmol_drawingMode, 1},
  {"projection",          viewmol_projection, 1},
  {"sphereResolution",    viewmol_sphereResolution, 1},
  {"lineWidth",           viewmol_lineWidth, 1},
  {"groundColor",         viewmol_groundColor, 1},
  {"backgroundColor",     viewmol_backgroundColor, 1},
  {"labelAtoms",          viewmol_labelAtoms, 1},
  {"saveDrawing",         viewmol_saveDrawing, 1},
  {"isosurface",          viewmol_isosurface, 1},
  {"showThermodynamics",  viewmol_showThermodynamics, 1},
  {"redraw",              viewmol_redraw, 1},
  {"getFramesPerSecond",  viewmol_getFramesPerSecond, 1},
  {"write",               viewmol_write, 1},
  {"registerMenuItem",    viewmol_registerMenuItem, 1},
  {"showWarnings",        viewmol_showWarnings, 1},
  {"getWindowSize",       viewmol_getWindowSize, 1},
  {"quit",                viewmol_quit, 1},
  {NULL,                  NULL}
};

static PyObject *viewmol_load(PyObject *self, PyObject *args)
{
  PyObject *new;
  char *filename;

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "s", &filename)) return NULL;
  if (load((Widget)0, filename, (XmAnyCallbackStruct *)0))
  {
    new=(PyObject *)(molecules[imol].pyObject);
    return(new);
  }
  else
  {
    PyErr_SetString(PyExc_ValueError, "File does not exist");
    return(NULL);
  }
}

static PyObject *viewmol_save(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *molecule;
  int which, found;
  char *filename, *type;
  register int i;

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "Oss", &molecule, &filename, &type)) return NULL;
  if (!molecule)
  {
    PyErr_SetString(PyExc_ValueError, "Molecule does not exist");
    return(NULL);
  }
  found=FALSE;
  for (i=0; i<noutput; i++)
  {
    if (!strcmp(type, output[i].flag))
    {
      outputType=i;
	found=TRUE;
      break;
    }
  }
  if (!found)
  {
    PyErr_SetString(PyExc_ValueError, "Type is unknown");
    return(NULL);
  }
  which=molecule->moleculeID;
  i=windows[VIEWER].set;
  windows[VIEWER].set=which;
  saveMolecule(filename);
  windows[VIEWER].set=i;
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *viewmol_delete(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *molecule;
  int which, set_save;

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "O", &molecule)) return NULL;
  if (!molecule)
  {
    PyErr_SetString(PyExc_ValueError, "Molecule does not exist");
    return(NULL);
  }
  which=molecule->moleculeID;
  set_save=windows[VIEWER].set;
  windows[VIEWER].set=which;
  deleteMolecule((Widget)0, (caddr_t)FALSE, (XmAnyCallbackStruct *)0);
  if (set_save != which)
    windows[VIEWER].set=set_save;
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *viewmol_getMolecules(PyObject *self, PyObject *args)
{
  PyObject *list;
  int i;

  if (checkInterrupt()) return(NULL);
  if ((list=PyList_New(nmolecule)))
  {
    for (i=0; i<nmolecule; i++)
    {
	PyList_SetItem(list, i, (PyObject *)molecules[i].pyObject);
    }
    return((PyObject *)list);
  }
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *viewmol_getLights(PyObject *self, PyObject *args)
{
  PyLightSpecObject *new;
  PyObject *list;

  if (checkInterrupt()) return(NULL);
  if ((list=PyList_New(2)))
  {
    new=light_new();
    new->lightID=0;
    PyList_SetItem(list, 0, (PyObject *)new);
    new=light_new();
    new->lightID=1;
    PyList_SetItem(list, 1, (PyObject *)new);
    return((PyObject *)list);
  }
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *viewmol_getLabels(PyObject *self, PyObject *args)
{
  PyObject *list;
  int i;

  if (checkInterrupt()) return(NULL);
  if ((list=PyList_New(nAnnotations)))
  {
    for (i=0; i<nAnnotations; i++)
    {
	PyList_SetItem(list, i, (PyObject *)annotation[i].pyObject);
    }
    return((PyObject *)list);
  }
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *viewmol_model(PyObject *self, PyObject *args)
{
  int model=(-1);

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "|i", &model)) return(NULL);
  if (model == -1) /* get */
    return(PyInt_FromLong((long)windows[VIEWER].mode));
  else             /* set */
  {
    setModel((Widget)0, (caddr_t)model, (XmDrawingAreaCallbackStruct *)0);
    Py_INCREF(Py_None);
    return(Py_None);
  }
}

static PyObject *viewmol_drawingMode(PyObject *self, PyObject *args)
{
  XmToggleButtonCallbackStruct data;
  int drawingMode=(-1);

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "|i", &drawingMode)) return(NULL);
  if (drawingMode == -1) /* get */
    return(PyInt_FromLong((long)primitive));
  else                   /* set */
  {
    data.set=TRUE;
    setDrawingMode((Widget)0, (caddr_t)drawingMode, &data);
    Py_INCREF(Py_None);
    return(Py_None);
  }
}

static PyObject *viewmol_projection(PyObject *self, PyObject *args)
{
  XmToggleButtonCallbackStruct data;
  int projection=(-1);

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "|i", &projection)) return(NULL);
  if (projection == -1) /* get */
    return(PyInt_FromLong((long)projectionMode));
  else                  /* set */
  {
    data.set=TRUE;
    setProjection((Widget)0, (caddr_t)projection, &data);
    Py_INCREF(Py_None);
    return(Py_None);
  }
}

static PyObject *viewmol_sphereResolution(PyObject *self, PyObject *args)
{
  int sr=(-1);

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "|i", &sr)) return(NULL);
  if (sr == -1) /* get */
    return(PyInt_FromLong((long)sphereres));
  else          /* set */
  {
    sphereres=(double)sr;
    Py_INCREF(Py_None);
    return(Py_None);
  }
}

static PyObject *viewmol_lineWidth(PyObject *self, PyObject *args)
{
  int lw=(-1);

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "|i", &lw)) return(NULL);
  if (lw == -1) /* get */
    return(PyInt_FromLong((long)lineWidth));
  else          /* set */
  {
    lineWidth=(double)lw;
    Py_INCREF(Py_None);
    return(Py_None);
  }
}

static PyObject *viewmol_groundColor(PyObject *self, PyObject *args)
{
  PyObject *tuple;
  float r=-1.0, g=-1.0, b=-1.0;

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "|fff", &r, &g, &b)) return(NULL);
  if (r == -1.0 && g == -1.0 && b == -1.0) /* get */
  {
    if ((tuple=PyTuple_New(3)))
    {
      PyTuple_SetItem(tuple, 0, PyFloat_FromDouble((double)windows[VIEWER].foreground_rgb[0]));
      PyTuple_SetItem(tuple, 1, PyFloat_FromDouble((double)windows[VIEWER].foreground_rgb[1]));
      PyTuple_SetItem(tuple, 2, PyFloat_FromDouble((double)windows[VIEWER].foreground_rgb[2]));
      return(tuple);
    }
  }
  else                                     /* set */
  {
    if (r >= 0.0 && r <= 1.0 && g >= 0.0 && g <= 1.0 && b >= 0.0 && b <= 1.0)
    {
      windows[VIEWER].foreground_rgb[0]=r;
      windows[VIEWER].foreground_rgb[1]=g;
      windows[VIEWER].foreground_rgb[2]=b;
    }
    else
    {
      PyErr_SetString(PyExc_ValueError, "Color specifications have to be between 0.0 and 1.0.");
      return(NULL);
    }
  }
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *viewmol_backgroundColor(PyObject *self, PyObject *args)
{
  PyObject *tuple;
  float r=-1.0, g=-1.0, b=-1.0;

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "|fff", &r, &g, &b)) return(NULL);
  if (r == -1.0 && g == -1.0 && b == -1.0) /* get */
  {
    if ((tuple=PyTuple_New(3)))
    {
      PyTuple_SetItem(tuple, 0, PyFloat_FromDouble((double)windows[VIEWER].background_rgb[0]));
      PyTuple_SetItem(tuple, 1, PyFloat_FromDouble((double)windows[VIEWER].background_rgb[1]));
      PyTuple_SetItem(tuple, 2, PyFloat_FromDouble((double)windows[VIEWER].background_rgb[2]));
	return(tuple);
    }
  }
  else                                     /* set */
  {
    if (r >= 0.0 && r <= 1.0 && g >= 0.0 && g <= 1.0 && b >= 0.0 && b <= 1.0)
    {
      windows[VIEWER].background_rgb[0]=r;
      windows[VIEWER].background_rgb[1]=g;
      windows[VIEWER].background_rgb[2]=b;
    }
    else
    {
      PyErr_SetString(PyExc_ValueError, "Color specifications have to be between 0.0 and 1.0.");
      return(NULL);
    }
  }
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *viewmol_labelAtoms(PyObject *self, PyObject *args)
{
  int l;

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "i", &l)) return(NULL);
  if (l != TRUE && l != FALSE)
  {
      PyErr_SetString(PyExc_ValueError, "Label must be ON or OFF");
      return(NULL);
  }
  label=l;
  redraw(VIEWER);
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *viewmol_saveDrawing(PyObject *self, PyObject *args)
{
  int format;
  char *filename;

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "is", &format, &filename)) return NULL;
  if (!access(filename, F_OK))
  {
    PyErr_SetString(PyExc_IOError, "File already exists.");
    return(NULL);
  }
  if (format != TIFFFILE && format != PNGFILE && format != HPGL &&
      format != POSTSCRIPT && format != RAYTRACER)
  {
    PyErr_SetString(PyExc_ValueError, "Format not recognised.");
    return(NULL);
  }
  saveDrawing(VIEWER, format, filename);
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *viewmol_isosurface(PyObject *self, PyObject *args)
{
  double l=(-1.0e0);

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "|d", &l)) return(NULL);
  if (l == -1.0e0)
    return(PyFloat_FromDouble(level));
  else
  {
    level=l;
    annotateWavefunction();
    Py_INCREF(Py_None);
    return(Py_None);
  }
}

static PyObject *viewmol_showThermodynamics(PyObject *self, PyObject *args)
{
  int selected=0;

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "|i", &selected)) return(NULL);

  thermoDialog((Widget)0, (caddr_t)0, (XmAnyCallbackStruct *)0);
  if (selected >= 0  && selected <= nmolecule)
    selectScreen(selected);
  else
  {
    if (selected == (-1))
      selectScreen(nmolecule+1);
    else
    {
      PyErr_SetString(PyExc_ValueError, "No such molecule.");
      return(NULL);
    }
  }
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *viewmol_redraw(PyObject *self, PyObject *args)
{
  if (checkInterrupt()) return(NULL);
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *viewmol_getFramesPerSecond(PyObject *self, PyObject *args)
{
  if (checkInterrupt()) return(NULL);
  return(PyFloat_FromDouble(drawingSpeed));
}

static PyObject *viewmol_write(PyObject *self, PyObject *args)
{
  char *string;

  if (!PyArg_ParseTuple(args, "s", &string)) return(NULL);
  infoSetText(string);
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *viewmol_registerMenuItem(PyObject *self, PyObject *args)
{
  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "s", &moduleName)) return(NULL);

  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *viewmol_showWarnings(PyObject *self, PyObject *args)
{
  int warning;

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "i", &warning)) return(NULL);
  if (warning != TRUE && warning != FALSE)
  {
      PyErr_SetString(PyExc_ValueError, "Argument must be ON or OFF");
      return(NULL);
  }
  showWarning=warning;
  redraw(VIEWER);
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *viewmol_getWindowSize(PyObject *self, PyObject *args)
{
  PyObject *tuple;
  Dimension width, height;

  if (checkInterrupt()) return(NULL);
  if ((tuple=PyTuple_New(2)))
  {
    XtVaGetValues(windows[VIEWER].widget, XtNwidth, &width, XtNheight, &height, NULL);
    PyTuple_SetItem(tuple, 0, PyInt_FromLong((long)width));
    PyTuple_SetItem(tuple, 1, PyInt_FromLong((long)height));
    return(tuple);
  }
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *viewmol_quit(PyObject *self, PyObject *args)
{
  if (checkInterrupt()) return(NULL);
  ende((Widget)0, (caddr_t)0, (XmAnyCallbackStruct *)0);
  Py_INCREF(Py_None);
  return(Py_None);
}

int checkInterrupt()
{
  XEvent event;

  if (pythonInterrupt)
  {
    PyErr_SetString(PyExc_KeyboardInterrupt, "");
    pythonInterrupt=FALSE;
    return(TRUE);
  }
  else
  {
    if (animate == ANIMATE)
      (void)normalMode(0);
    else
      redraw(VIEWER);
    while (XtAppPending(app))
    {
      XtAppNextEvent(app, &event);
      XtDispatchEvent(&event);
    }
    return(FALSE);
  }
}

void initViewmolModule(void)
{
  PyObject *viewmol, *m, *d, *v;

  viewmol=PyImport_AddModule("viewmol");
  m=Py_InitModule("viewmol", viewmol_methods);
  d=PyModule_GetDict(m);
  v=PyString_FromString(VERSION);
  PyDict_SetItemString(d, "version", v);
  PyDict_SetItemString(d, "__version__", v);
  PyDict_SetItemString(d, "ON", PyInt_FromLong((long)TRUE));
  PyDict_SetItemString(d, "OFF", PyInt_FromLong((long)FALSE));
  PyDict_SetItemString(d, "WIREMODEL", PyInt_FromLong((long)WIREMODEL));
  PyDict_SetItemString(d, "STICKMODEL", PyInt_FromLong((long)STICKMODEL));
  PyDict_SetItemString(d, "BALLMODEL", PyInt_FromLong((long)BALLMODEL));
  PyDict_SetItemString(d, "CPKMODEL", PyInt_FromLong((long)CUPMODEL));
  PyDict_SetItemString(d, "ORTHO", PyInt_FromLong((long)ORTHO));
  PyDict_SetItemString(d, "PERSPECTIVE", PyInt_FromLong((long)PERSPECTIVE));
  PyDict_SetItemString(d, "DOT", PyInt_FromLong((long)GLU_POINT));
  PyDict_SetItemString(d, "LINE", PyInt_FromLong((long)GLU_LINE));
  PyDict_SetItemString(d, "SURFACE", PyInt_FromLong((long)GLU_FILL));
  PyDict_SetItemString(d, "TIFF", PyInt_FromLong((long)TIFFFILE));
  PyDict_SetItemString(d, "PNG", PyInt_FromLong((long)PNGFILE));
  PyDict_SetItemString(d, "HPGL", PyInt_FromLong((long)HPGL));
  PyDict_SetItemString(d, "POSTSCRIPT", PyInt_FromLong((long)POSTSCRIPT));
  PyDict_SetItemString(d, "RAYTRACER", PyInt_FromLong((long)RAYTRACER));
  PyDict_SetItemString(d, "REACTION", PyInt_FromLong((long)(-1)));
  Py_DECREF(v);
  m=PyImport_Import(PyString_FromString("sys"));
  d=PyModule_GetDict(m);
  PyDict_SetItemString(d, "stdout", viewmol);
  PyDict_SetItemString(d, "stderr", viewmol);
}
