/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                       M O L E C U L E M O D U L E . C                        *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: moleculemodule.c,v 1.3 2004/08/29 14:57:28 jrh Exp $
* $Log: moleculemodule.c,v $
* Revision 1.3  2004/08/29 14:57:28  jrh
* Release 2.4.1
*
* Revision 1.2  2003/11/07 11:07:39  jrh
* Release 2.4
*
* Revision 1.1  2000/12/10 15:12:50  jrh
* Initial revision
*
*/     
#include<stdio.h>
#include<Xm/DrawingA.h>
#include<Xm/ToggleB.h>
#include "viewmol.h"

#define PyMolecule_API_pointers 1
#define PyMoleculeSpec_Type_NUM 0

#define ENTHALPY      1L
#define ENTROPY       2L
#define GIBBS_ENERGY  3L
#define HEAT_CAPACITY 4L

PyMoleculeSpecObject *molecule_new(void);
static PyObject *molecule_translate(PyObject *, PyObject *);
static PyObject *molecule_rotate(PyObject *, PyObject *);
static PyObject *molecule_getSpectrum(PyObject *, PyObject *);
static PyObject *molecule_getEnergyLevels(PyObject *, PyObject *);
static PyObject *molecule_getHistory(PyObject *, PyObject *);
static PyObject *molecule_showForces(PyObject *, PyObject *);
static PyObject *molecule_getAtoms(PyObject *, PyObject *);
static PyObject *molecule_getBonds(PyObject *, PyObject *);
static PyObject *molecule_getWavenumbers(PyObject *, PyObject *);
static PyObject *molecule_getMOEnergies(PyObject *, PyObject *);
static PyObject *molecule_title(PyObject *, PyObject *);
static PyObject *molecule_bondAverage(PyObject *, PyObject *);
static PyObject *molecule_bondLength(PyObject *, PyObject *);
static PyObject *molecule_bondAngle(PyObject *, PyObject *);
static PyObject *molecule_torsionAngle(PyObject *, PyObject *);
static PyObject *molecule_getThermodynamics(PyObject *, PyObject *);
static PyObject *molecule_reaction(PyObject *, PyObject *);
static PyObject *molecule_showElectrons(PyObject *, PyObject *);
static PyObject *molecule_showGrid(PyObject *, PyObject *);
static PyObject *molecule_selectBasisfunction(PyObject *, PyObject *);
static PyObject *molecule_unitCell(PyObject *, PyObject *);
static PyObject *molecule_millerPlane(PyObject *, PyObject *);
static PyObject *molecule_addAtom(PyObject *, PyObject *);
static PyObject *molecule_molecule(PyObject *, PyObject *);
static void setInternal(struct MOLECULE *, int, int, int, int, int, char *);
static PyObject *molecule_getattr(PyMoleculeSpecObject *, char *);
static void molecule_dealloc(PyMoleculeSpecObject *);

extern PyAtomSpecObject *atom_new(void);
extern void getRotation(int, int, int, int);
extern void getTranslation(int, int, int);
extern void getEnlargement(int, int);
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 initMODiagram(Widget, caddr_t, caddr_t);
extern void initHistory(Widget, caddr_t, caddr_t);
extern void deleteMolecule(Widget, caddr_t, XmAnyCallbackStruct *);
extern PySpectrumSpecObject *spectrum_new(void);
extern PyHistorySpecObject *history_new(void);
extern PyEnergyLevelSpecObject *energylevels_new(void);
extern double bondAverage(struct MOLECULE *, int);
extern double bondLength(struct ATOM *, int, int);
extern double bondAngle(struct ATOM *, int, int, int);
extern double torsionAngle(struct ATOM *, int, int, int, int);
extern void *getmem(size_t, size_t);
extern void *expmem(void *, size_t, size_t);
extern void fremem(void **);
extern void modifyGeometry(int, char *);
extern void calcmo(void);
extern int  existsGridObject(void);
extern void deleteGridObjects(void);
extern void redraw(int);
/* extern char *bfname(int);*/
extern void bfnamn(int, int *, int*);
extern void annotateWavefunction(void);
extern void buildMolecule(struct MOLECULE *, int, int, int);
extern void expandCell(int, int);
extern void millerPlane(void);
extern int checkInterrupt(void);
extern void newMolecule(Widget, caddr_t, XmAnyCallbackStruct *);

