/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                                 S A V E . C                                  *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: save.c,v 1.6 2003/11/07 11:15:35 jrh Exp $
* $Log: save.c,v $
* Revision 1.6  2003/11/07 11:15:35  jrh
* Release 2.4
*
* Revision 1.5  2000/12/10 15:16:29  jrh
* Release 2.3
*
* Revision 1.4  1999/05/24 01:27:35  jrh
* Release 2.2.1
*
* Revision 1.3  1999/02/07 21:56:49  jrh
* Release 2.2
*
* Revision 1.2  1998/01/26 00:49:30  jrh
* Release 2.1
*
* Revision 1.1  1996/12/10  18:43:57  jrh
* Initial revision
*
*/
#include<locale.h>
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<X11/Xlib.h>
#include<X11/Xutil.h>
#include<Xm/Xm.h>
#include<GL/glu.h>
#include "viewmol.h"
#include "dialog.h"

extern void GetMessageBoxButton(Widget, XtPointer, caddr_t);
extern char *selectFile(char *, char *, int);
extern char *getStringResource(Widget, char *);
extern int  getIntResource(Widget, char *);
extern void getRGBColor(Widget, Pixel, float *, float *, float *);
extern int  messgb(Widget, int, char *, struct PushButtonRow *, int);
extern int checkFile(char **);
extern double bondLength(struct ATOM *, int, int);
extern double bondAngle(struct ATOM *, int, int, int);
extern double dist(double, double, double, double, double, double);
extern double angle(double, double, double, double, double, double, double,
                    double, double);   
extern void *getmem(size_t, size_t);
extern void *expmem(void *, size_t, size_t);
extern void fremem(void **);

void checkStringResource(char *, const char **, int, int, char **, int *);
void checkIntResource(char *, int, char **, int *);
void checkDoubleResource(char *, double, char **, int *);
void checkWindowResource(struct WINDOW *, char *, char **, int *);
void copyLanguageResources(char **, int *);

extern struct MOLECULE *molecules;
extern struct OPTION *output;
extern struct WINDOW windows[];
extern double paperWidth, paperHeight;
extern double sphereres, lineWidth, level, denres, hbondThreshold;
extern int outputType, saveLanguage, automaticRecalculate, saveAll;
extern int primitive, bondType, interp, simplify, nmolecule, thermoUnits;
extern Widget topShell;
extern char language[], webbrowser[], moloch[], raytracer[], displayImage[];

