/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                           T H E R M O F O R M . C                            *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: thermoform.c,v 1.2 2003/11/07 11:16:54 jrh Exp $
* $Log: thermoform.c,v $
* Revision 1.2  2003/11/07 11:16:54  jrh
* Release 2.4
*
* Revision 1.1  2000/12/10 15:17:42  jrh
* Initial revision
*
*/
#include<ctype.h>
#include<math.h>
#include<X11/Intrinsic.h>
#include<Xm/Xm.h>
#include<Xm/BulletinB.h>
#include<Xm/Form.h>
#include<Xm/Frame.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 "TabBook.h"
#include "viewmol.h"
#include "dialog.h"

void thermoDialog(Widget, caddr_t, XmAnyCallbackStruct *);
void thermoExit(Widget, caddr_t, XmPushButtonCallbackStruct *);
void GetThermo(Widget, caddr_t, XmToggleButtonCallbackStruct *);
void calculateThermodynamics(int);
void GetUnits(Widget, caddr_t, XmToggleButtonCallbackStruct *);
void environmentChanged(Widget, struct SLIDER *, XmScrollBarCallbackStruct *);
void GetReaction(Widget, caddr_t, XmToggleButtonCallbackStruct *);
void findReaction(Widget, caddr_t, XmPushButtonCallbackStruct *);
void makeReaction(char *, double *);
void setUnits(int, double *);
double getEnergyConversionFactor(int);
XmString makeIndexedString(char *);
void setReaction(Widget, caddr_t, XmPushButtonCallbackStruct *);
void selectScreen(int);

extern double getSymmetryNumber(struct MOLECULE *);
extern void MapBox(Widget, caddr_t, XmToggleButtonCallbackStruct *);
extern Widget CreatePushButtonRow(Widget, struct PushButtonRow *, int);
extern void redraw(int);
extern void setMenuItem(int, int);
extern Widget initShell(Widget, char *, Widget *, Widget *);
extern void GetSlider(Widget, struct SLIDER *, XmScrollBarCallbackStruct *);
extern Widget makeMenu(Widget, int, char *, struct MenuItem *);
extern char *getStringResource(Widget, char *);
extern double getMass(struct MOLECULE *);
extern double getDensity(struct MOLECULE *);
extern void getSumFormula(struct MOLECULE *, char *, int);
extern void selectMolecule(Widget, caddr_t, XmToggleButtonCallbackStruct *);
extern Widget CreateToggleBox(Widget, struct PushButtonRow *, int, int, int, int, int);
extern int reaction(int, int *);
extern void thermo(struct MOLECULE *);
extern void *getmem(size_t, size_t);
extern void fremem(void **);

extern struct WINDOW windows[];
extern struct MOLECULE *molecules;
extern Widget topShell;
extern double temp, pressure;
extern int nmolecule, ireaction;
extern int thermoUnits;

static Widget dialog, tabs, board, *header, *table, equation, eWidget[7];
static double temp_save, reactionData[7];
static int thermoSettings_save, thermoUnits_save, nreactions;

static struct
{
  char *label;
  double value;
} thermodata[] = {
  { "Htrans", 0.0},
  { "Strans", 0.0},
  { "Gtrans", 0.0},
  { "Ctrans", 0.0},
  { "Hpv",    0.0},
  { "",       0.0},
  { "Gpv",    0.0},
  { "",       0.0},
  { "Hrot",   0.0},
  { "Srot",   0.0},
  { "Grot",   0.0},
  { "Crot",   0.0},
  { "Hvib",   0.0},
  { "Svib",   0.0},
  { "Gvib",   0.0},
  { "Cvib",   0.0},
  { "Htotal", 0.0},
  { "Stotal", 0.0},
  { "Gtotal", 0.0},
  { "Ctotal", 0.0}
};
static struct PushButtonRow reactionButtons[] = {
  { "previous", setReaction, (XtPointer)(-1), NULL },
  { "next", setReaction, (XtPointer)1, NULL },
};

