/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                                G E T R C . C                                 *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: getrc.c,v 1.6 2003/11/07 11:02:15 jrh Exp $
* $Log: getrc.c,v $
* Revision 1.6  2003/11/07 11:02:15  jrh
* Release 2.4
*
* Revision 1.5  2000/12/10 15:07:37  jrh
* Release 2.3
*
* Revision 1.4  1999/05/24 01:25:42  jrh
* Release 2.2.1
*
* Revision 1.3  1999/02/07 21:49:34  jrh
* Release 2.2
*
* Revision 1.2  1998/01/26 00:47:51  jrh
* Release 2.1
*
* Revision 1.1  1996/12/10  18:41:02  jrh
* Initial revision
*
*/
#include<ctype.h>
#include<locale.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/utsname.h>
#include<X11/Xlib.h>
#include<X11/StringDefs.h>
#include<X11/Intrinsic.h>
#include<Xm/Xm.h>
#include<GL/glu.h>
#include "viewmol.h"
#include "dialog.h"
#include "isotopes.h"

void getPrimitive(char *, int *, int);
int  getColor(GLfloat *p);
void unknownResource(char *, char *);
void storeCommand(char *, char *, char *);
void setBond(struct MOLECULE *);
void checkProgram(char *, char *);
void getFormats(void);

extern void GetMessageBoxButton(Widget, XtPointer, caddr_t);
extern char *getStringResource(Widget, char *);
extern int  getIntResource(Widget, char *);
extern void getRGBColor(Widget, Pixel, float *, float *, float *);
extern int checkFile(char **);
extern XVisualInfo *getVisualInfo(Display *, Visual *);
extern void *getmem(size_t, size_t);
extern void *expmem(void *, size_t, size_t);
extern void fremem(void **);
extern void osname(char *);
extern int messgb(Widget, int, char *, struct PushButtonRow *, int);
extern void loadAtomColors(XVisualInfo *, Colormap);
extern void GetPapersize(Widget, caddr_t, XmToggleButtonCallbackStruct *);

extern struct MOLECULE *molecules;
extern struct ELEMENT *elements;
extern struct WINDOW windows[];
extern struct OPTION *options;
extern struct OPTION *output;
extern Pixel stdcol[9];
extern char webbrowser[MAXLENLINE], moloch[MAXLENLINE], raytracer[MAXLENLINE], displayImage[MAXLENLINE];
extern int ne, nopt, noutput;
extern double bndfac, temp, sphereres, denres, lineWidth;
extern double paperHeight, paperWidth;
extern int debug, label;
extern int lines, thermoUnits;
extern int primitive, bondType, unit, automaticRecalculate;
extern double level, hbondThreshold;
extern char *formBondLength, *formBondAngle, *formTorsionAngle;
extern int rgbMode, simplify, interp, showWarning;
extern Widget topShell;
extern XtAppContext app;

static char viewmolpath[MAXLENLINE];

#define DEFAULTPATH "/usr/local/lib/viewmol"