void saveMolecule(char *name)
{
  static struct PushButtonRow buttons1[] = {{"continue", GetMessageBoxButton, (XtPointer)0, NULL},
                                            {"cancel", GetMessageBoxButton, (XtPointer)1, NULL}};
  static struct PushButtonRow buttons2[] = {{"continue", GetMessageBoxButton, (XtPointer)0, NULL}};
  static char *filename="vm_coord";
  struct MOLECULE *mol;
  FILE *file;
  double a2bohr=1.0/0.52917706;
  double a, b, c, alpha, beta, gamma;
  char *word, line[MAXLENLINE], command[MAXLENLINE];
  const char *locale;
  int cell;
  register int i, j, k;

  if (name == NULL)
    filename=selectFile("*", filename, TRUE);
  else
    filename=name;

  if (filename != NULL)
  {
    if (!access(filename, F_OK))
    {
      word=getStringResource(topShell, "FileExists");
      sprintf(line, word, filename);
      if (messgb(topShell, 1, line, buttons1, 2) == 1) return;
    }
    strcpy(command, output[outputType].command);
    word=command;
    if (!(checkFile(&word)))
    {
      word=getStringResource(topShell, "noFile");
      strtok(command, " \t");                                                    
      sprintf(line, word, command);
      messgb(topShell, 3, line, buttons2, 1);                                    
      return;
    }
    sprintf(line, word, filename);
    if ((file=popen(line, "w")) == NULL)
    {
      word=getStringResource(topShell, "cannotExecute");
      sprintf(line, word, command);
      messgb(topShell, 3, line, buttons2, 1);
      return;
    }

    locale=setlocale(LC_ALL, "C");
    if (windows[VIEWER].set >= 0)
    {
      mol=&molecules[windows[VIEWER].set];
      if (mol->unitcell)
      {
        a=bondLength(mol->unitcell->corners, 0, 1);
        b=bondLength(mol->unitcell->corners, 0, 2);
        c=bondLength(mol->unitcell->corners, 0, 3);
        alpha=bondAngle(mol->unitcell->corners, 2, 0, 3);
        beta=bondAngle(mol->unitcell->corners, 1, 0, 3);
        gamma=bondAngle(mol->unitcell->corners, 1, 0, 2);
        fprintf(file, "$unitcell %10.6f %10.6f %10.6f %10.3f %10.3f %10.3f\n",
                a*a2bohr, b*a2bohr, c*a2bohr, alpha, beta, gamma);
      }
      fprintf(file, "$coord\n");
      for (i=0; i<mol->na; i++)
      {
	if (!(mol->atoms[i].flags & DISPLAY_ONLY))
	{
          if (saveAll || (mol->atoms[i].flags & SELECTED))
          {
            fprintf(file, "%22.14f%22.14f%22.14f  %s\n",
                    (mol->atoms[i].x-mol->transx)*a2bohr,
                    (mol->atoms[i].y-mol->transy)*a2bohr,
                    (mol->atoms[i].z-mol->transz)*a2bohr,
                     mol->atoms[i].name);
          }
        }
      }
      fprintf(file, "$bonds\n");
      for (i=0; i<mol->nb; i++)
      {
        if (!(mol->atoms[mol->bonds[i].first].flags & DISPLAY_ONLY) &&
            !(mol->atoms[mol->bonds[i].second].flags & DISPLAY_ONLY))
        {
          fprintf(file, "%d %d %d\n", mol->bonds[i].first+1,
                  mol->bonds[i].second+1, mol->bonds[i].order);
        }
      }
      if (mol->nhist > 0)
      {
        fprintf(file, "$grad          cartesian gradients\n");
        for (i=0; i<mol->nhist; i++)
        {
          fprintf(file, "  cycle =  %d    SCF energy = %17.10f   |dE/dxyz| =   %9.6f\n",
                  i+1, mol->optimization[i].energy, mol->optimization[i].gnorm);
          j=mol->optimization[i].coords;
          if (mol->unitcell)
          {
/*          a=dist(mol->history[j+n].x, mol->history[j+n].y, mol->history[j+n].z,
                   mol->history[j+n+1].x, mol->history[j+n+1].y, mol->history[j+n+1].z);
            b=dist(mol->history[j+n].x, mol->history[j+n].y, mol->history[j+n].z,
                   mol->history[j+n+2].x, mol->history[j+n+2].y, mol->history[j+n+2].z);
            c=dist(mol->history[j+n].x, mol->history[j+n].y, mol->history[j+n].z,
                   mol->history[j+n+3].x, mol->history[j+n+3].y, mol->history[j+n+3].z);
            alpha=angle(mol->history[j+n+2].x, mol->history[j+n+2].y, mol->history[j+n+2].z,
                        mol->history[j+n].x, mol->history[j+n].y, mol->history[j+n].z,
                        mol->history[j+n+3].x, mol->history[j+n+3].y, mol->history[j+n+3].z);
            beta=angle(mol->history[j+n+1].x, mol->history[j+n+1].y, mol->history[j+n+1].z,
                       mol->history[j+n].x, mol->history[j+n].y, mol->history[j+n].z,
                       mol->history[j+n+3].x, mol->history[j+n+3].y, mol->history[j+n+3].z);
            gamma=angle(mol->history[j+n+1].x, mol->history[j+n+1].y, mol->history[j+n+1].z,
                        mol->history[j+n].x, mol->history[j+n].y, mol->history[j+n].z,
                        mol->history[j+n+2].x, mol->history[j+n+2].y, mol->history[j+n+2].z); */
            fprintf(file, "  unitcell %10.6f %10.6f %10.6f %10.3f %10.3f %10.3f\n",
                    mol->unitcell[i+1].a*a2bohr, mol->unitcell[i+1].b*a2bohr,
                    mol->unitcell[i+1].c*a2bohr, mol->unitcell[i+1].alpha,
                    mol->unitcell[i+1].beta, mol->unitcell[i+1].gamma);
          }
          for (k=j; k<j+mol->na; k++)
          {
	    if (!(mol->atoms[k-j].flags & DISPLAY_ONLY))
	    {
              if (saveAll || (mol->atoms[k-j].flags & SELECTED))
              {
                 fprintf(file, "%22.14f%22.14f%22.14f  %s\n",
                         mol->history[k].x*a2bohr, mol->history[k].y*a2bohr,
                         mol->history[k].z*a2bohr, mol->atoms[k-j].name);
              }
	    }
          }
          for (k=j; k<j+mol->na; k++)
          {
	    if (!(mol->atoms[k-j].flags & DISPLAY_ONLY))
	    {
              if (saveAll || (mol->atoms[k-j].flags & SELECTED))
              {
                  fprintf(file, "%22.14f%22.14f%22.14f\n",
                          mol->history[k].gx/a2bohr, mol->history[k].gy/a2bohr,
                          mol->history[k].gz/a2bohr);
              }
	    }
          }
        }
      }
    }
    else
    {
      cell=FALSE;
      for (i=0; i<nmolecule; i++)
      {
        mol=&molecules[i];
        if (mol->unitcell && !cell)
        {
/*        a=bondLength(mol->unitcell->corners, 0, 1);
          b=bondLength(mol->unitcell->corners, 0, 2);
          c=bondLength(mol->unitcell->corners, 0, 3);
          alpha=bondAngle(mol->unitcell->corners, 2, 0, 3);
          beta=bondAngle(mol->unitcell->corners, 1, 0, 3);
          gamma=bondAngle(mol->unitcell->corners, 1, 0, 2); */
          fprintf(file, "$unitcell %10.6f %10.6f %10.6f %10.3f %10.3f %10.3f\n",
                  mol->unitcell->a*a2bohr, mol->unitcell->b*a2bohr,
                  mol->unitcell->c*a2bohr, mol->unitcell->alpha,
                  mol->unitcell->beta, mol->unitcell->gamma);
          cell=TRUE;
        }
        if (i == 0) fprintf(file, "$coord\n");
        for (j=0; j<mol->na; j++)
          fprintf(file, "%22.14f%22.14f%22.14f  %s\n",
                  (mol->atoms[j].x-mol->transx)*a2bohr,
                  (mol->atoms[j].y-mol->transy)*a2bohr,
                  (mol->atoms[j].z-mol->transz)*a2bohr,
                   mol->atoms[j].name);
      }
      fprintf(file, "$bonds\n");
      for (i=0; i<nmolecule; i++)
      {
        mol=&molecules[i];
        for (j=0; j<mol->nb; j++)
          fprintf(file, "%d %d %d\n", mol->bonds[j].first+1,
                  mol->bonds[j].second+1, mol->bonds[j].order);
      }
    }
    fprintf(file, "$end\n");
    pclose(file);
    (void)setlocale(LC_ALL, locale);

    if (name == NULL)
    {
      word=getStringResource(topShell, "MoleculeSaved");
      sprintf(line, word, filename);
      messgb(topShell, 0, line, buttons2, 1);
    }
  }
}

