/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                               D R A W O P . C                                *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: drawop.c,v 1.6 2003/11/07 10:59:57 jrh Exp $
* $Log: drawop.c,v $
* Revision 1.6  2003/11/07 10:59:57  jrh
* Release 2.4
*
* Revision 1.5  2000/12/10 15:05:20  jrh
* Release 2.3
*
* Revision 1.4  1999/05/24 01:25:16  jrh
* Release 2.2.1
*
* Revision 1.3  1999/02/07 21:47:38  jrh
* Release 2.2
*
* Revision 1.2  1998/01/26 00:47:34  jrh
* Release 2.1
*
* Revision 1.1  1996/12/10  18:40:36  jrh
* Initial revision
*
*/
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<X11/Intrinsic.h>
#include<X11/StringDefs.h>
#include<X11/keysym.h>
#include<Xm/Xm.h>
#include<GL/gl.h>
#include<GL/glu.h>
#include "viewmol.h"

void cross(double, double, double, double);
Boolean animateHistory(XtPointer);

extern double makeTics(double, double, double *, char *);
extern void pixelToWorld(int, double *, double *);
extern void drawBackground(int, Pixel, double);
extern void setWindowColor(int, Pixel, const float *);
extern int StringWidth(XFontStruct *, char *);
extern void (*drawBegin)(GLenum), (*drawEnd)(void), (*drawVertex2d)(double, double);
extern void (*drawString)(char *, double, double, double, double, GLuint, char);
extern void (*drawLineWidth)(GLfloat);
extern void printDialog(Widget, caddr_t, XmAnyCallbackStruct *);
extern char *getStringResource(Widget, char*);
extern void restoreGeometry(struct SAVE *, int);
extern void redraw(int);
extern void setMenuItem(int, int);
extern void setGeometry(int);
extern void deleteAnnotation(int *);
extern void fremem(void **);
extern void selectMolecule(Widget, caddr_t, XmToggleButtonCallbackStruct *);
extern void setHistoryAnimation(Widget, caddr_t, XmAnyCallbackStruct *);
extern void getWorldCoordinates(double, double, double, double *, double *, double *);
extern void getScreenCoordinates(double, double, double, double *, double *, double *);

extern struct MOLECULE *molecules;
extern struct WINDOW windows[];
extern Widget topShell;
extern Pixel stdcol[9], historyColor;
extern float historyColor_rgb[4];
extern int historyAnnotation, nmolecule;
extern int animate, swapBuffers;