extern struct WINDOW windows[];
extern struct MOLECULE *molecules;
extern struct ELEMENT *elements;
extern PyObject *moleculeModule;
extern double temp, gridres;
extern float *rotObject;
extern int nmolecule, ne, moveItem, rotateXY, rotateZ, showForces;
extern int iwavef, interp, showUnitCell, element;

static char PyMoleculeSpec_Type__doc__[] =
  "Molecule specification";

statichere PyTypeObject PyMoleculeSpec_Type = {
  PyObject_HEAD_INIT(NULL)
  0,                               /*ob_size*/
  "MoleculeSpec",                  /*tp_name*/
  sizeof(PyMoleculeSpecObject),    /*tp_basicsize*/
  0,                               /*tp_itemsize*/
  /* methods */
  (destructor)molecule_dealloc,    /*tp_dealloc*/
  0,                               /*tp_print*/
  (getattrfunc)molecule_getattr,   /*tp_getattr*/
  0,                               /*tp_setattr*/
  0,                               /*tp_compare*/
  0,                               /*tp_repr*/
  0,                               /*tp_as_number*/
  0,                               /*tp_as_sequence*/
  0,                               /*tp_as_mapping*/
  0,                               /*tp_hash*/
  0,                               /*tp_call*/
  0,                               /*tp_str*/
  0,                               /*tp_getattro*/
  0,                               /*tp_setattro*/
  /* Space for future expansion */
  0L,0L,
  /* Documentation string */
  PyMoleculeSpec_Type__doc__
};

static PyMethodDef molecule_methods[] = {
  {"translate",           molecule_translate,  1},
  {"rotate",              molecule_rotate,  1},
  {"getSpectrum",         molecule_getSpectrum, 1},
  {"getEnergyLevels",     molecule_getEnergyLevels, 1},
  {"getHistory",          molecule_getHistory, 1},
  {"showForces",          molecule_showForces, 1},
  {"getAtoms",            molecule_getAtoms, 1},
  {"getBonds",            molecule_getBonds, 1},
  {"getWavenumbers",      molecule_getWavenumbers, 1},
  {"getMOEnergies",       molecule_getMOEnergies, 1},
  {"title",               molecule_title, 1},
  {"bondAverage",         molecule_bondAverage, 1},
  {"bondLength",          molecule_bondLength, 1},
  {"bondAngle",           molecule_bondAngle, 1},
  {"torsionAngle",        molecule_torsionAngle, 1},
  {"getThermodynamics",   molecule_getThermodynamics, 1},
  {"reaction",            molecule_reaction, 1},
  {"showElectrons",       molecule_showElectrons, 1},
  {"showGrid",            molecule_showGrid, 1},
  {"selectBasisfunction", molecule_selectBasisfunction, 1},
  {"unitCell",            molecule_unitCell, 1},
  {"millerPlane",         molecule_millerPlane, 1},
  {"addAtom",             molecule_addAtom, 1},
  {"molecule",            molecule_molecule, 1},
  {NULL,                  NULL}
};