int getrc(void)
{
  struct OPTION *oldOptions;
  FILE *file;
  static struct PushButtonRow buttons1[] = {{"exit", GetMessageBoxButton, (XtPointer)0, NULL}};
  const char *locale;
  char line[MAXLENLINE], rcfile[MAXLENLINE], *word;
#ifdef DARWIN
  char os[_SYS_NAMELEN+12];
#else
  char os[SYS_NMLN+12];
#endif
  size_t mne=115;
  int nline=0, error, i;

  debug=0;
  label=FALSE;
  locale=setlocale(LC_ALL, "C");

/* Find default model */

  word=getStringResource(topShell, "model");
  if (strstr(word, "wire"))
    windows[VIEWER].mode=WIREMODEL;
  else if (strstr(word, "stick"))
    windows[VIEWER].mode=STICKMODEL;
  else if (strstr(word, "ball"))
    windows[VIEWER].mode=BALLMODEL;
  else if (strstr(word, "cpk"))
    windows[VIEWER].mode=CUPMODEL;
  else
  {
    unknownResource("Viewmol.model", word);
    windows[VIEWER].mode=WIREMODEL;
  }

/* Find default drawing primitive */

  word=getStringResource(topShell, "drawingMode");
  if (strstr(word, "dot"))
    primitive=GLU_POINT;
  else if (strstr(word, "line"))
    primitive=GLU_LINE;
  else if (strstr(word, "surface"))
    primitive=GLU_FILL;
  else
  {
    unknownResource("Viewmol.drawingMode", word);
    primitive=GLU_FILL;
  }

/* Find default sphere/cylinder/arrow resolution and line
   width for wire model */

  sphereres=(double)getIntResource(topShell, "sphereResolution");
  lineWidth=(double)getIntResource(topShell, "lineWidth");

  word=getStringResource(topShell, "bondType");
  if (strstr(word, "single"))
    bondType=SINGLE_BONDS;
  else if (strstr(word, "multiple"))
    bondType=MULTIPLE_BONDS;
  else if (strstr(word, "conjugated"))
    bondType=CONJUGATION;
  else
  {
    unknownResource("Viewmol.bondType", word);
    bondType=CONJUGATION;
  }
  word=getStringResource(topShell, "simplifyWhileRotating");
  if (!strcmp(word, "True")) simplify=TRUE;
  else                       simplify=FALSE;

  word=getStringResource(topShell, "automaticRecalculation");
  if (!strcmp(word, "True")) automaticRecalculate=TRUE;
  else                       automaticRecalculate=FALSE;

  word=getStringResource(topShell, "interpolation");
  if (!strcmp(word, "none"))
    interp=IP_NONE;
  else if (!strcmp(word, "linear"))
    interp=IP_LINEAR;
  else if (!strcmp(word, "logarithmic"))
    interp=IP_LOG;
  else
  {
    unknownResource("Viewmol.interpolation", word);
    interp=IP_LINEAR;
  }
  hbondThreshold=atof(getStringResource(topShell, "hydrogenBondThreshold"));

  /* Default units for thermodynamics */
  word=getStringResource(topShell, "thermoUnits");
  if (!strcmp(word, "joules"))
    thermoUnits=JOULES;
  else if (!strcmp(word, "calories"))
    thermoUnits=CALORIES;
  else if (!strcmp(word, "thermochemical calories"))
    thermoUnits=THERMOCALORIES;
  else
  {
    unknownResource("Viewmol.thermoUnits", word);
    thermoUnits=JOULES;
  }

  temp=298.15;
  lines=TRUE;
  windows[SPECTRUM].foreground=stdcol[BLACK];
  windows[SPECTRUM].background=stdcol[WHITE];
  windows[SPECTRUM].mode=SPECTRUM_IR;
  word=getStringResource(topShell, "wavenumbers");
  windows[SPECTRUM].left=atof(strtok(word, ":"));
  windows[SPECTRUM].right=atof(strtok(NULL, ":"));
  windows[SPECTRUM].bottom=100.0;
  windows[SPECTRUM].top=0.0;
  windows[SPECTRUM].near=0.0;
  windows[SPECTRUM].far=1.0;
  windows[HISTORY].foreground=stdcol[BLUE];
  windows[HISTORY].background=stdcol[WHITE];
  windows[HISTORY].mode=SCALES | ENERGY | GNORM;
  windows[HISTORY].left=1.0;
  windows[HISTORY].bottom=0.0;
  windows[HISTORY].near=0.0;
  windows[HISTORY].far=1.0;
  unit=HARTREE;
  windows[MO].foreground=stdcol[BLACK];
  windows[MO].background=stdcol[WHITE];
  windows[MO].mode=ENERGY_LEVELS;
  windows[MO].left=0.0;
  windows[MO].right=1.0;
  windows[MO].near=0.0;
  windows[MO].far=1.0;
  level=atof(getStringResource(topShell, "isosurface"));
  denres=atof(getStringResource(topShell, "densityResolution"));
  getFormats();

/* Get commands to start Web browser, Moloch, Rayshade, and display program for images
   and check if programs are accessible */

  word=getStringResource(topShell, "webBrowser");
  checkProgram(webbrowser, word);
  word=getStringResource(topShell, "Moloch");
  checkProgram(moloch, word);
  word=getStringResource(topShell, "Raytracer");
  checkProgram(raytracer, word);
  word=getStringResource(topShell, "DisplayImage");
  checkProgram(displayImage, word);

/* Get paper size */

  word=getStringResource(topShell, "paperSize");
  if (strchr(word, 'x') != NULL)
  {
    paperWidth=atof(strtok(word, "x"));
    paperHeight=atof(strtok(NULL, "x"));
    /* We have to call GetPapersize to set the paper variable in printform */
    GetPapersize((Widget)0, (caddr_t)USER, (XmToggleButtonCallbackStruct *)0);
  }
  else
  {
    if (strstr(word, "A5") != NULL)
      GetPapersize((Widget)0, (caddr_t)A5, (XmToggleButtonCallbackStruct *)0);
    else if (strstr(word, "A4") != NULL)
      GetPapersize((Widget)0, (caddr_t)A4, (XmToggleButtonCallbackStruct *)0);
    else if (strstr(word, "A3") != NULL)
      GetPapersize((Widget)0, (caddr_t)A3, (XmToggleButtonCallbackStruct *)0);
    else if (strstr(word, "Letter") != NULL)
      GetPapersize((Widget)0, (caddr_t)LETTER, (XmToggleButtonCallbackStruct *)0);
    else if (strstr(word, "Legal") != NULL)
      GetPapersize((Widget)0, (caddr_t)LEGAL, (XmToggleButtonCallbackStruct *)0);
    else
    {
      unknownResource("Viewmol.paperSize", word);
      GetPapersize((Widget)0, (caddr_t)A4, (XmToggleButtonCallbackStruct *)0);
    }
  }

  elements=(struct ELEMENT *)getmem(mne, sizeof(struct ELEMENT));

  if (getenv("VIEWMOLPATH") == NULL)
  {
    sprintf(viewmolpath, "VIEWMOLPATH=%s", DEFAULTPATH);
    putenv(viewmolpath);
  }
  strcpy(rcfile, "viewmolrc");
  if ((file=fopen(rcfile, "r")) == NULL)
  {
    if ((word=getenv("HOME")) != NULL)
    {
      strcpy(rcfile, word);
      strcat(rcfile, "/.viewmolrc");
    }
    if ((file=fopen(rcfile, "r")) == NULL)
    {
      if ((word=getenv("VIEWMOLPATH")) != NULL)
      {
        strcpy(rcfile, word);
        strcat(rcfile, "/viewmolrc");
      }
      if ((file=fopen(rcfile, "r")) == NULL)
      {
        word=getStringResource(topShell, "unableToOpen");
        sprintf(line, word, "viewmolrc");
        messgb(topShell, 3, line, buttons1, 1);
        exit(-1);
      }
    }
  }

  ne=0;
  osname(os);
  while (fgets(line, MAXLENLINE, file) != NULL)
  {
    nline++;
    error=FALSE;
    if (line[0] != '#')
    {
      if (strstr(line, "debug"))
      {
        word=strtok(line, "=");
        word=strtok(NULL, "=");
        debug=atoi(word);
      }
      else if (strstr(line, "option"))
      {
        if (options == NULL)
          options=(struct OPTION *)getmem(nopt+1, sizeof(struct OPTION));
        else
        {
          oldOptions=options;
          options=(struct OPTION *)expmem((void *)options, nopt+1, sizeof(struct OPTION));
	    if (oldOptions != options)
          {
            /* Move pointer to identifier if expanding memory caused a move of the
               entire options structure */
            for (i=0; i<nopt+1; i++)
		  options[i].identifier+=(char *)options-(char *)oldOptions;
          }
	  }
        strtok(line, " \t");
        strncpy(options[nopt].flag, strtok(NULL, " \t"), 8);
        storeCommand(options[nopt].command, strtok(NULL, " \t"), os);
        while ((word=strtok(NULL, " \t")) != NULL)
        {
          strncat(options[nopt].command, " ", MAXLENLINE-strlen(options[nopt].command)-1);
          strncat(options[nopt].command, word, MAXLENLINE-strlen(options[nopt].command)-1);
        }
        if ((word=strtok(options[nopt].command, "\"")) != NULL)
        {
          options[nopt].identifier=strtok(NULL, "\"");
          (void)strtok(NULL, "\"");
        }
	else
	  options[nopt].identifier=NULL;
        nopt++;
      }
      else if (strstr(line, "output"))
      {
        if (output == NULL)
          output=(struct OPTION *)getmem(noutput+1, sizeof(struct OPTION));
        else
          output=(struct OPTION *)expmem((void *)output, noutput+1, sizeof(struct OPTION));
        strtok(line, " \t");
        strncpy(output[noutput].flag, strtok(NULL, " \t"), 7);
        storeCommand(output[noutput].command, strtok(NULL, " \t"), os);
        while ((word=strtok(NULL, " \t")) != NULL)
        {
          strncat(output[noutput].command, " ", MAXLENLINE-strlen(output[noutput].command)-1);
          strncat(output[noutput].command, word, MAXLENLINE-strlen(output[noutput].command)-1);
        }
        noutput++;
      }
      else
      {
        if ((word=strtok(line, " \t")) != NULL)
        {
          strncpy(elements[ne].symbol, word, 2);
          elements[ne].symbol[0]=toupper(elements[ne].symbol[0]);
          elements[ne].symbol[1]=tolower(elements[ne].symbol[1]);
          if ((word=strtok(NULL, " \t")) != NULL)
          {
            elements[ne].rad=atof(word);
		elements[ne].radScale=1.0;
            if (!(error=getColor(&elements[ne].dark[0])))
              error=getColor(&elements[ne].light[0]);
          }
          else
            error=TRUE;
          elements[ne].ambient[0]=(-1.0);
	  elements[ne].specular[0]=elements[ne].specular[1]=elements[ne].specular[2]=1.0;
          elements[ne].shininess=128.0;
          elements[ne].alpha=1.0;
          while ((word=strtok(NULL, " \t")) != NULL)
          {
            if (strstr(word, "am") != NULL)
              error=getColor(&elements[ne].ambient[0]);
            if (strstr(word, "em") != NULL)
              error=getColor(&elements[ne].emission[0]);
            if (strstr(word, "sp") != NULL)
              error=getColor(&elements[ne].specular[0]);
            if (strstr(word, "sh") != NULL)
            {
              word=strtok(NULL, " \t");
              elements[ne].shininess=(float)atof(word);
            }
            if (strstr(word, "al") != NULL)
            {
              word=strtok(NULL, " \t");
              elements[ne].alpha=(float)atof(word);
            }
          }
	    if (elements[ne].ambient[0] < 0.0)
	    {
              elements[ne].ambient[0]=0.5*(elements[ne].dark[0]+elements[ne].light[0]);
              elements[ne].ambient[1]=0.5*(elements[ne].dark[1]+elements[ne].light[1]);
              elements[ne].ambient[2]=0.5*(elements[ne].dark[2]+elements[ne].light[2]);
	    }
        }
        else
          error=TRUE;
        if (error)
        {
          word=getStringResource(topShell, "errorOnLine");
          sprintf(line, word, nline, rcfile);
          messgb(topShell, 2, line, buttons1, 1);
          exit(-1);
        }
        if (++ne >= mne)
        {
          mne+=10;
          elements=(struct ELEMENT *)expmem((void *)elements, mne, sizeof(struct ELEMENT));
        }
      } /* end of "if (strstr(line ..." */
    } /* end of "if (line[0] ... " */
  } /* end of "while (fgets ..." */
  if (nopt == 0)
  {
    word=getStringResource(topShell, "noInputFilter");
    sprintf(line, word, rcfile);
    messgb(topShell, 3, line, buttons1, 1);
    exit(-1);
  }
  fclose(file);
  (void)setlocale(LC_ALL, locale);
  return(TRUE);
}

