/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                               C A L C M O . C                                *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: calcmo.c,v 1.6 2003/11/07 10:58:35 jrh Exp $
* $Log: calcmo.c,v $
* Revision 1.6  2003/11/07 10:58:35  jrh
* Release 2.4
*
* Revision 1.5  2000/12/10 15:02:43  jrh
* Release 2.3
*
* Revision 1.4  1999/05/24 01:24:54  jrh
* Release 2.2.1
*
* Revision 1.3  1999/02/07 21:45:16  jrh
* Release 2.2
*
* Revision 1.2  1998/01/26 00:47:08  jrh
* Release 2.1
*
* Revision 1.1  1996/12/10  18:39:57  jrh
* Initial revision
*
*/
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/times.h>
#include<X11/Xlib.h>
#include<X11/cursorfont.h>
#include "viewmol.h"
#include "dialog.h"

void setGridObject(struct GRIDOBJECT *, double, double, int, int, int);

extern double makemo(struct ATOM *, struct ORBITAL *, double, double, double,
                     int, int, int, int);
extern int runMoloch(double *, double, double, double, int, struct ORBITAL *, int);
extern void GetMessageBoxButton(Widget, XtPointer, caddr_t);
extern char *getStringResource(Widget, char *);
extern void *getmem(size_t, size_t);
extern void *expmem(void *, size_t, size_t);
extern void setCursor(Widget, int);
extern int norbs(struct MOLECULE *);
/* extern char *bfname(int);*/
extern void fremem(void **);
extern void restoreGeometry(struct SAVE *, int);
extern int  messgb(Widget, int, char *, struct PushButtonRow *, int);
extern int runProg(char *, int, char *, char *, char *, pid_t *);

extern struct WINDOW windows[];
extern struct MOLECULE *molecules;
extern double gridres;
extern int iwavef;
extern int bfatom;
extern Widget topShell;
extern char moloch[];

static int imol;