void thermoDialog(Widget widget, caddr_t dummy, XmAnyCallbackStruct *data)
{
  Widget form, form1, form2, form3, form4, form5, form6;
  Widget title, massLabel, symmLabel, reactant, rb, reactionTab;
  Widget menu, slider1, slider2, sep;
  struct MOLECULE *mol;
  XmString str;
  static struct PushButtonRow buttons[] = {
    { "ok", thermoExit, (XtPointer)TRUE, NULL },
    { "cancel", thermoExit, (XtPointer)FALSE, NULL },
  };
  static struct PushButtonRow checkbox_buttons[] = {
    { "translation", GetThermo, (XtPointer)TRANSLATION, NULL },
    { "pv", GetThermo, (XtPointer)PV, NULL },
    { "rotation", GetThermo, (XtPointer)ROTATION, NULL },
    { "vibration", GetThermo, (XtPointer)VIBRATION, NULL },
    { "total", NULL, (XtPointer)0, NULL },
  };
  static struct MenuItem unitsMenu[] = {
    { "joules", &xmPushButtonGadgetClass, GetUnits, (XtPointer)JOULES, NULL, NULL },
    { "calories", &xmPushButtonGadgetClass, GetUnits, (XtPointer)CALORIES, NULL, NULL },
    { "thermocalories", &xmPushButtonGadgetClass, GetUnits, (XtPointer)THERMOCALORIES, NULL, NULL },
    { NULL, NULL, NULL, NULL, NULL, NULL }
  };
  static struct MenuItem reactantMenu[] = {                           
    { "reactant", &xmPushButtonGadgetClass, GetReaction, (XtPointer)REACTANT, NULL, NULL },                    
    { "notInvolved", &xmPushButtonGadgetClass, GetReaction, (XtPointer)0, NULL, NULL },         
    { "product", &xmPushButtonGadgetClass, GetReaction, (XtPointer)PRODUCT, NULL, NULL },                 
    { "allReactions", &xmPushButtonGadgetClass, GetReaction, (XtPointer)ALLREACTIONS, NULL, NULL },
    { NULL, NULL, NULL, NULL, NULL, NULL }
  };
  static struct SLIDER tempSlider, pressureSlider;
  short decimals;
  char line[MAXLENLINE+40], formula[40], eq[2*MAXLENLINE];
  char *format;
  register int i, j, k, l;

  /* This function creates the dialog for thermodynamics */

  setMenuItem(VIEWER_THERMODYNAMICS, False);
  thermoSettings_save=molecules[0].thermoSettings;
  thermoUnits_save=thermoUnits;
  temp_save=temp;

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

  tabs=XtVaCreateManagedWidget("tabs", xcgTabBookWidgetClass, form,
                               NULL);

  header=(Widget *)getmem((size_t)(24*nmolecule), sizeof(Widget));
  table=header+4*nmolecule;
  l=0;
  for (i=0; i<nmolecule; i++)
  {
    mol=&molecules[i];
    getSumFormula(mol, formula, TRUE);
    str=makeIndexedString(formula);
    (void)XtVaCreateManagedWidget(formula, xmPushButtonWidgetClass, tabs,
                                  XmNlabelString, str,
                                  NULL);
    XmStringFree(str);
    sprintf(line, "page%d", i);
    form1=XtVaCreateWidget(line, xmRowColumnWidgetClass, tabs,
                           XmNorientation, XmVERTICAL,
                           NULL);

    form2=XtVaCreateWidget("rowcolumn", xmFormWidgetClass, form1,
                           NULL);

    str=XmStringCreate(mol->title, "DEFAULT");
    title=XtVaCreateManagedWidget("moleculeTitleValue", xmLabelWidgetClass, form2,
                                  XmNlabelString, str,
                                  XmNtopAttachment, XmATTACH_FORM,
                                  XmNleftAttachment, XmATTACH_FORM,
                                  XmNrightAttachment, XmATTACH_FORM,
                                  NULL); 
    XmStringFree(str);

    reactant=makeMenu(form2, XmMENU_OPTION, "reactantMenu", reactantMenu);
    if (mol->reaction < 0)
      j=0;
    else if (mol->reaction == 0)
      j=1;
    else
      j=2;
    XtVaSetValues(reactant, XmNmenuHistory, reactantMenu[j].widget,
                            XmNtopAttachment, XmATTACH_WIDGET,
                            XmNtopWidget, title,
                            XmNrightAttachment, XmATTACH_FORM,
                            NULL);
    XtManageChild(reactant);

    format=getStringResource(board, "moleculeMass");
    sprintf(line, format, getMass(mol));
    str=XmStringCreate(line, "DEFAULT");
    massLabel=XtVaCreateManagedWidget("moleculeMass", xmLabelWidgetClass, form2,
                                      XmNlabelString, str,
                                      XmNtopAttachment, XmATTACH_WIDGET,
                                      XmNtopWidget, title,
                                      XmNleftAttachment, XmATTACH_FORM,
                                      NULL);
    XmStringFree(str);

    format=getStringResource(board, "symmetryNumber");
    sprintf(line, format, (int)(getSymmetryNumber(mol)));
    str=XmStringCreate(line, "DEFAULT");
    symmLabel=XtVaCreateManagedWidget("symmetryNumber", xmLabelWidgetClass, form2,
                                      XmNlabelString, str,
                                      XmNtopAttachment, XmATTACH_WIDGET,
                                      XmNtopWidget, massLabel,
                                      XmNleftAttachment, XmATTACH_FORM,
                                      NULL); 

    XmStringFree(str);

    if (mol->unitcell)
    {
      format=getStringResource(board, "solidDensity");
      sprintf(line, format, getDensity(mol));
      str=XmStringCreate(line, "DEFAULT");
      (void)XtVaCreateManagedWidget("density", xmLabelWidgetClass, form2,
                                    XmNlabelString, str,
                                    XmNtopAttachment, XmATTACH_WIDGET,
                                    XmNtopWidget, symmLabel,
                                      XmNleftAttachment, XmATTACH_FORM,
                                    NULL);
      XmStringFree(str);
    }
    else
    {
      format=getStringResource(board, "rotationalConstants");
      sprintf(line, format, mol->rotConstants[0], mol->rotConstants[1], mol->rotConstants[2]);
      str=XmStringCreate(line, "DEFAULT");
      (void)XtVaCreateManagedWidget("rotConstants", xmLabelWidgetClass, form2,
                                    XmNlabelString, str,
                                    XmNtopAttachment, XmATTACH_WIDGET,
                                    XmNtopWidget, symmLabel,
                                    XmNleftAttachment, XmATTACH_FORM,
                                    NULL);
      XmStringFree(str);
    }

    form3=XtVaCreateWidget("rowcolumn", xmRowColumnWidgetClass, form1,
                           XmNorientation, XmHORIZONTAL,
                           XmNpacking, XmPACK_COLUMN,
                           XmNnumColumns, (short)6,
                           NULL);

    (void)XtVaCreateManagedWidget("", xmLabelWidgetClass, form3, NULL);
    header[l++]=XtVaCreateManagedWidget("enthalphy", xmLabelWidgetClass, form3, NULL);
    header[l++]=XtVaCreateManagedWidget("entropy", xmLabelWidgetClass, form3, NULL);
    header[l++]=XtVaCreateManagedWidget("gibbsEnergy", xmLabelWidgetClass, form3, NULL);
    header[l++]=XtVaCreateManagedWidget("heatCapacity", xmLabelWidgetClass, form3, NULL);

    for (j=0; j<XtNumber(checkbox_buttons)-1; j++)
    {
      checkbox_buttons[j].widget=XtVaCreateManagedWidget(checkbox_buttons[j].label,
                                 xmToggleButtonWidgetClass, form3, NULL);
      XtAddCallback(checkbox_buttons[j].widget, XmNvalueChangedCallback,
                    (XtCallbackProc)checkbox_buttons[j].callback,
                    (XtPointer)checkbox_buttons[j].client_data);
      if (mol->thermoSettings & (int)checkbox_buttons[j].client_data)
        XtVaSetValues(checkbox_buttons[j].widget, XmNset, True, NULL);

      for (k=0; k<4; k++)
      {
        table[20*i+4*j+k]=XtVaCreateManagedWidget(thermodata[4*j+k].label,
                                                  xmLabelWidgetClass, form3,
                                                  NULL);
      }
      if (!(mol->thermoSettings & (int)checkbox_buttons[j].client_data))
      {
        for (k=0; k<4; k++)
          XtVaSetValues(table[20*i+4*j+k], XmNsensitive, False, NULL);
      }
    }
    checkbox_buttons[4].widget=XtVaCreateManagedWidget(checkbox_buttons[4].label,
                                                xmLabelWidgetClass, form3, NULL);
    for (k=0; k<4; k++)
    {
      table[20*i+16+k]=XtVaCreateManagedWidget(thermodata[16+k].label,
                                               xmLabelWidgetClass, form3,
                                               NULL);
    }
    calculateThermodynamics(i);
    XtManageChild(form3);
    XtManageChild(form2);
    XtManageChild(form1);
  }

/* Build reaction page */

  reactionTab=XtVaCreateManagedWidget("reactions", xmPushButtonWidgetClass, tabs,
                                      NULL);
  XtAddCallback(reactionTab, XmNactivateCallback, (XtCallbackProc)findReaction, NULL);
  form4=XtVaCreateWidget("reactionpage", xmFormWidgetClass, tabs,
				 XmNfractionBase, 6,
                         NULL);
  equation=XtVaCreateManagedWidget("equation", xmLabelWidgetClass, form4,
                                   XmNleftAttachment, XmATTACH_FORM,
                                   XmNrightAttachment, XmATTACH_FORM,
                                   XmNtopAttachment, XmATTACH_FORM,
                                   NULL);
  form5=XtVaCreateWidget("rowcolumn", xmRowColumnWidgetClass, form4,
                         XmNorientation, XmVERTICAL,
                         XmNpacking, XmPACK_COLUMN,
                         XmNnumColumns, (short)2,
                         XmNleftAttachment, XmATTACH_FORM,
                         XmNrightAttachment, XmATTACH_FORM,
                         XmNtopAttachment, XmATTACH_WIDGET,
                         XmNtopWidget, equation,
                         NULL);

  (void)XtVaCreateManagedWidget("electronicEnergy", xmLabelWidgetClass, form5, NULL);
  (void)XtVaCreateManagedWidget("statisticalEnergy", xmLabelWidgetClass, form5, NULL);
  (void)XtVaCreateManagedWidget("reactionEnergy", xmLabelWidgetClass, form5, NULL);
  (void)XtVaCreateManagedWidget("reactionEntropy", xmLabelWidgetClass, form5, NULL);
  (void)XtVaCreateManagedWidget("reactionGibbsEnergy", xmLabelWidgetClass, form5, NULL);
  (void)XtVaCreateManagedWidget("reactionHeatCapacity", xmLabelWidgetClass, form5, NULL);
  (void)XtVaCreateManagedWidget("equilibriumConstant", xmLabelWidgetClass, form5, NULL);
  for (i=0; i<7; i++)
  {
    sprintf(line, "E%d", i);
    eWidget[i]=XtVaCreateManagedWidget(line, xmLabelWidgetClass, form5,
                                       NULL);
  }
  XtManageChild(form5);
  rb=CreatePushButtonRow(form4, reactionButtons, XtNumber(reactionButtons));
  XtVaSetValues(rb, XmNtopAttachment, XmATTACH_POSITION,
                    XmNtopPosition, 5,
			  XmNleftAttachment, XmATTACH_FORM,
			  XmNrightAttachment, XmATTACH_FORM,
			  NULL);
  makeReaction(eq, reactionData);
  str=makeIndexedString(eq);
  XtVaSetValues(equation, XmNlabelString, str, NULL);
  XmStringFree(str);
  setUnits(thermoUnits, reactionData);
  XtManageChild(form4);

/* Fixed part of form with unit selector and temperature and pressure sliders */

  form6=XtVaCreateWidget("controlarea", xmFormWidgetClass, form,
                         XmNorientation, XmHORIZONTAL,
                         NULL);
  (void)XtVaCreateManagedWidget("temperature", xmLabelWidgetClass, form6,
                                XmNtopAttachment, XmATTACH_FORM,
                                XmNleftAttachment, XmATTACH_FORM,
                                NULL);

  menu=makeMenu(form6, XmMENU_OPTION, "units", unitsMenu);
  XtVaSetValues(menu, XmNmenuHistory, unitsMenu[thermoUnits].widget,
                XmNtopAttachment, XmATTACH_FORM,
                XmNrightAttachment, XmATTACH_FORM,
                NULL);
  XtManageChild(menu);

  (void)XtVaCreateManagedWidget("unitlabel", xmLabelWidgetClass, form6,
                                 XmNtopAttachment, XmATTACH_FORM,
                                 XmNrightAttachment, XmATTACH_WIDGET,
                                 XmNrightWidget, menu,
                                 NULL);
  XtManageChild(form6);

  slider1=XtVaCreateManagedWidget("temperatureSlider", xmScaleWidgetClass, form,
                                  XmNorientation, XmHORIZONTAL,
                                  XmNvalue, (int)(temp),
                                  XmNminimum, 0,
                                  XmNmaximum, 1000,
                                  XmNshowValue, True,
                                  XmNsensitive, True,
                                  NULL);
  tempSlider.number=&temp;
  tempSlider.decimals=0;
  tempSlider.draw=NULL;
  XtAddCallback(slider1, XmNvalueChangedCallback, (XtCallbackProc)environmentChanged, &tempSlider);

  (void)XtVaCreateManagedWidget("pressure", xmLabelWidgetClass, form, NULL);
  slider2=XtVaCreateManagedWidget("pressureSlider", xmScaleWidgetClass, form,
                                  XmNorientation, XmHORIZONTAL,
                                  XmNshowValue, True,
                                  XmNsensitive, True,
                                  NULL);
  XtVaGetValues(slider2, XmNdecimalPoints, &decimals, NULL);
  XtVaSetValues(slider2, XmNvalue, (int)(pressure*pow((double)10.0,
                (double)decimals)), NULL);
  pressureSlider.number=&pressure;
  pressureSlider.decimals=decimals;
  pressureSlider.draw=NULL;
  XtAddCallback(slider2, XmNvalueChangedCallback,
                (XtCallbackProc)environmentChanged, &pressureSlider);

  sep=XtVaCreateManagedWidget("sep", xmSeparatorWidgetClass, form,
                              XmNtraversalOn, False,
                              NULL);

  CreatePushButtonRow(form, buttons, XtNumber(buttons));
  XtAddCallback(dialog, XmNpopupCallback, (XtCallbackProc)MapBox, (XmAnyCallbackStruct *)NULL);
  XtManageChild(form);
  XtManageChild(board);
}