void setAtom(struct MOLECULE *mol)
{
  static struct PushButtonRow buttons[] = {{"exit", GetMessageBoxButton, (XtPointer)0, NULL},
                                           {"continue", GetMessageBoxButton, (XtPointer)1, NULL}};
  XVisualInfo *visualInfo;
  Visual *vi;
  Colormap colormap;
  char *word, line[MAXLENLINE];
  char *missing=NULL, test[5];
  register int i, j, k;

  k=1;
  for (i=0; i<mol->na; i++)
  {
    for (j=0; j<ne; j++)
    {
      if (!strncmp(mol->atoms[i].name, elements[j].symbol, 2))
      {
        mol->atoms[i].rad=elements[j].rad;
        mol->atoms[i].radScale=1.0;
        mol->atoms[i].element=&elements[j];
        break;
      }
    }
    if (mol->atoms[i].element == NULL)
    {
      if (missing == NULL)
      {
        k+=4;
        missing=(char *)getmem((size_t)k, sizeof(char));
        strncat(missing, mol->atoms[i].name, 2);
        strncat(missing, ", ", 2);
      }
      else
      {
	strcpy(test, mol->atoms[i].name);
	strcat(test, ", ");
        if (!strstr(missing, test))
	{
          k+=4;
          missing=(char *)expmem((void *)missing, (size_t)k, sizeof(char));
          strncat(missing, mol->atoms[i].name, 2);
          strncat(missing, ", ", 2);
        }
      }
      mol->atoms[i].radScale=1.0;
      mol->atoms[i].mass=0.0;
      mol->atoms[i].nelectrons=0;
      mol->atoms[i].neutronScatterfac=0.0;
      for (j=0; j<ne; j++)
      {
        if (!strncmp(elements[j].symbol, "??", 2))
        {
          mol->atoms[i].element=&elements[j];
          mol->atoms[i].rad=elements[j].rad;;
          break;
        }
      }
    }
    for (j=0; j<LASTELEMENT; j++)
    {
      if (!strncmp(mol->atoms[i].name, pse[j], 2))
      {
        mol->atoms[i].mass=isotope[j][0];
        mol->atoms[i].nelectrons=electrons[j];
        mol->atoms[i].neutronScatterfac=0.0;
        break;
      }
    }
  }
  if (k > 1)
  {
    if (showWarning)
    {
      if (k > 5)
        word=getStringResource(topShell, "unknownElements");
      else
        word=getStringResource(topShell, "unknownElement");
      missing[strlen(missing)-2]='\0';
      sprintf(line, word, missing);
      fremem((void **)&missing);
      if (!messgb(topShell, 3, line, buttons, 2))
        exit(-1);
    }
  }
  if (mol->unitcell)
  {
    for (i=0; i<ne; i++)
      if (!strncmp(elements[i].symbol, "Uc", 2)) break;
    for (j=0; j<mol->unitcell->nc; j++)
      mol->unitcell->corners[j].element=&elements[i];
  }
  setBond(mol);
  if (!rgbMode)
  {
    XtVaGetValues(topShell,
                  XtNvisual, &vi,
                  XmNcolormap, &colormap,
                  NULL);
    visualInfo=getVisualInfo(XtDisplay(topShell), vi);
    loadAtomColors(visualInfo, colormap);
  }
}