void calcmo(void)
{
  struct ORBITAL *orb=NULL;
  double fac, boxf, step;
#ifdef DEBUG
  double d, m, a;
#endif
  int mo=0, morb;
  register double x, y, z;
  register int i, j, k, l;

  imol=windows[MO].set;
  if (molecules[imol].ngridobjects == 0)
  {
    molecules[imol].ngridobjects++;
    molecules[imol].gridObjects=(struct GRIDOBJECT *)getmem(molecules[imol].ngridobjects,
                                 sizeof(struct GRIDOBJECT));
    setGridObject(&(molecules[imol].gridObjects[0]), windows[VIEWER].far,
                  gridres, iwavef, molecules[imol].imo, molecules[imol].ibasfu);
  }
  else
  {
    j=FALSE;
    for (i=0; i<molecules[imol].ngridobjects; i++)
    {
      if ((molecules[imol].gridObjects[i].type & ~GRIDREAD) == iwavef)
      {
        if ((iwavef == MOLECULAR_ORBITAL &&
             molecules[imol].gridObjects[i].mo == molecules[imol].imo) ||
            (iwavef == BASIS_IN_MO &&
             molecules[imol].gridObjects[i].mo == molecules[imol].imo &&
             molecules[imol].gridObjects[i].basisfunction == molecules[imol].ibasfu) ||
            (iwavef == BASIS_FUNCTION &&
             molecules[imol].gridObjects[i].basisfunction == molecules[imol].ibasfu) ||
             iwavef == DENSITY)
        {
          if (molecules[imol].gridObjects[i].resolution == gridres)
            return;
          else
          {
            fremem((void **)&(molecules[imol].gridObjects[i].grid));
            molecules[imol].ngridobjects--;
            j=TRUE;
          }
        }
      } 
    }
    if (!j) molecules[imol].gridObjects=(struct GRIDOBJECT *)expmem(molecules[imol].gridObjects,
                                         molecules[imol].ngridobjects+1, sizeof(struct GRIDOBJECT));
    setGridObject(&(molecules[imol].gridObjects[molecules[imol].ngridobjects]),
                  windows[VIEWER].far, gridres, iwavef, molecules[imol].imo,
			molecules[imol].ibasfu);
    molecules[imol].ngridobjects++;
  }

  setCursor(windows[VIEWER].widget, XC_watch);
  if (windows[MO].widget != NULL) setCursor(windows[MO].widget, XC_watch);
  fac=1.0/0.52917706;
  boxf=fac*windows[VIEWER].far;
  step=2.0*boxf/gridres;
  morb=norbs(&molecules[imol]);
  for (i=0; i<molecules[imol].na; i++)
  {
    molecules[imol].atoms[i].x=fac*(molecules[imol].atoms[i].x-molecules[imol].transx);
    molecules[imol].atoms[i].y=fac*(molecules[imol].atoms[i].y-molecules[imol].transy);
    molecules[imol].atoms[i].z=fac*(molecules[imol].atoms[i].z-molecules[imol].transz);
  }

  i=(int)(gridres)+1;
  molecules[imol].gridObjects[molecules[imol].ngridobjects-1].grid=
    (double *)getmem((size_t)(i*i*i), sizeof(double));
  switch (iwavef)
  {
    case BASIS_FUNCTION:    orb=(struct ORBITAL *)getmem((size_t)1, sizeof(struct ORBITAL));
                            orb->coeff=(double *)getmem((size_t)morb, sizeof(double));
                            orb->coeff[molecules[imol].ibasfu]=1.0;
                            mo=0;
                            break;
    case BASIS_IN_MO:       orb=(struct ORBITAL *)getmem((size_t)1, sizeof(struct ORBITAL));
                            orb->coeff=(double *)getmem((size_t)morb, sizeof(double));
                            orb->coeff[molecules[imol].ibasfu]=molecules[imol].orbitals[molecules[imol].imo].coeff[molecules[imol].ibasfu];
                            mo=0;
                            break;
    case MOLECULAR_ORBITAL: mo=molecules[imol].imo;
                            orb=molecules[imol].orbitals;
                            break;
    case DENSITY:           mo=-1;
                            orb=molecules[imol].orbitals;
                            break;
  }
#ifndef DEBUG
  if ((iwavef == MOLECULAR_ORBITAL || iwavef == DENSITY) && molecules[imol].needMoloch)
  {
#endif
    if (runMoloch(molecules[imol].gridObjects[molecules[imol].ngridobjects-1].grid,
                  gridres, boxf, step, mo, molecules[imol].orbitals,
                  molecules[imol].nbasfu)) iwavef=0;
#ifndef DEBUG
  }
  else
  {
#else
    d=0.0;
    m=0.0;
#endif
    i=0;
    z=-boxf;
    for (j=0; j<=gridres; j++)
    {
      y=-boxf;
      for (k=0; k<=gridres; k++)
      {
        x=-boxf;
        for (l=0; l<=gridres; l++)
        {
#ifdef DEBUG
          a=molecules[imol].gridObjects[molecules[imol].ngridobjects-1].grid[i];
#endif
          molecules[imol].gridObjects[molecules[imol].ngridobjects-1].grid[i++]
            =makemo(molecules[imol].atoms, orb, x, y, z, molecules[imol].na,
                    morb, molecules[imol].nbasfu, mo);
#ifdef DEBUG
          printf("%15.10f %15.10f %15.10f\n", a,
                 molecules[imol].gridObjects[molecules[imol].ngridobjects-1].grid[i-1],
                 a-molecules[imol].gridObjects[molecules[imol].ngridobjects-1].grid[i-1]);
          d=d+fabs(molecules[imol].gridObjects[molecules[imol].ngridobjects-1].grid[i-1]-a);
          m=m > fabs(molecules[imol].gridObjects[molecules[imol].ngridobjects-1].grid[i-1]-a)
           ? m : fabs(molecules[imol].gridObjects[molecules[imol].ngridobjects-1].grid[i-1]-a);
#endif
          x+=step;
        }
        y+=step;
      }
      z+=step;
    }
#ifndef DEBUG
  }
#else
  printf("%d grid points, average deviation %15.10f, maximum %15.10f\n", i,
         d/((gridres+1.0)*(gridres+1.0)*(gridres+1.0)), m);
#endif
  restoreGeometry(molecules[imol].coord, imol);
  if (iwavef == BASIS_FUNCTION || iwavef == BASIS_IN_MO)
  {
    fremem((void **)&orb->coeff);
    fremem((void **)&orb);
  }
  setCursor(windows[VIEWER].widget, XC_top_left_arrow);
  if (windows[MO].widget != NULL) setCursor(windows[MO].widget, XC_top_left_arrow);
}