void thermoExit(Widget button, caddr_t which, XmPushButtonCallbackStruct *data)
{
  if (!(int)which)
  {
    molecules[XcgTabBookGetActivePage(tabs)-1].thermoSettings=thermoSettings_save;
    thermoUnits=thermoUnits_save;
    temp=temp_save;
  }
  XtDestroyWidget(dialog);
  fremem((void **)&header);
  setMenuItem(VIEWER_THERMODYNAMICS, True);
}

void GetThermo(Widget button, caddr_t which, XmToggleButtonCallbackStruct *data)
{
  double q;
  int w=(int)which;
  register int i, j, k;

  q=log((double)w)/log((double)2.0);
  i=(int)rint(q);
  j=XcgTabBookGetActivePage(tabs)-1;
  if (data->set)
  {
    molecules[j].thermoSettings |= w;
    for (k=0; k<4; k++)
      XtVaSetValues(table[20*j+4*i+k], XmNsensitive, True, NULL);
  }
  else
  {
    molecules[j].thermoSettings &= ~w;
    for (k=0; k<4; k++)
      XtVaSetValues(table[20*j+4*i+k], XmNsensitive, False, NULL);
  }
  calculateThermodynamics(XcgTabBookGetActivePage(tabs)-1);
}

void calculateThermodynamics(int which)
{
  struct MOLECULE *mol;
  XmString str;
  double convFactor;
  char value[MAXLENLINE], *format;
  register int i;

  convFactor=getEnergyConversionFactor(thermoUnits);

  mol=&molecules[which];
  thermo(mol);

  thermodata[0].value=0.001*convFactor*mol->htrl;
  thermodata[1].value=convFactor*mol->strl;
  thermodata[2].value=0.001*convFactor*(mol->htrl-temp*mol->strl);
  thermodata[3].value=convFactor*mol->ctrl;

  thermodata[4].value=0.001*convFactor*mol->pv;
  thermodata[5].value=0.0;
  thermodata[6].value=0.001*convFactor*mol->pv;
  thermodata[7].value=0.0;

  thermodata[8].value=0.001*convFactor*mol->hrot;
  thermodata[9].value=convFactor*mol->srot;
  thermodata[10].value=0.001*convFactor*(mol->hrot-temp*mol->srot);
  thermodata[11].value=convFactor*mol->crot;

  thermodata[12].value=0.001*convFactor*mol->hvib;
  thermodata[13].value=convFactor*mol->svib;
  thermodata[14].value=0.001*convFactor*(mol->hvib-temp*mol->svib);
  thermodata[15].value=convFactor*mol->cvib;

  format=getStringResource(dialog, "format");
  for (i=0; i<16; i++)
  {
    if (i != 5 && i != 7)
      sprintf(value, format, thermodata[i].value);
    else
      sprintf(value, " ");
    str=XmStringCreate(value, "DEFAULT");
    XtVaSetValues(table[20*which+i], XmNlabelString, str, NULL);
    XmStringFree(str);
  }

  thermodata[16].value=0.0;
  if (mol->thermoSettings & TRANSLATION)
    thermodata[16].value+=mol->htrl;
  if (mol->thermoSettings & PV)
    thermodata[16].value+=mol->pv;
  if (mol->thermoSettings & ROTATION)
    thermodata[16].value+=mol->hrot;
  if (mol->thermoSettings & VIBRATION)
    thermodata[16].value+=mol->hvib;
  thermodata[16].value*=0.001*convFactor;
  thermodata[17].value=0.0;
  if (mol->thermoSettings & TRANSLATION)
    thermodata[17].value+=mol->strl;
  if (mol->thermoSettings & ROTATION)
    thermodata[17].value+=mol->srot;
  if (mol->thermoSettings & VIBRATION)
    thermodata[17].value+=mol->svib;
  thermodata[17].value*=convFactor;
  thermodata[18].value=0.0;
  if (mol->thermoSettings & TRANSLATION)
    thermodata[18].value+=thermodata[2].value;
  if (mol->thermoSettings & PV)
    thermodata[18].value+=thermodata[6].value;
  if (mol->thermoSettings & ROTATION)
    thermodata[18].value+=thermodata[10].value;
  if (mol->thermoSettings & VIBRATION)
    thermodata[18].value+=thermodata[14].value;
  thermodata[19].value=0.0;
  if (mol->thermoSettings & TRANSLATION)
    thermodata[19].value+=mol->ctrl;
  if (mol->thermoSettings & ROTATION)
    thermodata[19].value+=mol->crot;
  if (mol->thermoSettings & VIBRATION)
    thermodata[19].value+=mol->cvib;
  thermodata[19].value*=convFactor;

  for (i=0; i<4; i++)
  {
    sprintf(value, format, thermodata[16+i].value);
    str=XmStringCreate(value, "DEFAULT");
    XtVaSetValues(table[20*which+16+i], XmNlabelString, str, NULL);
    XmStringFree(str);
  }
}