void saveConfiguration(void)
{
  static struct PushButtonRow buttons[] = {{"continue", GetMessageBoxButton, (XtPointer)0, NULL}};
  FILE *file, *tmp;
  const char *models[]={"wire", "stick", "ball", "cpk"};
  const char *primitives[]={"dot", "line", "surface"};
  const char *bondtypes[]={"single", "multiple", "conjugated"};
  const char *simplifyrotation[]={"False", "True"};
  const char *interpolation[]={"none", "linear", "logarithmic"};
  const char *recalculate[]={"False", "True"};
  const char *units[]={"joules", "calories", "thermochemical calories"};
  const char *locale;
  char *paper[]={"A5", "A4", "A3", "Letter", "Legal", "100.0x100.0"};
  int nconfig=0, paperSize;
  char *word, *config=NULL, *p;
  char *home, f[MAXLENLINE], line[MAXLENLINE];
  char **str=(char **)&f;
  register int i, found;

  checkStringResource("model", models, 4, windows[VIEWER].mode, &config,
                      &nconfig);
  checkStringResource("drawingMode", primitives, 3, primitive-GLU_POINT+1, &config,
                      &nconfig);
  checkStringResource("bondType", bondtypes, 3, bondType, &config,
                      &nconfig);
  checkStringResource("simplifyWhileRotating", simplifyrotation, 2, simplify+1, &config,
                      &nconfig);
  checkStringResource("interpolation", interpolation, 3, interp, &config,
                      &nconfig);
  checkStringResource("automaticRecalculation", recalculate, 2, automaticRecalculate+1, &config,
                      &nconfig);
  if ((paperWidth == 210.2 && paperHeight == 148.6) ||
      (paperWidth == 148.6 && paperHeight == 210.2))
    paperSize=A5;
  else if ((paperWidth == 297.3 && paperHeight == 210.2) ||
           (paperWidth == 210.2 && paperHeight == 297.3))
    paperSize=A4;
  else if ((paperWidth == 420.4 && paperHeight == 297.3) ||
           (paperWidth == 297.3 && paperHeight == 420.4))
    paperSize=A3;
  else if ((paperWidth == 278.0 && paperHeight == 216.0) ||
           (paperWidth == 216.0 && paperHeight == 278.0))
    paperSize=LETTER;
  else if ((paperWidth == 356.0 && paperHeight == 216.0) ||
           (paperWidth == 216.0 && paperHeight == 356.0))
    paperSize=LEGAL;
  else
  {
    paperSize=USER;
    sprintf(paper[5], "%5.1fx%5.1f", paperWidth, paperHeight);
  }
  checkStringResource("paperSize", (const char **)paper, 6, paperSize, &config,
                      &nconfig);
  *str=(char *)&webbrowser;
  checkStringResource("webBrowser", (const char **)str, 1, 1, &config, &nconfig);
  *str=(char *)&moloch;
  checkStringResource("Moloch", (const char **)str, 1, 1, &config, &nconfig);
  *str=(char *)&raytracer;
  checkStringResource("Raytracer", (const char **)str, 1, 1, &config, &nconfig);
  *str=(char *)&displayImage;
  checkStringResource("DisplayImage", (const char **)str, 1, 1, &config, &nconfig);
  checkIntResource("sphereResolution", (int)sphereres, &config, &nconfig);
  checkIntResource("lineWidth", (int)lineWidth, &config, &nconfig);
  checkDoubleResource("isosurface", level, &config, &nconfig);
  checkDoubleResource("densityResolution", denres, &config, &nconfig);
  checkDoubleResource("hydrogenBondThreshold", hbondThreshold, &config, &nconfig);
  checkStringResource("thermoUnits", units, 3, thermoUnits+1, &config, &nconfig);
  checkWindowResource(&windows[VIEWER], "viewer", &config, &nconfig);
  checkWindowResource(&windows[SPECTRUM], "spectrum", &config, &nconfig);
  checkWindowResource(&windows[HISTORY], "history", &config, &nconfig);
  checkWindowResource(&windows[MO], "MODiagram", &config, &nconfig);
  if (saveLanguage) copyLanguageResources(&config, &nconfig);

  locale=setlocale(LC_ALL, "C");
  if ((home=getenv("HOME")) != NULL)
  {
    strcpy(f, home);
    strcat(f, "/.Xdefaults");
    tmp=tmpfile();
    if (!access(f, W_OK))
    {
      file=fopen(f, "r");
      while (fgets(line, MAXLENLINE, file) != NULL)
      {
        if (!strstr(line, "Viewmol"))
          fprintf(tmp, "%s", line);
        else
        {
          found=FALSE;
          p=config;
          for (i=0; i<nconfig; i++)
          {
            if (*p != '\0')
            {
              word=strchr(p, ':')-1;
              if(!strncmp(line, p, word-p))
              {
                fprintf(tmp, "%s", p);
                *p='\0';
                found=TRUE;
                break;
              }
            }
            p+=MAXLENLINE;
          }
          if (!found) fprintf(tmp, "%s", line);
        }
      }
      fclose(file);
    }
    p=config;
    found=TRUE;
    for (i=0; i<nconfig; i++)
    {
      if (*p != '\0')
      {
        if (found)
        {
          fprintf(tmp, "!-----------------------------------------------------------------\n");
          fprintf(tmp, "! The following resources have been set automatically by Viewmol's\n");
          fprintf(tmp, "! \"Configuration .../Save\" feature\n");
          fprintf(tmp, "!-----------------------------------------------------------------\n");
          found=FALSE;
        }
        fprintf(tmp, "%s", p);
      }
      p+=MAXLENLINE;
    }
    rewind(tmp);
    file=fopen(f, "w");
    while (fgets(line, MAXLENLINE, tmp) != NULL)
      fprintf(file, "%s", line);
    fclose(tmp);
    fclose(file);

    word=getStringResource(topShell, "ConfigurationSaved");
    messgb(topShell, 0, word, buttons, 1);
  }
  (void)setlocale(LC_ALL, locale);
  if (config)
    fremem((void *)&config);
}