void drawHistory(Widget w, caddr_t x, XmDrawingAreaCallbackStruct *data)
{
  Dimension width, height;
  char line[18], form[8], *word;
  double left, right, bottom, top;
  double g, e, xpix, ypix=0.0, xpixold, ticMark, ticInc, strl;
  const float black[4] = {0.0, 0.0, 0.0, 0.0};
  int imol, renderMode;
  register int i;

/* This subroutine draws the diagram for optimization history */

  imol=windows[HISTORY].set;
  glGetIntegerv(GL_RENDER_MODE, &renderMode);
  if (renderMode == GL_RENDER)
    glXMakeCurrent(XtDisplay(windows[HISTORY].widget), XtWindow(windows[HISTORY].widget), windows[HISTORY].context);

  XtVaGetValues(windows[HISTORY].widget, XtNwidth, &width, XtNheight, &height, NULL);
  e=molecules[imol].emax-molecules[imol].emin > 0.00001 ? molecules[imol].emax-molecules[imol].emin : 0.00001;
  g=molecules[imol].gmax == 0.0 ? 0.01 : molecules[imol].gmax;
  left=windows[HISTORY].left;
  right=windows[HISTORY].right=(double)(molecules[imol].nhist);
  bottom=windows[HISTORY].bottom;
  top=windows[HISTORY].top=g;
  xpixold=0.0;
  xpix=1.0;
  while (fabs(xpixold-xpix) > 1.0e-4)
  {
    xpixold=xpix;
    xpix=(right-left)/(double)width;
    ypix=(top-bottom)/(double)height;
    left=windows[HISTORY].left-10.0*xpix;
    right=windows[HISTORY].right+10.0*xpix;
    bottom=windows[HISTORY].bottom-10.0*ypix;
    top=windows[HISTORY].top+10.0*ypix;
  }

  drawBackground(HISTORY, windows[HISTORY].background, (double)0.0);
  setWindowColor(FOREGROUND, stdcol[BLACK], black);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(left, right, bottom, top, windows[HISTORY].near, windows[HISTORY].far);
  glShadeModel(GL_FLAT);

/* Draw tic marks */

  if (windows[HISTORY].mode & SCALES)
  {
    (*drawLineWidth)((GLfloat)1.);
    ticInc=makeTics(1.0, (double)molecules[imol].nhist, &ticMark, form);
    if (ticInc < 1.0) ticInc=1.0;
    if (ticMark == 1.0) ticMark+=ticInc;
    do
    {
      (*drawBegin)(GL_LINES);
      (*drawVertex2d)(ticMark, g);
      (*drawVertex2d)(ticMark, g+5.0*ypix);
      (*drawEnd)();
      (*drawBegin)(GL_LINES);
      (*drawVertex2d)(ticMark, 0.0);
      (*drawVertex2d)(ticMark, -5.0*ypix);
      (*drawEnd)();
      sprintf(line, "%d", (int)ticMark);
      strl=(double)StringWidth(windows[HISTORY].font, line)/2.0*xpix;
      (*drawString)(line, ticMark-strl, g-15.0*ypix, 0.0, 0.0, windows[HISTORY].GLfontId, 'l');
      ticMark+=ticInc;
    } while (ticMark < (double)molecules[imol].nhist);

    if (windows[HISTORY].mode & GNORM)
    {
      ticInc=makeTics(0.0, g, &ticMark, form);
      ticMark+=ticInc;
      do
      {
        (*drawBegin)(GL_LINES);
        (*drawVertex2d)((double)molecules[imol].nhist, ticMark);
        (*drawVertex2d)((double)molecules[imol].nhist+5.0*xpix, ticMark);
        (*drawEnd)();
        sprintf(line, form, ticMark);
        strl=(double)(StringWidth(windows[HISTORY].font, line)+4)*xpix;
        setWindowColor(FOREGROUND, historyColor, historyColor_rgb);
        (*drawString)(line, (double)molecules[imol].nhist, ticMark-5.0*ypix,
                      0.0, strl, windows[HISTORY].GLfontId, 'r');
        setWindowColor(FOREGROUND, stdcol[BLACK], black);
        ticMark+=ticInc;
      } while (ticMark < g);
    }
    if (windows[HISTORY].mode & ENERGY)
    {
      ticInc=makeTics(molecules[imol].emin, e+molecules[imol].emin, &ticMark, form);
      do
      {
        (*drawBegin)(GL_LINES);
        (*drawVertex2d)(1.0-5.0*xpix, (ticMark-molecules[imol].emin)*g/e);
        (*drawVertex2d)(1.0, (ticMark-molecules[imol].emin)*g/e);
        (*drawEnd)();
        sprintf(line, form, ticMark);
        setWindowColor(FOREGROUND, windows[HISTORY].foreground, windows[HISTORY].foreground_rgb);
        (*drawString)(line, 1.0+3.0*xpix, (ticMark-molecules[imol].emin)*g/e-5.0*ypix,
                      0.0, 0.0, windows[HISTORY].GLfontId, 'l');
        setWindowColor(FOREGROUND, stdcol[BLACK], black);
        ticMark+=ticInc;
      } while (ticMark < e+molecules[imol].emin);
    }
  }

/* Draw diagram frame and labels */

  (*drawLineWidth)((GLfloat)3.);
  (*drawBegin)(GL_LINE_LOOP);
  (*drawVertex2d)(1.0, 0.0);
  (*drawVertex2d)(1.0, g);
  (*drawVertex2d)((double)molecules[imol].nhist, g);
  (*drawVertex2d)((double)molecules[imol].nhist, 0.0);
  (*drawEnd)();
  if (windows[HISTORY].mode & GNORM)
  {
    setWindowColor(FOREGROUND, historyColor, historyColor_rgb);
    (*drawBegin)(GL_LINES);
    (*drawVertex2d)((double)molecules[imol].nhist-150.0*xpix, g-40.0*ypix);
    (*drawVertex2d)((double)molecules[imol].nhist-130.0*xpix, g-40.0*ypix);
    (*drawEnd)();
    setWindowColor(FOREGROUND, stdcol[BLACK], black);
    word=getStringResource(topShell, "gradientNorm");
    (*drawString)(word, (double)molecules[imol].nhist-120.0*xpix, g-40.0*ypix,
                  0.0, 0.0, windows[HISTORY].GLfontId, 'l');
  }
  if (windows[HISTORY].mode & ENERGY)
  {
    setWindowColor(FOREGROUND, windows[HISTORY].foreground, windows[HISTORY].foreground_rgb);
    (*drawBegin)(GL_LINES);
    (*drawVertex2d)((double)molecules[imol].nhist-150.0*xpix, g-60.0*ypix);
    (*drawVertex2d)((double)molecules[imol].nhist-130.0*xpix, g-60.0*ypix);
    (*drawEnd)();
    setWindowColor(FOREGROUND, stdcol[BLACK], black);
    word=getStringResource(topShell, "energy");
    (*drawString)(word, (double)molecules[imol].nhist-120.0*xpix, g-60.0*ypix,
                  0.0, 0.0, windows[HISTORY].GLfontId, 'l');
  }

/* Draw gradient norm curve */

  if (windows[HISTORY].mode & GNORM)
  {
    setWindowColor(FOREGROUND, historyColor, historyColor_rgb);
    (*drawBegin)(GL_LINE_STRIP);
    for (i=0; i<molecules[imol].nhist; i++)
      (*drawVertex2d)((double)(i+1), molecules[imol].optimization[i].gnorm);
    (*drawEnd)();
  }

/* Draw energy curve */

  if (windows[HISTORY].mode & ENERGY)
  {
    setWindowColor(FOREGROUND, windows[HISTORY].foreground, windows[HISTORY].foreground_rgb);
    (*drawBegin)(GL_LINE_STRIP);
    for (i=0; i<molecules[imol].nhist; i++)
      (*drawVertex2d)((double)(i+1), (molecules[imol].optimization[i].energy-molecules[imol].emin)*g/e);
    (*drawEnd)();
  }
  cross((double)(windows[HISTORY].mouseX), e, xpix, ypix);
  if (swapBuffers) glXSwapBuffers(XtDisplay(windows[HISTORY].widget), XtWindow(windows[HISTORY].widget));
}