void GetUnits(Widget button, caddr_t which, XmToggleButtonCallbackStruct *data)
{
  register int i;

  thermoUnits=(int)which;

  for (i=0; i<nmolecule; i++)
    calculateThermodynamics(i);
  setUnits(thermoUnits, reactionData);
}

void environmentChanged(Widget widget, struct SLIDER *slider, XmScrollBarCallbackStruct *data)
{
  char eq[MAXLENLINE];
  register int i;

  GetSlider(widget, slider, data);
  for (i=0; i<nmolecule; i++)
    calculateThermodynamics(i);
  makeReaction(eq, reactionData);
  setUnits(thermoUnits, reactionData);
}

void GetReaction(Widget widget, caddr_t which, XmToggleButtonCallbackStruct *data)
{
  molecules[XcgTabBookGetActivePage(tabs)-1].reaction=(int)which;
}

void findReaction(Widget widget, caddr_t dummy, XmPushButtonCallbackStruct *data)
{
  XmString str;
  char eq[2*MAXLENLINE];

  makeReaction(eq, reactionData);
  str=makeIndexedString(eq);
  XtVaSetValues(equation, XmNlabelString, str, NULL);
  XmStringFree(str);
  setUnits(thermoUnits, reactionData);
}

void makeReaction(char *eq, double *reactionData)
{
  double R=6.022169e23*1.380622e-23;
  int ret;
  char line[MAXLENLINE+40], formula[40];
  char leftSide[MAXLENLINE], rightSide[MAXLENLINE];
  register int i;

  leftSide[0]='\0';
  rightSide[0]='\0';
  ret=reaction(ireaction, &nreactions);
  reactionData[0]=reactionData[1]=reactionData[2]=0.0;
  reactionData[3]=reactionData[4]=reactionData[5]=0.0;
  reactionData[6]=0.0;
  for (i=0; i<nmolecule; i++)
  {
    if (molecules[i].stoichioNumber < 0)
    {
      getSumFormula(&molecules[i], formula, TRUE);
      if (*leftSide) strcat(leftSide, " + ");
      if (molecules[i].stoichioNumber == (-1))
        strcat(leftSide, formula);
      else
      {
        sprintf(line, "%d %s", (-molecules[i].stoichioNumber), formula);
        strcat(leftSide, line);
      }
	if (ret == REACTION_OK)
	{
        if (molecules[i].optimization)
          reactionData[0]+=molecules[i].stoichioNumber
                         *molecules[i].optimization[molecules[i].nhist-1].energy
                         *2625.5001;
        reactionData[1]+=0.001*molecules[i].stoichioNumber*(molecules[i].htrl
                       +molecules[i].pv+molecules[i].hrot+molecules[i].hvib);
        reactionData[3]+=molecules[i].stoichioNumber*(molecules[i].strl
                       +molecules[i].srot+molecules[i].svib);
        reactionData[5]+=molecules[i].stoichioNumber*(molecules[i].ctrl
                       +molecules[i].crot+molecules[i].cvib);
      }
    }
    else if (molecules[i].stoichioNumber > 0)
    {
      getSumFormula(&molecules[i], formula, TRUE);
      if (*rightSide) strcat(rightSide, " + ");
      if (molecules[i].stoichioNumber == 1)
        strcat(rightSide, formula);
      else
      {
        sprintf(line, "%d %s", molecules[i].stoichioNumber, formula);
        strcat(rightSide, line);
      }
	if (ret == REACTION_OK)
	{
        if (molecules[i].optimization)
          reactionData[0]+=molecules[i].stoichioNumber
                         *molecules[i].optimization[molecules[i].nhist-1].energy
                         *2625.5001;
        reactionData[1]+=0.001*molecules[i].stoichioNumber*(molecules[i].htrl
                       +molecules[i].pv+molecules[i].hrot+molecules[i].hvib);
        reactionData[3]+=molecules[i].stoichioNumber*(molecules[i].strl
                       +molecules[i].srot+molecules[i].svib);
        reactionData[5]+=molecules[i].stoichioNumber*(molecules[i].ctrl
                       +molecules[i].crot+molecules[i].cvib);
      }
    }
  }
  if (!(*leftSide && *rightSide))
    strcpy(eq, getStringResource(board, "noReaction"));
  else if (ret == INCONSISTENT_TYPE)
    strcpy(eq, getStringResource(board, "inconsistentType"));
  else
  {
    strcpy(eq, leftSide);
    strcat(eq, " \\rightarrow ");
    strcat(eq, rightSide);
    reactionData[2]=reactionData[0]+reactionData[1];
    reactionData[4]=reactionData[2]-temp*0.001*reactionData[3];
    reactionData[6]=(-1000.0*reactionData[4])/(R*temp*log(10.0));
    switch (ret)
    {
      case MISSING_ATOMS:     strcat(eq, " ");
                              strcat(eq, getStringResource(board, "missingAtoms"));
                              break;
      case CANT_BALANCE:      strcat(eq, " ");
                              strcat(eq, getStringResource(board, "cantBalance"));
                              break;
      default:                break;
    }
  }
  if (nreactions <= 1)
  {
    XtUnmanageChild(reactionButtons[0].widget);
    XtUnmanageChild(reactionButtons[1].widget);
  }
  else
  {
    XtManageChild(reactionButtons[0].widget);
    XtManageChild(reactionButtons[1].widget);
    if (ireaction == 0)
      XtVaSetValues(reactionButtons[0].widget,XmNsensitive, False, NULL);
    else
      XtVaSetValues(reactionButtons[0].widget,XmNsensitive, True, NULL);
    if (ireaction == nreactions-1)
      XtVaSetValues(reactionButtons[1].widget,XmNsensitive, False, NULL);
    else
      XtVaSetValues(reactionButtons[1].widget,XmNsensitive, True, NULL);
  }
}

