/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                             W A V E F O R M . C                              *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: waveform.c,v 1.6 2003/11/07 11:17:24 jrh Exp $
* $Log: waveform.c,v $
* Revision 1.6  2003/11/07 11:17:24  jrh
* Release 2.4
*
* Revision 1.5  2000/12/10 15:18:53  jrh
* Release 2.3
*
* Revision 1.4  1999/05/24 01:27:56  jrh
* Release 2.2.1
*
* Revision 1.3  1999/02/07 21:59:44  jrh
* Release 2.2
*
* Revision 1.2  1998/01/26 00:49:47  jrh
* Release 2.1
*
* Revision 1.1  1996/12/10  18:44:23  jrh
* Initial revision
*
*/
#include<math.h>
#include<X11/Intrinsic.h>
#include<X11/cursorfont.h>
#include<Xm/Xm.h>
#include<Xm/BulletinB.h>
#include<Xm/Form.h>
#include<Xm/Label.h>
#include<Xm/LabelG.h>
#include<Xm/PanedW.h>
#include<Xm/PushB.h>
#include<Xm/PushBG.h>
#include<Xm/RowColumn.h>
#include<Xm/Scale.h>
#include<Xm/Separator.h>
#include<Xm/Text.h>
#include<Xm/ToggleB.h>
#include<GL/glu.h>
#include "viewmol.h"
#include "dialog.h"

extern void MapBox(Widget, caddr_t, caddr_t);
extern void GetUnit(Widget, struct TOGGLE *, XmPushButtonCallbackStruct *);
extern void GetSlider(Widget, struct SLIDER *, XmScrollBarCallbackStruct *);
extern Widget CreateToggleBox(Widget, struct PushButtonRow *, int, int,
                              int, int, int);
extern char *getStringResource(Widget, char *);
extern void CreatePushButtonRow(Widget, struct PushButtonRow *, int);
extern int checkFile(char **);
extern void calcmo(void);
extern void redraw(int);
extern void drawMolecule(Widget, caddr_t, XmDrawingAreaCallbackStruct *);
extern void *getmem(size_t, size_t);
extern void fremem(void **);
extern void setMenuItem(int, int);
extern int  makeAnnotation(int, int, float, float, float, int, const GLfloat *,
                           int, int, char *);
extern void deleteAnnotation(int *);
extern void pixelToWorld(int, double *, double *);
extern int StringWidth(XFontStruct *, char *);
extern int StringHeight(XFontStruct *);
/* extern char *bfname(int);*/
extern int messgb(Widget, int, char *, struct PushButtonRow *, int);
extern void GetMessageBoxButton(Widget, XtPointer, caddr_t);
extern void setCursor(Widget, int);
extern Widget initShell(Widget, char *, Widget *, Widget *);

void wavefunctionDialogExit(Widget, caddr_t, XmPushButtonCallbackStruct *);
int  existsGridObject(void);
void annotateWavefunction(void);
void deleteGridObjects(void);
void GetWave(Widget, struct TOGGLE *, XmToggleButtonCallbackStruct *);
void GetAutomatic(Widget, caddr_t, XmToggleButtonCallbackStruct *);

extern struct WINDOW windows[];
extern struct MOLECULE *molecules;
extern struct ELEMENT *elements;
extern int iwavef, interp;
extern int selectAtom, primitive, swapBuffers, selectAtomAnnotation;
extern int *wavefunctionAnnotation, bfatom, ne, rgbMode;
extern int nmolecule, automaticRecalculate;
extern double level, gridres;
extern Widget topShell;
extern char moloch[];
extern Pixel stdcol[9];

static Widget dialog, slider2;
static int iwavef_save, interp_save;
static double level_save, gridres_save;