void cross(double x, double y, double xpix, double ypix)
{
  const float red[4] = {1.0, 0.0, 0.0, 0.0};
  double xw, yw, zw;
  register double a=0.0, b=0.0;
  register int imol, c1, c2;

/* Draw a red cross at position (x, molecules[imol].optimization[molecules[imol].cycle].energy)
   if the energy curve is shown, else draw cross at position
   (x, molecules[imol].optimization[molecules[imol].cycle].gnorm) */

  imol=windows[HISTORY].set;
  getWorldCoordinates(x, y, 0.0, &xw, &yw, &zw);
  c1=(int)floor(xw);
  c1=c1 < 1 ? 1 : c1;
  c1=c1 > molecules[imol].nhist ? molecules[imol].nhist : c1;
  c2=(int)ceil(xw);
  c2=c2 < 1 ? 1 : c2;
  c2=c2 > molecules[imol].nhist ? molecules[imol].nhist : c2;
  setWindowColor(FOREGROUND, stdcol[RED], red);
  if (windows[HISTORY].mode & ENERGY)
  {
    a=molecules[imol].optimization[c1-1].energy-molecules[imol].optimization[c2-1].energy;
    b=molecules[imol].optimization[c1-1].energy-molecules[imol].emin+a*((double)c1-xw);
  }
  else if (windows[HISTORY].mode & GNORM)
  {
    a=molecules[imol].optimization[c1-1].gnorm-molecules[imol].optimization[c2-1].gnorm;
    b=molecules[imol].optimization[c1-1].gnorm+a*((double)c1-xw);
    y=windows[HISTORY].top;
  }
  xpix*=10.0;
  ypix*=10.0;
  (*drawBegin)(GL_LINES);
  (*drawVertex2d)(xw, b*windows[HISTORY].top/y-ypix);
  (*drawVertex2d)(xw, b*windows[HISTORY].top/y+ypix);
  (*drawEnd)();
  (*drawBegin)(GL_LINES);
  (*drawVertex2d)(xw-xpix, b*windows[HISTORY].top/y);
  (*drawVertex2d)(xw+xpix, b*windows[HISTORY].top/y);
  (*drawEnd)();
}