void setUnits(int thermoUnits, double reactionData[])
{
  XmString h, s, g, cv;
  double convFactor;
  char *unit, *word, line[MAXLENLINE];
  register int i, j;

  convFactor=getEnergyConversionFactor(thermoUnits);
  switch (thermoUnits)
  {
    case JOULES:         unit=getStringResource(dialog, "joules");
                         break;
    case CALORIES:
    case THERMOCALORIES: unit=getStringResource(dialog, "calories");
                         break;
    default:             return;
  }
  word=getStringResource(header[0], "labelString");
  sprintf(line, word, unit);
  h=XmStringCreate(line, "DEFAULT");
  word=getStringResource(header[1], "labelString");
  sprintf(line, word, unit);
  s=XmStringCreate(line, "DEFAULT");
  word=getStringResource(header[2], "labelString");
  sprintf(line, word, unit);
  g=XmStringCreate(line, "DEFAULT");
  word=getStringResource(header[3], "labelString");
  sprintf(line, word, unit);
  cv=makeIndexedString(line);
  j=0;
  for (i=0; i<nmolecule; i++)
  {
    XtVaSetValues(header[j++], XmNlabelString, h,  NULL);
    XtVaSetValues(header[j++], XmNlabelString, s,  NULL);
    XtVaSetValues(header[j++], XmNlabelString, g,  NULL);
    XtVaSetValues(header[j++], XmNlabelString, cv, NULL);
  }
  XmStringFree(h);
  XmStringFree(s);
  XmStringFree(g);
  XmStringFree(cv);

  for (i=0; i<7; i++)
  {
    switch (i)
    {
      case 0:
      case 1:
      case 2:
      case 4: word=getStringResource(dialog, "kiloperMole");
              break;
      case 3:
      case 5: word=getStringResource(dialog, "perMoleandK");
              break;
      case 6: word=getStringResource(dialog, "noUnit");
              break;
    }
    if (i != 6)
      sprintf(line, word, convFactor*reactionData[i], unit);
    else
      sprintf(line, word, reactionData[i]);
    s=XmStringCreate(line, "DEFAULT");
    XtVaSetValues(eWidget[i], XmNlabelString, s, NULL);
    XmStringFree(s);
  }
}