void wavefunctionDialog(Widget widget, caddr_t dummy, XmAnyCallbackStruct *data)
{
  Widget form, board, form1, checkbox1, checkbox2, sep1, sep2, sep3, sep4;
  Widget slider1, label, button;
  register int i, j;
  struct TOGGLE *toggle1;
  static struct TOGGLE toggle2[3];
  struct PushButtonRow buttons[] = {
    { "ok", wavefunctionDialogExit, (XtPointer)TRUE, NULL },
    { "cancel", wavefunctionDialogExit, (XtPointer)FALSE, NULL },
  };
  struct PushButtonRow *checkbox1_buttons;
  struct PushButtonRow checkbox2_buttons[] = {
    { "none", GetUnit, (XtPointer)&toggle2[0], NULL },
    { "linear", GetUnit, (XtPointer)&toggle2[1], NULL },
    { "logarithmic", GetUnit, (XtPointer)&toggle2[2], NULL },
  };
  static struct SLIDER surface, gridsize;
  struct MOLECULE *mol;
  int nDrawable, start, levelInt, minimum, maximum;
  short decimals;

  if (windows[VIEWER].set >= 0)
    mol=&molecules[windows[VIEWER].set];
  else
    mol=&molecules[0];
  setMenuItem(VIEWER_WAVEFUNCTION, False);

  iwavef_save=iwavef;
  interp_save=interp;
  level_save=level;
  gridres_save=gridres;
  gridsize.number=&gridres;
  gridsize.decimals=0;
  gridsize.draw=NULL;

  dialog=initShell(windows[VIEWER].widget, "wavefunctionForm",
                   &board, &form);

  form1=XtVaCreateWidget("controlarea", xmFormWidgetClass, form, NULL);

  if (mol->basisset)
  {
    start=ALL_OFF;
    nDrawable=DENSITY;
  }
  else
  {
    start=DENSITY;
    nDrawable=ALL_OFF;
  }
  for (i=0; i<mol->ngridobjects; i++)
    if (mol->gridObjects[i].type & GRIDREAD) nDrawable++;

  toggle1=(struct TOGGLE *)getmem((size_t)nDrawable, sizeof(struct TOGGLE));
  checkbox1_buttons=(struct PushButtonRow *)getmem((size_t)nDrawable, sizeof(struct PushButtonRow));
  toggle1[0].var=&iwavef;
  toggle1[0].draw=NULL;
  toggle1[0].value=ALL_OFF;
  checkbox1_buttons[0].callback=GetWave;
  checkbox1_buttons[0].client_data=(XtPointer)&toggle1[0];
  checkbox1_buttons[0].widget=(Widget)NULL;
  checkbox1_buttons[0].label="all_off";
  j=0;
  for (i=1; i<nDrawable; i++)
  {
    toggle1[i].var=&iwavef;
    toggle1[i].draw=NULL;
    toggle1[i].value=i+start;
    checkbox1_buttons[i].callback=GetWave;
    checkbox1_buttons[i].client_data=(XtPointer)&toggle1[i];
    checkbox1_buttons[i].widget=(Widget)NULL;
    if (toggle1[i].value > DENSITY)
      checkbox1_buttons[i].label=mol->gridObjects[j++].text;
  }
  if (mol->basisset)
  {
    checkbox1_buttons[1].label="basis_function";
    checkbox1_buttons[2].label="basis_in_mo";
    checkbox1_buttons[3].label="molecular_orbital";
    checkbox1_buttons[4].label="electron_density";
  }
  checkbox1=CreateToggleBox(form1, checkbox1_buttons, nDrawable, XmVERTICAL, 1,
                            True, iwavef-start);

  if (mol->orbitals != NULL)
  {
    if (mol->needMoloch && *moloch == '\0')
    {
      XtVaSetValues(checkbox1_buttons[BASIS_IN_MO-1].widget, XmNsensitive, False, NULL);
      XtVaSetValues(checkbox1_buttons[MOLECULAR_ORBITAL-1].widget, XmNsensitive, False, NULL);
      XtVaSetValues(checkbox1_buttons[DENSITY-1].widget, XmNsensitive, False, NULL);
    }
  }
  else
  {
    XtVaSetValues(checkbox1_buttons[BASIS_IN_MO-1].widget, XmNsensitive, False, NULL);
    XtVaSetValues(checkbox1_buttons[MOLECULAR_ORBITAL-1].widget, XmNsensitive, False, NULL);
    XtVaSetValues(checkbox1_buttons[DENSITY-1].widget, XmNsensitive, False, NULL);
  }

  XtVaSetValues(checkbox1, XmNleftAttachment, XmATTACH_FORM,
                           XmNrightAttachment, XmATTACH_FORM,
                           XmNtopAttachment, XmATTACH_FORM,
                           NULL);
  sep1=XtVaCreateManagedWidget("sep1", xmSeparatorWidgetClass, form1,
                               XmNtraversalOn, False,
                               XmNleftAttachment, XmATTACH_FORM,
                               XmNrightAttachment, XmATTACH_FORM,
                               XmNtopAttachment, XmATTACH_WIDGET,
                               XmNtopWidget, checkbox1,
                               NULL);

  label=XtVaCreateManagedWidget("levelLabel", xmLabelWidgetClass, form1,
                                XmNleftAttachment, XmATTACH_FORM,
                                XmNrightAttachment, XmATTACH_FORM,
                                XmNtopAttachment, XmATTACH_WIDGET,
                                XmNtopWidget, sep1,
                                NULL);
  slider1=XtVaCreateManagedWidget("level", xmScaleWidgetClass, form1,
                                  XmNorientation, XmHORIZONTAL,
                                  XmNshowValue, True,
                                  XmNsensitive, True,
                                  XmNleftAttachment, XmATTACH_FORM,
                                  XmNrightAttachment, XmATTACH_FORM,
                                  XmNtopAttachment, XmATTACH_WIDGET,
                                  XmNtopWidget, label,
                                  NULL);
  XtVaGetValues(slider1, XmNdecimalPoints, &decimals, NULL);
  levelInt=(int)(level*pow((double)10.0, (double)decimals));
  XtVaGetValues(slider1, XmNminimum, &minimum, XmNmaximum, &maximum, NULL);
  if (minimum > levelInt) XtVaSetValues(slider1, XmNminimum, levelInt, NULL);
  if (maximum < levelInt) XtVaSetValues(slider1, XmNmaximum, levelInt, NULL);
  XtVaSetValues(slider1, XmNvalue, levelInt, NULL);
  surface.number=&level;
  surface.decimals=decimals;
  surface.draw=(void (*)())drawMolecule;
  XtAddCallback(slider1, XmNvalueChangedCallback, (XtCallbackProc)GetSlider, &surface);
  XtAddCallback(slider1, XmNdragCallback, (XtCallbackProc)GetSlider, &surface);

  sep2=XtVaCreateManagedWidget("sep2", xmSeparatorWidgetClass, form1,
                              XmNtraversalOn, False,
                              XmNleftAttachment, XmATTACH_FORM,
                              XmNrightAttachment, XmATTACH_FORM,
                              XmNtopAttachment, XmATTACH_WIDGET,
                              XmNtopWidget, slider1,
                              NULL);

  label=XtVaCreateManagedWidget("interpolationLabel", xmLabelWidgetClass, form1,
                                XmNleftAttachment, XmATTACH_FORM,
                                XmNrightAttachment, XmATTACH_FORM,
                                XmNtopAttachment, XmATTACH_WIDGET,
                                XmNtopWidget, sep2,
                                NULL);
  for (i=0; i<3; i++)
  {
    toggle2[i].var=&interp;
    toggle2[i].draw=NULL;
  }
  toggle2[0].value=IP_NONE;
  toggle2[1].value=IP_LINEAR;
  toggle2[2].value=IP_LOG;
  checkbox2=CreateToggleBox(form1, checkbox2_buttons, XtNumber(checkbox2_buttons),
                           XmVERTICAL, 1, True, interp-1);

  XtVaSetValues(checkbox2, XmNleftAttachment, XmATTACH_FORM,
                           XmNrightAttachment, XmATTACH_FORM,
                           XmNtopAttachment, XmATTACH_WIDGET,
                           XmNtopWidget, label,
                           NULL);

  sep3=XtVaCreateManagedWidget("sep3", xmSeparatorWidgetClass, form1,
                              XmNtraversalOn, False,
                              XmNleftAttachment, XmATTACH_FORM,
                              XmNrightAttachment, XmATTACH_FORM,
                              XmNtopAttachment, XmATTACH_WIDGET,
                              XmNtopWidget, checkbox2,
                              NULL);

  label=XtVaCreateManagedWidget("gridLabel", xmLabelWidgetClass, form1,
                                XmNleftAttachment, XmATTACH_FORM,
                                XmNrightAttachment, XmATTACH_FORM,
                                XmNtopAttachment, XmATTACH_WIDGET,
                                XmNtopWidget, sep3,
                                NULL);
  slider2=XtVaCreateManagedWidget("grid", xmScaleWidgetClass, form1,
                                  XmNorientation, XmHORIZONTAL,
                                  XmNdecimalPoints, 0,
                                  XmNshowValue, True,
                                  XmNsensitive, True,
                                  XmNleftAttachment, XmATTACH_FORM,
                                  XmNrightAttachment, XmATTACH_FORM,
                                  XmNtopAttachment, XmATTACH_WIDGET,
                                  XmNtopWidget, label,
                                  NULL);
  if (iwavef > DENSITY)
    gridres=(double)(mol->gridObjects[iwavef-DENSITY-1].ngridpoints[0]);
  if (gridres > 0.0)
    XtVaSetValues(slider2, XmNvalue, (int)gridres, NULL);
  else
  {
    XtVaGetValues(slider2, XmNvalue, &levelInt, NULL);
    gridres=(double)levelInt;
  }
  XtAddCallback(slider2, XmNvalueChangedCallback, (XtCallbackProc)GetSlider, &gridsize);
  if (iwavef > DENSITY)
    XtVaSetValues(slider2, XmNsensitive, False, NULL);
  button=XtVaCreateManagedWidget("automatic", xmToggleButtonWidgetClass, form1,
                                 XmNleftAttachment, XmATTACH_FORM,
                                 XmNrightAttachment, XmATTACH_FORM,
                                 XmNtopAttachment, XmATTACH_WIDGET,
                                 XmNtopWidget, slider2,
                                 NULL);
  if (automaticRecalculate) XtVaSetValues(button, XmNset, True, NULL);
  XtAddCallback(button, XmNvalueChangedCallback, (XtCallbackProc)GetAutomatic, (XtPointer)&automaticRecalculate);

  sep4=XtVaCreateManagedWidget("sep5", xmSeparatorWidgetClass, form1,
                              XmNtraversalOn, False,
                              XmNleftAttachment, XmATTACH_FORM,
                              XmNrightAttachment, XmATTACH_FORM,
                              XmNtopAttachment, XmATTACH_WIDGET,
                              XmNtopWidget, button,
                              NULL);
  XtManageChild(form1);

  CreatePushButtonRow(form, buttons, XtNumber(buttons));
  XtAddCallback(dialog, XmNpopupCallback, (XtCallbackProc)MapBox, (XtPointer)NULL);

  XtManageChild(form);
  XtManageChild(board);
}