static PyObject *molecule_rotate(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  int moveItem_save, which, x, y, z;

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "iii", &x, &y, &z)) return(NULL);
 
  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID+MOLECULES;
  rotObject[4*which]=rotObject[4*which+1]=rotObject[4*which+2]=0.0;
  rotObject[4*which+3]=1.0;
  moveItem_save=moveItem;
  moveItem=which;
  getRotation(x, y, z, 1);
  moveItem=moveItem_save;
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *molecule_translate(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  int moveItem_save, rotateXY_save, rotateZ_save, which, x, y, z;

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "iii", &x, &y, &z)) return(NULL);

  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID+MOLECULES;
  moveItem_save=moveItem;
  moveItem=which;
  rotateXY_save=rotateXY;
  rotateZ_save=rotateZ;
  rotateXY=1;
  rotateZ=1;
  getTranslation(x, y, 1);
  getEnlargement(z, 1);
  moveItem=moveItem_save;
  rotateXY=rotateXY_save;
  rotateZ=rotateZ_save;
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *molecule_getSpectrum(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  PySpectrumSpecObject *spectrum;
  int which;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  if (molecules[which].nmodes == 0)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
  spectrum=spectrum_new();
  if (spectrum == NULL) return(NULL);
  spectrum->spectrumID=which;
  return((PyObject *)spectrum);
}

static PyObject *molecule_getEnergyLevels(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  PyEnergyLevelSpecObject *energylevels;
  int which;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  if (molecules[which].orbitals == NULL)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
  energylevels=energylevels_new();
  if (energylevels == NULL) return(NULL);
  energylevels->energyLevelID=which;
  return((PyObject *)energylevels);
}

static PyObject *molecule_getHistory(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  PyHistorySpecObject *history;
  int which;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  if (molecules[which].nhist < 2)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
  history=history_new();
  if (history == NULL) return(NULL);
  history->historyID=which;
  return((PyObject *)history);
}

static PyObject *molecule_showForces(PyObject *self, PyObject *args)
{
  int show;

  if (checkInterrupt()) return(NULL);
  if (!PyArg_ParseTuple(args, "i", &show)) return(NULL);
  if (show != TRUE && show != FALSE)
  {
    PyErr_SetString(PyExc_ValueError, "Mode must be ON or OFF");
    return(NULL);
  }
  else
    showForces=show;
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *molecule_getAtoms(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  PyAtomSpecObject *atom;
  PyObject *list;
  int which, i;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  if ((list=PyList_New(molecules[which].na)))
  {
    for (i=0; i<molecules[which].na; i++)
    {
      atom=atom_new();
      if (atom == NULL) continue;
      atom->moleculeID=which;
      atom->atomID=i;
      PyList_SetItem(list, i, (PyObject *)atom);
    }
    return((PyObject *)list);
  }
  else
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
}

static PyObject *molecule_getBonds(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  PyObject *list, *tuple;
  int which, i;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  if ((list=PyList_New(molecules[which].nb)))
  {
    for (i=0; i<molecules[which].nb; i++)
    {
      if ((tuple=PyTuple_New(3)))
      {
        PyTuple_SetItem(tuple, 0, PyInt_FromLong((long)molecules[which].bonds[i].first));
        PyTuple_SetItem(tuple, 1, PyInt_FromLong((long)molecules[which].bonds[i].second));
        PyTuple_SetItem(tuple, 2, PyInt_FromLong((long)molecules[which].bonds[i].order));
        PyList_SetItem(list, i, tuple);
      }
    }
  }
  return((PyObject *)list);
}

static PyObject *molecule_getWavenumbers(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  PyObject *list, *tuple;
  int which, i;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  if ((list=PyList_New(molecules[which].nmodes)))
  {
    for (i=0; i<molecules[which].nmodes; i++)
    {
      if ((tuple=PyTuple_New(5)))
      {
        PyTuple_SetItem(tuple, 0, PyFloat_FromDouble(molecules[which].normal_modes[i].wavenumber));
        PyTuple_SetItem(tuple, 1, PyFloat_FromDouble(molecules[which].normal_modes[i].ir_intensity));
        PyTuple_SetItem(tuple, 2, PyFloat_FromDouble(molecules[which].normal_modes[i].raman_intensity));
        PyTuple_SetItem(tuple, 3, PyFloat_FromDouble(molecules[which].normal_modes[i].ins_intensity));
        PyTuple_SetItem(tuple, 4, PyString_FromString(molecules[which].normal_modes[i].sym));
        PyList_SetItem(list, i, tuple);
      }
    }
  }
  return((PyObject *)list);
}

static PyObject *molecule_getMOEnergies(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  PyObject *list, *tuple;
  int which, i;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  if (molecules[which].orbitals == NULL)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
  if ((list=PyList_New(molecules[which].nbasfu)))
  {
    for (i=0; i<molecules[which].nbasfu; i++)
    {
      if ((tuple=PyTuple_New(4)))
      {
        PyTuple_SetItem(tuple, 0, PyFloat_FromDouble(molecules[which].orbitals[i].energy));
        PyTuple_SetItem(tuple, 1, PyFloat_FromDouble(molecules[which].orbitals[i].occupation));
        PyTuple_SetItem(tuple, 2, PyInt_FromLong((long)(molecules[which].orbitals[i].spin)));
        PyTuple_SetItem(tuple, 3, PyString_FromString(molecules[which].orbitals[i].symmetry));
        PyList_SetItem(list, i, tuple);
      }
    }
  }
  return((PyObject *)list);
}

static PyObject *molecule_title(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  int which;
  char *title=NULL;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  if (!PyArg_ParseTuple(args, "|s", &title)) return(NULL);
  if (title == NULL) /* get */
    return(PyString_FromString(molecules[which].title));
  else
  {
    strncpy(molecules[which].title, title, MAXLENLINE-1);
    Py_INCREF(Py_None);
    return(Py_None);
  }
}

static PyObject *molecule_bondAverage(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  PyAtomSpecObject *atom;
  double r;
  int which, i;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }

  if (!PyArg_ParseTuple(args, "O", &atom)) return(NULL);
  if (!atom)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  if (which != atom->moleculeID)
  {
    PyErr_SetString(PyExc_ValueError, "Atom given is not part of the molecule given");
    return(NULL);
  }
  i=atom->atomID;
  r=bondAverage(&molecules[which], i);
  return(PyFloat_FromDouble(r));
}