void checkStringResource(char *resource, const char **types, int ntypes, int comp,
                         char **config, int *nconfig)
{
  char *word, *p;
  register int i;

  comp--;
  word=getStringResource(topShell, resource);
  for (i=0; i<ntypes; i++)
    if (strstr(word, types[i])) break;
  if (i != comp)
  {
    (*nconfig)++;
    if (*config == NULL)
    {
      *config=(char *)getmem(*nconfig, MAXLENLINE*sizeof(char));
      p=*config;
    }
    else
    {
      *config=(char *)expmem((void *)*config, *nconfig, MAXLENLINE*sizeof(char));
      p=*config+((*nconfig)-1)*MAXLENLINE;
    }
    strcpy(p, "Viewmol.");
    strcat(p, resource);
    strcat(p, ": ");
    strcat(p, types[comp]);
    strcat(p, "\n");
  }
}

void checkIntResource(char *resource, int comp, char **config, int *nconfig)
{
  char line[MAXLENLINE], *p;

  if (comp != getIntResource(topShell, resource))
  {
    (*nconfig)++;
    if (*config == NULL)
    {
      *config=(char *)getmem(*nconfig, MAXLENLINE*sizeof(char));
      p=*config;
    }
    else
    {
      *config=(char *)expmem((void *)*config, *nconfig, MAXLENLINE*sizeof(char));
      p=*config+((*nconfig)-1)*MAXLENLINE;
    }
    strcpy(p, "Viewmol.");
    strcat(p, resource);
    strcat(p, ": ");
    sprintf(line, "%d\n", comp);
    strcat(p, line);
  }
}