void setBond(struct MOLECULE *mol)
{
  register int i;

  for (i=0; i<ne; i++)
  {
    if (!strncmp(elements[i].symbol, "Bd", 2))
    {
      mol->bondRadius=elements[i].rad;
      mol->bondStyle=&elements[i];
    }
  }
}

void getPrimitive(char *line, int *primitive, int deflt)
{
  if (strstr(line, "dot"))
    *primitive=GLU_POINT;
  else if (strstr(line, "line"))
    *primitive=GLU_LINE;
  else if (strstr(line, "surface"))
    *primitive=GLU_FILL;
  else
    *primitive=deflt;
}

int getColor(GLfloat *p)
{
  char *word;

  if ((word=strtok(NULL, " \t")) == NULL) return(TRUE);
  p[0]=(float)atof(word);
  if (p[0] < 0.0) p[0]=0.0;
  if (p[0] > 1.0) p[0]=1.0;
  if ((word=strtok(NULL, " \t")) == NULL) return(TRUE);
  p[1]=(float)atof(word);
  if (p[1] < 0.0) p[1]=0.0;
  if (p[1] > 1.0) p[1]=1.0;
  if ((word=strtok(NULL, " \t")) == NULL) return(TRUE);
  p[2]=(float)atof(word);
  if (p[2] < 0.0) p[2]=0.0;
  if (p[2] > 1.0) p[2]=1.0;
  p[3]=0.0;
  return(FALSE);
}