static PyObject *molecule_bondLength(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  PyAtomSpecObject *atom1, *atom2;
  double r;
  int which, i, j;
  char value[MAXLENLINE], *unit=NULL;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;

  if (!PyArg_ParseTuple(args, "OO|ds", &atom1, &atom2, &r, &unit)) return(NULL);
  if (atom1 && atom2)
  {
    if (which != atom1->moleculeID || which != atom2->moleculeID)
    {
      PyErr_SetString(PyExc_ValueError, "Atom given is not part of the molecule given");
      return(NULL);
    }
    i=atom1->atomID;
    j=atom2->atomID;
    if (unit == NULL) /* get */
    {
      r=bondLength((&molecules[which])->atoms, i, j);
      return(PyFloat_FromDouble(r));
    }
    else              /* set */
    {
      sprintf(value, "%f %s", r, unit);
      setInternal(&molecules[which], BONDLENGTH, i, j, 0, 0, value);
    }
  }
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *molecule_bondAngle(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  PyAtomSpecObject *atom1, *atom2, *atom3;
  double r=400.0e0;
  int which, i, j, k;
  char value[MAXLENLINE];

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;

  if (!PyArg_ParseTuple(args, "OOO|d", &atom1, &atom2, &atom3, &r)) return(NULL);
  if (atom1 && atom2 && atom3)
  {
    if (which != atom1->moleculeID || which != atom2->moleculeID ||
        which != atom3->moleculeID)
    {
      PyErr_SetString(PyExc_ValueError, "Atom given is not part of the molecule given");
      return(NULL);
    }
    i=atom1->atomID;
    j=atom2->atomID;
    k=atom3->atomID;
    if (r == 400.0e0) /* get */
    {
      r=bondAngle((&molecules[which])->atoms, i, j, k);
      return(PyFloat_FromDouble(r));
    }
    else              /* set */
    {
      sprintf(value, "%f", r);
      setInternal(&molecules[which], ANGLE, i, j, k, 0, value);
    }
  }
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *molecule_torsionAngle(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  PyAtomSpecObject *atom1, *atom2, *atom3, *atom4;
  double r=400.0e0;
  int which, i, j, k, l;
  char value[MAXLENLINE];

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }

  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;

  if (!PyArg_ParseTuple(args, "OOOO|d", &atom1, &atom2, &atom3, &atom4, &r)) return(NULL);
  if (atom1 && atom2 && atom3 && atom4)
  {
    if (which != atom1->moleculeID || which != atom2->moleculeID ||
        which != atom3->moleculeID || which != atom4->moleculeID)
    {
      PyErr_SetString(PyExc_ValueError, "Atom given is not part of the molecule given");
      return(NULL);
    }
    i=atom1->atomID;
    j=atom2->atomID;
    k=atom3->atomID;
    l=atom4->atomID;
    if (r == 400.e0) /* get */
    {
      r=torsionAngle((&molecules[which])->atoms, i, j, k, l);
      return(PyFloat_FromDouble(r));
    }
    else             /* set */
    {
      sprintf(value, "%f", r);
      setInternal(&molecules[which], TORSION, i, j, k, l, value);
    }
  }
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *molecule_getThermodynamics(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  struct MOLECULE *mol;
  double value=0.0;
  int which, quantity, type;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }

  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  if (!PyArg_ParseTuple(args, "ii", &quantity, &type)) return NULL;
  mol=&molecules[which];
  switch (quantity)
  {
    case ENTHALPY:      switch (type)
                        {
                          case TRANSLATION: value=0.001*mol->htrl;
                                            break;
                          case PV:          value=0.001*mol->pv;
                                            break;
                          case ROTATION:    value=0.001*mol->hrot;
                                            break;
                          case VIBRATION:   value=0.001*mol->hvib;
                                            break;
                          case TOTAL:       value=0.001*(mol->htrl+mol->pv+mol->hrot+mol->hvib);
                                            break;
                        }
                        break;
    case ENTROPY:       switch (type)
                        {
                          case TRANSLATION: value=mol->strl;
                                            break;
                          case PV:          PyErr_SetString(PyExc_ValueError, "pV is invalid for entropy");
                                            return(NULL);
                                            /* NOTREACHED */
                                            break;
                          case ROTATION:    value=mol->srot;
                                            break;
                          case VIBRATION:   value=mol->svib;
                                            break;
                          case TOTAL:       value=mol->strl+mol->srot+mol->svib;
                                            break;
                        }
                        break;
    case GIBBS_ENERGY:  switch (type)
                        {
                          case TRANSLATION: value=0.001*(mol->htrl-temp*mol->strl);
                                            break;
                          case PV:          value=0.001*mol->pv;
                                            break;
                          case ROTATION:    value=0.001*(mol->hrot-temp*mol->srot);
                                            break;
                          case VIBRATION:   value=0.001*(mol->hvib-temp*mol->svib);
                                            break;
                          case TOTAL:       value=0.001*(mol->htrl+mol->pv+mol->hrot
                                                 +mol->hvib-temp*(mol->strl+mol->srot+mol->svib));
                                            break;
                        }
                        break;
    case HEAT_CAPACITY: switch (type)
                        {
                          case TRANSLATION: value=mol->ctrl;
                                            break;
                          case PV:          PyErr_SetString(PyExc_ValueError, "pV is invalid for heat capacity");
                                            return(NULL);
                                            /* NOTREACHED */
                                            break;
                          case ROTATION:    value=mol->crot;
                                            break;
                          case VIBRATION:   value=mol->cvib;
                                            break;
                          case TOTAL:       value=mol->ctrl+mol->crot+mol->cvib;
                                            break;
                        }
	                break;
  }
  return(PyFloat_FromDouble(value));
}

static PyObject *molecule_reaction(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  struct MOLECULE *mol;
  int which, side=0;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }

  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  side=0;
  if (!PyArg_ParseTuple(args, "|i", &side)) return NULL;
  mol=&molecules[which];
  if (side == 0) /* get */
    return(PyInt_FromLong((long)mol->reaction));
  else           /* set */
    mol->reaction=side;
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *molecule_showElectrons(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  struct MOLECULE *mol;
  int which;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }

  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  if (!PyArg_ParseTuple(args, "i|di", &iwavef, &gridres, &interp)) return NULL;
  mol=&molecules[which];

  switch (iwavef)
  {
    case FALSE:             deleteGridObjects();
                            break;
    case BASIS_FUNCTION:
    case BASIS_IN_MO:
    case MOLECULAR_ORBITAL:
    case DENSITY:           if (!existsGridObject())
                            {
                              calcmo();
                              annotateWavefunction();
                            }
                            break;
    default:                PyErr_SetString(PyExc_ValueError,
                                            "Unknown type of wave function");
                            return(NULL);
                            /* NOTREACHED */
                            break;
  }
  redraw(VIEWER);
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *molecule_showGrid(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  struct MOLECULE *mol;
  int which, igrid;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }

  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  if (!PyArg_ParseTuple(args, "i|i", &igrid, &interp)) return NULL;
  mol=&molecules[which];
  if (mol->gridObjects == (struct GRIDOBJECT *)NULL)
  {
    PyErr_SetString(PyExc_ValueError, "No grids have been read for this molecule");
    return(NULL);
  }
  if (igrid < 1 || igrid > mol->ngridobjects)
  {
    PyErr_SetString(PyExc_ValueError, "A grid with this number does not exist");
    return(NULL);
  }
  iwavef=DENSITY+igrid;
  redraw(VIEWER);
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *molecule_selectBasisfunction(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  PyAtomSpecObject *atom;
  struct MOLECULE *mol;
  int which, count;
  char *name;
  register int i, found=FALSE;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }

  if (!PyArg_ParseTuple(args, "Osi", &atom, &name, &count)) return NULL;
  if (!atom)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  } 
  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  if (which != atom->moleculeID)
  {
    PyErr_SetString(PyExc_ValueError, "Atom given is not part of the molecule given");
    return(NULL);
  }
  mol=&molecules[which];
  for (i=0; i<mol->atoms[atom->atomID].nBasisFunctions; i++)
  {
    if (!strcmp(mol->atoms[atom->atomID].basisFunctionNames[i], name))
    {
      count--;
      if (count == 0)
      {
        mol->ibasfu=i;
        found=TRUE;
        break;
      }
    }
  }
  if (!found)
  {
    PyErr_SetString(PyExc_ValueError, "No such basisfunction");
    return(NULL);
  }
  else
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }
}