void wavefunctionDialogExit(Widget widget, caddr_t which, XmPushButtonCallbackStruct *data)
{
  const GLfloat black[4] = {0.0, 0.0, 0.0, 0.0};
  char *word;

  if (!(int)which)
  {
    iwavef=iwavef_save;
    interp=interp_save;
    level=level_save;
    gridres=gridres_save;
  }
  else
  {
    switch (iwavef)
    {
      case ALL_OFF:           deleteGridObjects();
                              break;
      case BASIS_FUNCTION:
      case BASIS_IN_MO:       word=getStringResource(topShell, "selectAtomTitle");
                              selectAtomAnnotation=makeAnnotation(selectAtomAnnotation,
                                                   CENTERED, 0.0, 1.0, 0.0, stdcol[BLACK],
                                                   black, 0, 0, word);
                              selectAtom=TRUE;
                              break;
      case MOLECULAR_ORBITAL:
      case DENSITY:           selectAtom=FALSE;
                              if (!existsGridObject())
                              {
                                setCursor(dialog, XC_watch);
                                calcmo();
                              }
                              break;
      /* Grid read from input */
      default:                selectAtom=FALSE;
                              break;
    }
  }
  XtDestroyWidget(dialog);
  annotateWavefunction();
  setMenuItem(VIEWER_WAVEFUNCTION, True);
  redraw(VIEWER);
}