int runMoloch(double *grid, double gridres, double box, double step, int imo,
              struct ORBITAL *orbitals, int nbasfu)
{
  static struct PushButtonRow buttons[] = {{"continue", GetMessageBoxButton,
                                            (XtPointer)0, NULL}};
  FILE *infile, *outfile;
  pid_t pid;
  char line[MAXLENLINE], mofile[MAXLENLINE], *word;
  register int i, j;

  if (access("control", F_OK))
  {
    word=getStringResource(topShell, "noControlFile");
    messgb(topShell, 1, word, buttons, 1);
    return(TRUE);
  }
  rename("control", "control.save");
  if (!access("propinp", F_OK))
    rename("propinp", "propinp.save");

  if ((infile=fopen("control.save", "r")) == NULL)
  {
    word=getStringResource(topShell, "unableToOpen");
    sprintf(line, word, "control.save");
    messgb(topShell, 1, line, buttons, 1);
    return(TRUE);
  }
  if ((outfile=fopen("control", "w")) == NULL)
  {
    word=getStringResource(topShell, "unableToOpen");
    sprintf(line, word, "control");
    messgb(topShell, 1, line, buttons, 1);
    return(TRUE);
  }
  while (fgets(line, MAXLENLINE, infile) != NULL)
  {
    if (strstr(line, "$end")) break;
    if (strstr(line, "$scfmo"))
    {
      fprintf(outfile, "%s", line);
      if ((word=strtok(line, "=")) != NULL)
        strcpy(mofile, strtok(NULL, " \t\n"));
      else
        strcpy(mofile, "control");
      continue;
    }
    if (!strstr(line, "$properties"))
    {
      fprintf(outfile, "%s", line);
      continue;
    }
    else
    {
      do
      {
        fgets(line, MAXLENLINE, infile);
      }
      while (!(word=strchr(line, '$')));
      continue;
    }
  }
  fprintf(outfile, "$properties file=propinp\n");
  fprintf(outfile, "$end\n");
  fclose(infile);
  fclose(outfile);

  outfile=fopen("propinp", "w");
  fprintf(outfile, "$properties\n");
  fprintf(outfile, "    trace                              off\n");
  fprintf(outfile, "    moments                            off\n");
  fprintf(outfile, "    potential                          off\n");
  fprintf(outfile, "    cowan-griffin                      off\n");
  fprintf(outfile, "    localization                       off\n");
  fprintf(outfile, "    population analyses                off\n");
  fprintf(outfile, "    plot                               active\n");
  fprintf(outfile, "    firstorder                         off\n");
  fprintf(outfile, "    fit                                off\n");
  j=0;
  for (i=0; i<=imo; i++)
    if (!strcmp(molecules[imol].orbitals[i].symmetry,
                molecules[imol].orbitals[imo].symmetry)) j++;
  fprintf(outfile, "$grid #1\n");
  if (imo != -1)
    fprintf(outfile, " mo %d%s\n", j, molecules[imol].orbitals[imo].symmetry);
  else
    fprintf(outfile, " mo\n");
  fprintf(outfile, " origin       .000000      .000000      .000000\n");
  fprintf(outfile, " vector1     1.000000      .000000      .000000\n");
  fprintf(outfile, " vector2      .000000     1.000000      .000000\n");
  fprintf(outfile, " vector3      .000000      .000000     1.000000\n");
  fprintf(outfile, " grid1 range %13.6f%13.6f points %d\n", -box, -box+gridres*step, (int)(gridres)+1);
  fprintf(outfile, " grid2 range %13.6f%13.6f points %d\n", -box, -box+gridres*step, (int)(gridres)+1);
  fprintf(outfile, " grid3 range %13.6f%13.6f points %d\n", -box, -box+gridres*step, (int)(gridres)+1);
  fprintf(outfile, " infile = %s\n", mofile);
  fprintf(outfile, " outfile = grid.dat\n");
  fprintf(outfile, "$end\n");
  fclose(outfile);
  if (runProg(moloch, TRUE, "/dev/null", "/dev/null", "/dev/null", &pid))
  {
    iwavef=ALL_OFF;
    word=getStringResource(topShell, "molochFailed");
    messgb(topShell, 1, word, buttons, 1);
    return(TRUE);
  }
  rename("control.save", "control");
  unlink("propinp");
  if (!access("propinp.save", F_OK)) rename("propinp.save", "propinp");