void checkDoubleResource(char *resource, double comp, char **config, int *nconfig)
{
  char line[MAXLENLINE], *p;

  if (comp != atof(getStringResource(topShell, resource)))
  {
    (*nconfig)++;
    if (*config == NULL)
    {
      *config=(char *)getmem(*nconfig, MAXLENLINE*sizeof(char));
      p=*config;
    }
    else
    {
      *config=(char *)expmem((void *)*config, *nconfig, MAXLENLINE*sizeof(char));
      p=*config+((*nconfig)-1)*MAXLENLINE;
    }
    strcpy(p, "Viewmol.");
    strcat(p, resource);
    strcat(p, ": ");
    sprintf(line, "%f\n", comp);
    strcat(p, line);
  }
}

void checkWindowResource(struct WINDOW *window, char *resource, char **config, int *nconfig)
{
  XWindowAttributes attributes;
  Window dummy;
  Screen *screen;
  Pixel pixel;
  int xw, yw;
  float r, g, b;
  int x, y, which, update=FALSE;
  unsigned int w, h;
  char line[MAXLENLINE], *p;

  if (window->widget != 0)
  {
    XtVaGetValues(window->widget, XmNbackground, &pixel, NULL);
    getRGBColor(window->widget, pixel, &r, &g, &b);
    if (r != window->background_rgb[0] || g != window->background_rgb[1] || b != window->background_rgb[2])
    {
      (*nconfig)++;
      if (*config == NULL)
      {
        *config=(char *)getmem(*nconfig, MAXLENLINE*sizeof(char));
        p=*config;
      }
      else
      {
        *config=(char *)expmem((void *)*config, *nconfig, MAXLENLINE*sizeof(char));
        p=*config+((*nconfig)-1)*MAXLENLINE;
      }
      strcpy(p, "Viewmol.");
      strcat(p, resource);
      strcat(p, ".canvas.background: rgb:");
      sprintf(line, "%4x/%4x/%4x\n", (int)(65535.*window->background_rgb[0]),
              (int)(65535.*window->background_rgb[1]), (int)(65535.*window->background_rgb[2]));
      strcat(p, line);
    }
    XtVaGetValues(window->widget, XmNforeground, &pixel, NULL);
    getRGBColor(window->widget, pixel, &r, &g, &b);
    if (r != window->foreground_rgb[0] || g != window->foreground_rgb[1] || b != window->foreground_rgb[2])
    {
      (*nconfig)++;
      if (*config == NULL)
      {
        *config=(char *)getmem(*nconfig, MAXLENLINE*sizeof(char));
        p=*config;
      }
      else
      {
        *config=(char *)expmem((void *)*config, *nconfig, MAXLENLINE*sizeof(char));
        p=*config+((*nconfig)-1)*MAXLENLINE;
      }
      strcpy(p, "Viewmol.");
      strcat(p, resource);
      strcat(p, ".foreground: rgb:");
      sprintf(line, "%4x/%4x/%4x\n", (int)(65535.*window->foreground_rgb[0]),
              (int)(65535.*window->foreground_rgb[1]), (int)(65535.*window->foreground_rgb[2]));
      strcat(p, line);
    }
    XGetWindowAttributes(XtDisplay(window->widget), XtWindow(window->widget),
                         &attributes);
    XTranslateCoordinates(XtDisplay(window->widget), XtWindow(window->widget),
                          attributes.root, -attributes.border_width,
                          -attributes.border_width, &xw, &yw, &dummy);
    screen=XtScreen(window->widget);
    p=getStringResource(window->widget, "geometry");
    which=XParseGeometry(p, &x, &y, &w, &h);
    if (which & XValue)
    {
      if (which & XNegative)
        x=WidthOfScreen(screen)-w+x;
      if (x != xw) update=TRUE;
    }
    if (which & YValue)
    {
      if (which & YNegative)
        y=HeightOfScreen(screen)-h+y;
      if (y != yw) update=TRUE;
    }
    if (which & WidthValue)
      if (w != attributes.width) update=TRUE;
    if (which & HeightValue)
      if (h != attributes.height) update=TRUE;
    if (update)
    {
      (*nconfig)++;
      if (*config == NULL)
      {
        *config=(char *)getmem(*nconfig, MAXLENLINE*sizeof(char));
        p=*config;
      }
      else
      {
        *config=(char *)expmem((void *)*config, *nconfig, MAXLENLINE*sizeof(char));
        p=*config+((*nconfig)-1)*MAXLENLINE;
      }
      strcpy(p, "Viewmol.");
      if (strcmp(resource, "viewer"))
      {
        strcat(p, resource);
        strcat(p, ".");
      }
      strcat(p, "geometry: ");
      sprintf(line, "%dx%d+%d+%d\n", attributes.width, attributes.height, xw, yw);
      strcat(p, line);
    }
  }
}