static PyObject *molecule_unitCell(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  struct MOLECULE *mol;
  double xfac, yfac, zfac;
  int visible, which;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }

  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  mol=&molecules[which];
  if (mol->unitcell == 0)
  {
    PyErr_SetString(PyExc_ValueError, "No unit cell defined");
    return(NULL);
  }
  xfac=mol->unitcell->factor[0];
  yfac=mol->unitcell->factor[1];
  zfac=mol->unitcell->factor[2];
  if (!PyArg_ParseTuple(args, "i|ddd", &visible, &xfac, &yfac, &zfac))
    return NULL;
  if (visible != TRUE && visible != FALSE)
  {
    PyErr_SetString(PyExc_ValueError, "Visible must be ON or OFF");
    return(NULL);
  }
  showUnitCell=visible;
  if (xfac != mol->unitcell->factor[0] || yfac != mol->unitcell->factor[1] ||
      zfac != mol->unitcell->factor[2])
  {
     mol->unitcell->factor[0]=xfac;
     mol->unitcell->factor[1]=yfac;
     mol->unitcell->factor[2]=zfac;
     expandCell(TRUE, TRUE);
  }
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *molecule_millerPlane(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  struct MOLECULE *mol;
  int which, h, k, l, visible;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }

  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  mol=&molecules[which];
  if (mol->unitcell == 0)
  {
    PyErr_SetString(PyExc_ValueError, "No unit cell defined");
    return(NULL);
  }
  h=mol->unitcell->miller[0];
  k=mol->unitcell->miller[1];
  l=mol->unitcell->miller[2];
  if (!PyArg_ParseTuple(args, "i|iii", &visible, &h, &k, &l)) return NULL;
  if (visible != TRUE && visible != FALSE)
  {
    PyErr_SetString(PyExc_ValueError, "Visible must be ON or OFF");
    return(NULL);
  }
  mol->unitcell->miller[0]=h;
  mol->unitcell->miller[1]=k;
  mol->unitcell->miller[2]=l;
  mol->unitcell->showMiller=visible;
  millerPlane();
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *molecule_addAtom(PyObject *self, PyObject *args)
{
  PyMoleculeSpecObject *s;
  PyAtomSpecObject *attached=NULL;
  int which, atomID;
  char *symbol;
  register int i, found=FALSE;

  if (checkInterrupt()) return(NULL);
  if (!self)
  {
    Py_INCREF(Py_None);
    return(Py_None);
  }

  s=(PyMoleculeSpecObject *)self;
  which=s->moleculeID;
  if (!PyArg_ParseTuple(args, "s|O", &symbol, &attached)) return NULL;
  if (!attached)
    atomID=(-1);
  else
    atomID=attached->atomID;
  for (i=0; i<ne; i++)
  {
    if (!strncmp(elements[i].symbol, symbol, 2))
    {
      element=i;
      found=TRUE;
    }
  }
  if (!found)
  {
    PyErr_SetString(PyExc_ValueError, "Element does not exist");
    return(NULL);
  }
  buildMolecule(&molecules[which], atomID, ADD_ATOM, TRUE);
  Py_INCREF(Py_None);
  return(Py_None);
}