  if ((infile=fopen("grid.dat", "r")) == NULL)
  {
    word=getStringResource(topShell, "noMolochOutput");
    messgb(topShell, 1, word, buttons, 1);
    return(TRUE);
  }
  while (fgets(line, MAXLENLINE, infile) != NULL)
    if (strstr(line, "$plotdata")) break;
  i=0;
  while (fgets(line, MAXLENLINE, infile) != NULL)
  {
    if (strchr(line, '$')) break;
    while ((word=strchr(line, 'D')) != NULL) *word='E';
    word=line;
    if (strlen(line) > 76)
    {
      sscanf(word, "%16le", &grid[i++]);
      word+=16;
      while (sscanf(word, "%16le", &grid[i++]) != EOF) word+=16;
    }
    else
    {
      sscanf(word, "%15le", &grid[i++]);
      word+=15;
      while (sscanf(word, "%15le", &grid[i++]) != EOF) word+=15;
    }
    i--;
  }
  fclose(infile);
  unlink("grid.dat");
  return(FALSE);
}

void setGridObject(struct GRIDOBJECT *gridObject, double start, double gridres,
                   int iwavef, int imo, int ibasfu)
{
  char *word;

  gridObject->origin[0]=0.0;
  gridObject->origin[1]=0.0;
  gridObject->origin[2]=0.0;
  gridObject->vector1[0]=1.0;
  gridObject->vector1[1]=0.0;
  gridObject->vector1[2]=0.0;
  gridObject->vector2[0]=0.0;
  gridObject->vector2[1]=1.0;
  gridObject->vector2[2]=0.0;
  gridObject->vector3[0]=0.0;
  gridObject->vector3[1]=0.0;
  gridObject->vector3[2]=1.0;
  gridObject->start[0]=gridObject->start[1]=gridObject->start[2]=(-start);
  gridObject->step[0]=gridObject->step[1]=gridObject->step[2]=2.0*start/gridres;
  gridObject->ngridpoints[0]=gridObject->ngridpoints[1]=gridObject->ngridpoints[2]=(int)gridres;
  gridObject->npoints[0]=gridObject->ngridpoints[0];
  gridObject->npoints[1]=gridObject->ngridpoints[1];
  gridObject->npoints[2]=gridObject->ngridpoints[2];
  gridObject->resolution=gridres;
  gridObject->type=iwavef;
  gridObject->mo=imo;
  gridObject->basisfunction=ibasfu;
  switch (iwavef)
  {
    case BASIS_FUNCTION:    word=getStringResource(topShell, "basisfunctionTitle");
                            sprintf(gridObject->text, word, ibasfu+1,
                                    molecules[imol].atoms[bfatom].name,
/*                                  bfatom+1, bfname(ibasfu));*/
                                    bfatom+1, molecules[imol].atoms[bfatom].basisFunctionNames[ibasfu]);
                            break;
    case BASIS_IN_MO:       word=getStringResource(topShell, "basisfunctionInMOTitle");
                            sprintf(gridObject->text, word, ibasfu+1, imo+1,
                                    molecules[imol].atoms[bfatom].name, bfatom+1,
                                    molecules[imol].orbitals[imo].coeff[ibasfu],
/*                                  bfname(ibasfu));*/
                                    molecules[imol].atoms[bfatom].basisFunctionNames[ibasfu]);
                            break;
    case MOLECULAR_ORBITAL: word=getStringResource(topShell, "molecularOrbitalTitle");
                            sprintf(gridObject->text, word, imo+1,
                                    molecules[imol].orbitals[imo].symmetry,
                                    molecules[imol].orbitals[imo].energy);
                            break;
    case DENSITY:           word=getStringResource(topShell, "electronDensityTitle");
                            sprintf(gridObject->text, word);
                            break;
  }
}

clock_t getCPUTime(void)
{
  struct tms buffer;

  if (times(&buffer) != -1)
    return(buffer.tms_utime+buffer.tms_stime);
  else
    return((clock_t)0);
}