void annotateWavefunction(void)
{
  static int wavefunctionAnnotation[4]={-1, -1, -1, -1};
  struct MOLECULE *mol;
  Pixel pixel=0;
  double xpix, ypix;
  float x, y, z;
  GLfloat color[4] = {0.0, 0.0, 0.0, 1.0};
  const GLfloat black[4] = {0.0, 0.0, 0.0, 1.0};
  char line1[MAXLENLINE], line2[MAXLENLINE], *c, *d;
  int annotateSurface=0;
  register int i;

  if (windows[VIEWER].set >= 0)
    mol=&molecules[windows[VIEWER].set];
  else
    mol=&molecules[0];
  deleteAnnotation(&wavefunctionAnnotation[0]);
  if (wavefunctionAnnotation[1] != (-1)) wavefunctionAnnotation[1]--;
  deleteAnnotation(&wavefunctionAnnotation[1]);
  if (wavefunctionAnnotation[2] != (-1)) wavefunctionAnnotation[2]-=2;
  deleteAnnotation(&wavefunctionAnnotation[2]);
  if (wavefunctionAnnotation[3] != (-1)) wavefunctionAnnotation[3]-=3;
  deleteAnnotation(&wavefunctionAnnotation[3]);
  if (!existsGridObject()) return;

  if (iwavef == BASIS_FUNCTION && mol->ibasfu != -1)
  {
    c=getStringResource(topShell, "basisfunctionTitle");
    sprintf(line1, c, mol->ibasfu+1, mol->atoms[bfatom].name, bfatom+1,
/*          bfname(mol->ibasfu));*/
            mol->atoms[bfatom].basisFunctionNames[mol->ibasfu]);
    wavefunctionAnnotation[0]=makeAnnotation(wavefunctionAnnotation[0],
                              CENTERED, 0.0, 1.0, 0.0, stdcol[BLACK], black,
                              0, 0, line1);
    annotateSurface=2;
  }
  else if (iwavef == BASIS_IN_MO && mol->ibasfu != -1)
  {
    c=getStringResource(topShell, "basisfunctionInMOTitle");
    sprintf(line1, c, mol->ibasfu+1, mol->imo+1, mol->atoms[bfatom].name, bfatom+1,
/*          mol->orbitals[mol->imo].coeff[mol->ibasfu], bfname(mol->ibasfu));*/
            mol->orbitals[mol->imo].coeff[mol->ibasfu], mol->atoms[bfatom].basisFunctionNames[mol->ibasfu]);
    wavefunctionAnnotation[0]=makeAnnotation(wavefunctionAnnotation[0],
                              CENTERED, 0.0, 1.0, 0.0, stdcol[BLACK], black,
                              0, 0, line1);
    annotateSurface=2;
  }
  else if (iwavef == MOLECULAR_ORBITAL && mol->imo != -1)
  {
    c=getStringResource(topShell, "molecularOrbitalTitle");
    sprintf(line1, c, mol->imo+1, mol->orbitals[mol->imo].symmetry,
            mol->orbitals[mol->imo].energy);
    wavefunctionAnnotation[0]=makeAnnotation(wavefunctionAnnotation[0],
                              CENTERED, 0.0, 1.0, 0.0, stdcol[BLACK], black,
                              0, 0, line1);
    annotateSurface=2;
  }
  else if (iwavef == DENSITY)
  {
    c=getStringResource(topShell, "electronDensityTitle");
    sprintf(line1, c);
    wavefunctionAnnotation[0]=makeAnnotation(wavefunctionAnnotation[0],
                              CENTERED, 0.0, 1.0, 0.0, stdcol[BLACK], black,
                              0, 0, line1);
    annotateSurface=1;
  }
  if (annotateSurface)
  {
    pixelToWorld(VIEWER, &xpix, &ypix);
    c=getStringResource(topShell, "isosurfaceLabel");
    d=getStringResource(topShell, "isosurfaceLevel");
    if (annotateSurface == 1)
    {
      sprintf(line1, "%s%s", c, d);
      sprintf(line2, line1, level);
    }
    else
    {
      sprintf(line1, "%s%s%s", c, d, d);
      sprintf(line2, line1, level, (-level));
    }
    x=(windows[VIEWER].left+windows[VIEWER].right
     -xpix*(double)StringWidth(windows[VIEWER].font, line2))*0.5;
    y=windows[VIEWER].top-4.*ypix*(double)StringHeight(windows[VIEWER].font);
    z=(float)windows[VIEWER].near;
    wavefunctionAnnotation[1]=makeAnnotation(wavefunctionAnnotation[1],
                              COORDINATES, x, y, z, stdcol[BLACK], black,
                              0, 0, c);
    for (i=0; i<ne; i++)
      if (!strcmp(elements[i].symbol, "Ps")) break;
    if (rgbMode)
    {
      color[0]=0.5*(elements[i].dark[0]+elements[i].light[0]);
      color[1]=0.5*(elements[i].dark[1]+elements[i].light[1]);
      color[2]=0.5*(elements[i].dark[2]+elements[i].light[2]);
    }
    else
      pixel=elements[i].colormap[1];
    x+=xpix*(double)StringWidth(windows[VIEWER].font, c);
    sprintf(line1, d, level);
    wavefunctionAnnotation[2]=makeAnnotation(wavefunctionAnnotation[2],
                              COORDINATES, x, y, z, pixel, color, 0, 0,
                              line1);
    if (annotateSurface == 2)
    {
      for (i=0; i<ne; i++)
        if (!strcmp(elements[i].symbol, "Ms")) break;
      x+=xpix*(double)StringWidth(windows[VIEWER].font, line1);
      if (rgbMode)
      {
        color[0]=0.5*(elements[i].dark[0]+elements[i].light[0]);
        color[1]=0.5*(elements[i].dark[1]+elements[i].light[1]);
        color[2]=0.5*(elements[i].dark[2]+elements[i].light[2]);
      }
      else
        pixel=elements[i].colormap[1];
      sprintf(line1, d, (-level));
      wavefunctionAnnotation[3]=makeAnnotation(wavefunctionAnnotation[3],
                                COORDINATES, x, y, z, pixel, color, 0, 0,
                                line1);
    }
  }
}