static PyObject *molecule_molecule(PyObject *self, PyObject *args)
{
  if (checkInterrupt()) return(NULL);
  newMolecule((Widget)0, (caddr_t)0, (XmAnyCallbackStruct *)0);
  nmolecule++;
  return((PyObject *)(molecules[nmolecule-1].pyObject));
}

PyMoleculeSpecObject *molecule_new(void)
{
  PyMoleculeSpecObject *self;

  self=PyObject_NEW(PyMoleculeSpecObject, &PyMoleculeSpec_Type);
  if (self == NULL)
  {
    PyErr_NoMemory();
    return(NULL);
  }
  self->moleculeID=0;
  return(self);
}

static PyObject *molecule_getattr(PyMoleculeSpecObject *self, char *name)
{
  return(Py_FindMethod(molecule_methods, (PyObject *)self, name));
}

static void molecule_dealloc(PyMoleculeSpecObject *self)
{
  /* A dummy function is required here to avoid crashing
  PyMem_DEL(self); */
}

void initMoleculeModule(void)
{
  PyObject *module, *dict;
  static void *PyMolecule_API[PyMolecule_API_pointers];

  PyMoleculeSpec_Type.ob_type=&PyType_Type;

  PyImport_AddModule("molecule");
  module=Py_InitModule("molecule", molecule_methods);
  dict=PyModule_GetDict(module);

  PyMolecule_API[PyMoleculeSpec_Type_NUM]=(void *)&PyMoleculeSpec_Type;
  PyDict_SetItemString(dict, "_C_API", PyCObject_FromVoidPtr((void *)PyMolecule_API, NULL));
  PyDict_SetItemString(dict, "SINGLE", PyInt_FromLong(1L));
  PyDict_SetItemString(dict, "DOUBLE", PyInt_FromLong(2L));
  PyDict_SetItemString(dict, "TRIPLE", PyInt_FromLong(3L));
  PyDict_SetItemString(dict, "CONJUGATED", PyInt_FromLong(-2L));
  PyDict_SetItemString(dict, "HYDROGENBOND", PyInt_FromLong(-1L));
  PyDict_SetItemString(dict, "ENTHALPY", PyInt_FromLong(ENTHALPY));
  PyDict_SetItemString(dict, "ENTROPY", PyInt_FromLong(ENTROPY));
  PyDict_SetItemString(dict, "GIBBS_ENERGY", PyInt_FromLong(GIBBS_ENERGY));
  PyDict_SetItemString(dict, "HEAT_CAPACITY", PyInt_FromLong(HEAT_CAPACITY));
  PyDict_SetItemString(dict, "TRANSLATION", PyInt_FromLong((long)TRANSLATION));
  PyDict_SetItemString(dict, "PV", PyInt_FromLong((long)PV));
  PyDict_SetItemString(dict, "ROTATION", PyInt_FromLong((long)ROTATION));
  PyDict_SetItemString(dict, "VIBRATION", PyInt_FromLong((long)VIBRATION));
  PyDict_SetItemString(dict, "TOTAL", PyInt_FromLong((long)TOTAL));
  PyDict_SetItemString(dict, "BASIS_FUNCTION", PyInt_FromLong((long)BASIS_FUNCTION));
  PyDict_SetItemString(dict, "BASIS_IN_MO", PyInt_FromLong((long)BASIS_IN_MO));
  PyDict_SetItemString(dict, "MO", PyInt_FromLong((long)MOLECULAR_ORBITAL));
  PyDict_SetItemString(dict, "DENSITY", PyInt_FromLong((long)DENSITY));
  PyDict_SetItemString(dict, "IP_NONE", PyInt_FromLong((long)IP_NONE));
  PyDict_SetItemString(dict, "IP_LINEAR", PyInt_FromLong((long)IP_LINEAR));
  PyDict_SetItemString(dict, "IP_LOG", PyInt_FromLong((long)IP_LOG));
  PyDict_SetItemString(dict, "REACTANT", PyInt_FromLong((long)REACTANT));
  PyDict_SetItemString(dict, "PRODUCT", PyInt_FromLong((long)PRODUCT));
  PyDict_SetItemString(dict, "ALLREACTIONS", PyInt_FromLong((long)ALLREACTIONS));
  PyDict_SetItemString(dict, "ALPHA+BETA", PyInt_FromLong((long)ALPHAANDBETA));
  PyDict_SetItemString(dict, "ALPHA", PyInt_FromLong((long)ALPHA));
  PyDict_SetItemString(dict, "BETA", PyInt_FromLong((long)BETA));
}

void setInternal(struct MOLECULE *mol, int type, int i, int j, int k, int l, char *value)
{
  if (mol->ninternal == 0)
    mol->internals=(struct INTERNAL *)getmem((size_t)1, sizeof(struct INTERNAL));
  else
    mol->internals=(struct INTERNAL *)expmem((void *)mol->internals,
                   (size_t)(mol->ninternal+1), sizeof(struct INTERNAL));
  mol->internals[mol->ninternal].atoms[0]=i;
  mol->internals[mol->ninternal].atoms[1]=j;
  mol->internals[mol->ninternal].atoms[2]=k;
  mol->internals[mol->ninternal].atoms[3]=l;
  mol->internals[mol->ninternal].type=type;
  modifyGeometry(mol->ninternal, value);
  if (mol->ninternal == 0)
    fremem((void **)&mol->internals);
  else
    mol->internals=(struct INTERNAL *)expmem((void *)mol->internals,
                   (size_t)mol->ninternal, sizeof(struct INTERNAL));
}