void unknownResource(char *resource, char *value)
{
  static struct PushButtonRow buttons[] = {{"exit", GetMessageBoxButton, (XtPointer)0, NULL},
                                           {"continue", GetMessageBoxButton, (XtPointer)1, NULL}};
  char *word, line[MAXLENLINE];

  word=getStringResource(topShell, "unknownResource");
  sprintf(line, word, value, resource);
  if (!messgb(topShell, 3, line, buttons, 2)) exit(-1);
}

void storeCommand(char *target, char *value, char *replacement)
{
  char *p=value, *t=target;

  while (*p)
  {
    if (*p != '$')
      *t++=*p++;
    else
    {
      if (!strncmp(p, "$OSNAME", 7))
      {
        strcpy(t, replacement);
        p+=7;
        t+=strlen(replacement);
      }
      else if (!strncmp(p, "${OSNAME}", 9))
      {
        strcpy(t, replacement);
        p+=9;
        t+=strlen(replacement);
      }
      else
        *t++=*p++;
    }
  }
  *t='\0';
}

void checkProgram(char *program, char *word)
{
  char line[MAXLENLINE];

  strncpy(program, word, MAXLENLINE);
  if ((word=strtok(program, " \t")))
  {
    strcpy(line, strchr(program, '\0')+1);
    if (checkFile(&word))
    {
      strncpy(program, word, MAXLENLINE);
      strcat(program, " ");
      strcat(program, line);
      return;
    }
  }
  program[0]='\0';
}

void getFormats(void)
{
/* Get format strings for bond lengths, bond angles, and torsion angles */

  formBondLength=getStringResource(topShell, "bondLength");
  if (strstr(formBondLength, "pm")) bndfac=100.0;
  if (strstr(formBondLength, "bohr") || strstr(formBondLength, "au")) bndfac=1.0/0.52917706;
  formBondAngle=getStringResource(topShell, "bondAngle");
  formTorsionAngle=getStringResource(topShell, "torsionAngle");
}