void copyLanguageResources(char **config, int *nconfig)
{
  FILE *file;
  int copy=FALSE, mc, i;
  char filename[FILENAME_MAX], line[MAXLENLINE], *p;

  strcpy(filename, getenv("VIEWMOLPATH"));
  strcat(filename, "/locale/");
  strcat(filename, language);
  strcat(filename, "/Viewmol");
  if ((file=fopen(filename, "r")) != NULL)
  {
    mc=*nconfig;
    mc+=350;
    if (*config == NULL)
    {
      *config=(char *)getmem(mc, MAXLENLINE*sizeof(char));
      p=*config;
    }
    else
    {
      *config=(char *)expmem((void *)*config, mc, MAXLENLINE*sizeof(char));
      p=*config+(*nconfig)*MAXLENLINE;
    }

    i=*nconfig;
    while (fgets(line, MAXLENLINE, file) != NULL)
    {
      if (!strncmp(line, "Viewmol.language", 16)) copy=TRUE;
      if (copy)
      {
        strncpy(p, line, MAXLENLINE);
        if (++i >= mc)
        {
          mc+=50;
          *config=(char *)expmem((void *)*config, mc, MAXLENLINE*sizeof(char));
          p=*config+i*MAXLENLINE;
        }
        else
          p+=MAXLENLINE;
      }
    }
    *nconfig+=i;
    fclose(file);
  }
}