int existsGridObject(void)
{
  register int i, j;

  for (i=0; i<nmolecule; i++)
  {
    for (j=0; j<molecules[i].ngridobjects; j++)
    {
      if (molecules[i].gridObjects[j].type       == iwavef &&
          molecules[i].gridObjects[j].mo         == molecules[i].imo &&
          molecules[i].gridObjects[j].resolution == gridres) return(TRUE);
    }
  }
  return(FALSE);
}

void deleteGridObjects(void)
{
  register int i, j;

  for (i=0; i<nmolecule; i++)
  {
    for (j=0; j<molecules[i].ngridobjects; j++)
    {
      if (!(molecules[i].gridObjects[j].type & GRIDREAD))
	{
        fremem((void **)&(molecules[i].gridObjects[j].grid));
	  molecules[i].ngridobjects--;
      }
    }
    if (molecules[i].ngridobjects == 0)
      fremem((void **)&(molecules[i].gridObjects));
  }
  annotateWavefunction();
}

void GetWave(Widget widget, struct TOGGLE *which, XmToggleButtonCallbackStruct *data)
{
  static struct PushButtonRow button[] = {{"continue", GetMessageBoxButton, (XtPointer)0, NULL}};
  char *word, line[MAXLENLINE];
  register int i;

  if (data->set)
  {
    if ((which->value == BASIS_IN_MO || which->value == MOLECULAR_ORBITAL) &&
         molecules[windows[VIEWER].set].imo == (-1))
    {
      word=getStringResource(topShell, "noMOselected");
      sprintf(line, word, molecules[windows[VIEWER].set].title);
      messgb(topShell, 2, line, button, 1);
      XtVaSetValues(widget, XmNset, False, NULL);
      return;
    }
    *(which->var)=which->value;
    if (which->value > DENSITY)
    {
      i=which->value-DENSITY-1;
      XtVaSetValues(slider2, XmNsensitive, False,
                             XmNvalue, molecules[windows[VIEWER].set].gridObjects[i].ngridpoints[0],
                             NULL);
      gridres=(double)molecules[windows[VIEWER].set].gridObjects[i].ngridpoints[0];
    }
    else
      XtVaSetValues(slider2, XmNsensitive, True, XmNvalue, (int)gridres, NULL);
  }
  if (*(which->draw) != NULL) (*(which->draw))((Widget)0, (caddr_t)0, (caddr_t)0);
}

void GetAutomatic(Widget widget, caddr_t which, XmToggleButtonCallbackStruct *data)
{
  int *w;

  w=(int *)which;
  if (data->set)
    *w=TRUE;
  else
    *w=FALSE;

}