double getEnergyConversionFactor(int thermoUnits)
{
  switch (thermoUnits)
  {
    case JOULES:         return(1.0);
    case CALORIES:       return(1.0/4.1868);
    case THERMOCALORIES: return(1.0/4.184);
  }
  return(0.0);
}

XmString makeIndexedString(char *s)
{
  XmString str1, str2;
  char *begin, *end, line[MAXLENLINE];

  str1=XmStringCreate("", "DEFAULT");
  end=s;
  while (*end)
  {
    begin=end;
    while (*end && *end != '_' && *end != '\\' && *end != '^') end++;
    strncpy(line, begin, end-begin);
    line[end-begin]='\0';
    str2=XmStringCreate(line, "DEFAULT");
    str1=XmStringConcat(str1, str2);
    XmStringFree(str2);
    switch (*end)
    {
      case '^':
      case '_':  end++;
                 if (*end == '{')
                 {
                   begin=end+1; 
                   while (*end && *end != '}') end++;
                   strncpy(line, begin, end-begin);
                   line[end-begin]='\0';
                 }
                 else
                 {
                   line[0]=*end;
                     line[1]='\0';
                 }
                 end++;
                 str2=XmStringCreate(line, "INDEX");
                 str1=XmStringConcat(str1, str2);
                 XmStringFree(str2);
                 break;
      case '\\': end++;
                 if (*end != '\\')
                 {
                   if (!strncmp(end, "rightarrow", 10))
                   {
                     line[0]='®';
                     line[1]='\0';
                     end+=10;
                   }
                 }
                 str2=XmStringCreate(line, "SYMBOL");
                 str1=XmStringConcat(str1, str2);
                 XmStringFree(str2);
                 break;
    }
  }
  return(str1);
}

void setReaction(Widget w, caddr_t what, XmPushButtonCallbackStruct *data)
{
  XmString str;
  char eq[2*MAXLENLINE];

  ireaction+=(int)what;
  if (ireaction < 0) ireaction=0;
  if (ireaction >= nreactions) ireaction=nreactions-1;

  if (ireaction == 0)
    XtVaSetValues(reactionButtons[0].widget, XmNsensitive, False, NULL);
  else
    XtVaSetValues(reactionButtons[0].widget, XmNsensitive, True, NULL);
  if (ireaction == nreactions-1)
    XtVaSetValues(reactionButtons[1].widget, XmNsensitive, False, NULL);
  else
    XtVaSetValues(reactionButtons[1].widget, XmNsensitive, True, NULL);
  makeReaction(eq, reactionData);
  str=makeIndexedString(eq);
  XtVaSetValues(equation, XmNlabelString, str, NULL);
  XmStringFree(str);
  setUnits(thermoUnits, reactionData);
}

void selectScreen(int which)
{
  if (which > 0)
    XcgTabBookSetActivePage(tabs, which, XcgTabBook_OPT_NO_CB);
  else
    thermoExit((Widget)0, (caddr_t)FALSE, (XmPushButtonCallbackStruct *)0);
}