void quitHistory(Widget w, caddr_t client_data, XmAnyCallbackStruct *data)
{
  int imol;

  if (windows[HISTORY].widget == NULL) return;
  imol=windows[HISTORY].set;
  glXMakeCurrent(XtDisplay(windows[VIEWER].widget), XtWindow(windows[VIEWER].widget), windows[VIEWER].context);
  if (animate == TRAJECTORY)
    setHistoryAnimation((Widget)0, (caddr_t)0, (XmAnyCallbackStruct *)0) ;
  fremem((void **)&windows[HISTORY].font);
  XtUnmanageChild(XtParent(windows[HISTORY].widget));
  XtDestroyWidget(XtParent(windows[HISTORY].widget));
  windows[HISTORY].widget=0;
  deleteAnnotation(&historyAnnotation);
  if (molecules) restoreGeometry(molecules[imol].coord, imol);
  redraw(VIEWER);
  setMenuItem(VIEWER_OPTIMIZATION, True);
}

Boolean animateHistory(XtPointer data)
{
  static int direction=1;
  struct MOLECULE *mol;
  double xs, ys, zs;

  mol=&molecules[windows[HISTORY].set];
  if ((int)data > 0)
    mol->cycle=(int)data;
  else
  {
    mol->cycle+=direction;
    if (mol->cycle >= mol->nhist)
    {
      direction=(-1);
      mol->cycle=mol->nhist;
    }
    if (mol->cycle <= 1)
    {
      direction=1;
      mol->cycle=1;
    }
  }
  setGeometry(TRUE);
  getScreenCoordinates((double)(mol->cycle), 0.0, 0.0, &xs, &ys, &zs);
  windows[HISTORY].mouseX=(int)xs;
  redraw(VIEWER);
  redraw(HISTORY);
  return(False);
}

void historyKeyAction(KeySym keysym)
{
  XmToggleButtonCallbackStruct data;
  struct MOLECULE *mol;
  double xs, ys, zs;

  mol=&molecules[windows[HISTORY].set];
  switch (keysym)
  {
    case XK_Print: printDialog((Widget)0, (caddr_t)HISTORY, (XmAnyCallbackStruct *)0);
                   break;
    case XK_Left:  if (--mol->cycle < 1) mol->cycle=mol->nhist;
		   getScreenCoordinates((double)mol->cycle, 0.0, 0.0, &xs, &ys, &zs);
		   windows[HISTORY].mouseX=(int)xs;
                   break;
    case XK_Right: if (++mol->cycle > mol->nhist) mol->cycle=1;
		   getScreenCoordinates((double)mol->cycle, 0.0, 0.0, &xs, &ys, &zs);
		   windows[HISTORY].mouseX=(int)xs;
                   break;
    case XK_Tab:   if (nmolecule > 1)                                          
                   {                                                           
                     data.set=TRUE;                                            
                     do
                     {
                       windows[HISTORY].set++;                                  
                       if (windows[HISTORY].set >= nmolecule) windows[HISTORY].set=0;
                     } while (molecules[windows[HISTORY].set].optimization == NULL);
                     (void)selectMolecule((Widget)0,
					  (XtPointer)&windows[HISTORY].selectMenu[windows[HISTORY].set],
                                          &data);
                   }                   
                   break;
  }
  setGeometry(TRUE);
}
