/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                          B A L A N C E F O R M . C                           *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: balanceform.c,v 1.2 2003/11/07 10:57:27 jrh Exp $
* $Log: balanceform.c,v $
* Revision 1.2  2003/11/07 10:57:27  jrh
* Release 2.4
*
* Revision 1.1  2000/12/10 15:01:29  jrh
* Initial revision
*
*/
#include<stdlib.h>
#include<X11/Intrinsic.h>
#include<Xm/Xm.h>
#include<Xm/BulletinB.h>
#include<Xm/Form.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/Separator.h>
#include<Xm/Text.h>
#include "viewmol.h"
#include "dialog.h"

void balanceExit(Widget, caddr_t, XmPushButtonCallbackStruct *);
void GetInt(Widget, caddr_t, XmPushButtonCallbackStruct *);
int checkReaction(void);

extern void MapBox(Widget, caddr_t, XmToggleButtonCallbackStruct *);
extern Widget CreatePushButtonRow(Widget, struct PushButtonRow *, int);
extern Widget initShell(Widget, char *, Widget *, Widget *);
extern void getSumFormula(struct MOLECULE *, char *, int);
extern XmString makeIndexedString(char *);
extern void *getmem(size_t, size_t);
extern void *expmem(void *, size_t, size_t);
extern void fremem(void **);
extern void ringBell(void);

extern XtAppContext app;
extern struct WINDOW windows[];
extern struct MOLECULE *molecules;
extern Widget topShell;
extern int nmolecule, ne;
static Widget dialog, *stoichioNumber;
static int *balancedReactions=NULL, nmolecule_saved=0, cont;
static char **balancedReactants=NULL, *reactants=NULL;

void balanceDialog(Widget widget, caddr_t dummy, XmAnyCallbackStruct *data)
{
  XEvent event;
  Widget form, board, form1, label=NULL, sep;
  static struct PushButtonRow buttons[] = {
    { "ok", balanceExit, (XtPointer)TRUE, NULL },
    { "cancel", balanceExit, (XtPointer)FALSE, NULL },
  };
  XmString name;
  size_t mnc;
  int found;
  char formula[MAXLENLINE], last[MAXLENLINE], *p;
  register int i, j, k;

  /* This function creates the dialog for manually balancing reactions */

  if (balancedReactions)
  {
    mnc=0;
    k=0;
    for (i=0; i<nmolecule; i++)
    {
      getSumFormula(&molecules[i], formula, TRUE);
	mnc+=strlen(formula)+1;
	found=FALSE;
	for (j=0; j<nmolecule; j++)
	{
	  if (!strcmp(formula, balancedReactants[j]))
	  {
	    found=TRUE;
	    break;
        }
      }
	if (found)
	{
        molecules[i].stoichioNumber=balancedReactions[j];
        if (molecules[i].reaction == REACTANT && balancedReactions[j] < 0)
	    k++;
        if (molecules[i].reaction == PRODUCT && balancedReactions[j] > 0)
	    k++;
      }
    }
    if (nmolecule_saved == k) return;
    fremem((void **)&balancedReactions);
    fremem((void **)&balancedReactants);
    fremem((void **)&reactants);
    balancedReactions=(int *)getmem((size_t)nmolecule, sizeof(int));
    balancedReactants=(char **)getmem((size_t)nmolecule, sizeof(char *));
    reactants=(char *)getmem(mnc, sizeof(char));
  }
  else
  {
    mnc=0;
    for (i=0; i<nmolecule; i++)
    {
      getSumFormula(&molecules[i], formula, TRUE);
      mnc+=strlen(formula)+1;
    }
    balancedReactions=(int *)getmem((size_t)nmolecule, sizeof(int));
    balancedReactants=(char **)getmem((size_t)nmolecule, sizeof(char *));
    reactants=(char *)getmem(mnc, sizeof(char));
  }

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

  stoichioNumber=(Widget *)getmem((size_t)nmolecule, sizeof(Widget));

  form1=XtVaCreateWidget("controlarea", xmRowColumnWidgetClass, form,
				 XmNorientation, XmHORIZONTAL,
                         NULL);
  j=0;
  last[0]='\0';
  p=reactants;
  nmolecule_saved=0;
  for (i=0; i<nmolecule; i++)
  {
    if (molecules[i].reaction == REACTANT)
    {
/*    sprintf(str, "%d", (-molecules[i].stoichioNumber));  */
      stoichioNumber[j++]=XtVaCreateManagedWidget("stoichioNumber", xmTextWidgetClass, form1,
			  	                          XmNvalue, "1",    /* str, */
				                          XmNcolumns, 3,
	                                            NULL);
      getSumFormula(&molecules[i], formula, TRUE);
	strcpy(p, formula);
	balancedReactants[i]=p;
	p+=strlen(formula)+1;
	nmolecule_saved++;
      strncpy(last, formula, MAXLENLINE);
	strcat(formula, " +");
	name=makeIndexedString(formula);
	label=XtVaCreateManagedWidget("label", xmLabelWidgetClass, form1,
				            XmNlabelString, name,
	                              NULL);
      XmStringFree(name);
    }
  }
  strcat(last, " \\rightarrow");
  name=makeIndexedString(last);
  XtVaSetValues(label, XmNlabelString, name, NULL);
  XmStringFree(name);
  for (i=0; i<nmolecule; i++)
  {
    if (molecules[i].reaction == PRODUCT)
    {
/*    sprintf(str, "%d", molecules[i].stoichioNumber);  */
      stoichioNumber[j++]=XtVaCreateManagedWidget("stoichioNumber", xmTextWidgetClass, form1,
				                          XmNvalue, "1",   /* str, */
				                          XmNcolumns, 3,
	                                            NULL);
      getSumFormula(&molecules[i], formula, TRUE);
	strcpy(p, formula);
	balancedReactants[i]=p;
	p+=strlen(formula)+1;
	nmolecule_saved++;
      strncpy(last, formula, MAXLENLINE);
	strcat(formula, " +");
	name=makeIndexedString(formula);
	label=XtVaCreateManagedWidget("label", xmLabelWidgetClass, form1,
				            XmNlabelString, name,
	                              NULL);
      XmStringFree(name);
    }
  }
  name=makeIndexedString(last);
  XtVaSetValues(label, XmNlabelString, name, NULL);
  XmStringFree(name);
  XtManageChild(form1);
  sep=XtVaCreateManagedWidget("sep", xmSeparatorWidgetClass, form,
                               XmNorientation, XmHORIZONTAL,
                               XmNtraversalOn, False,
                               XmNtopAttachment, XmATTACH_WIDGET,
					 XmNtopWidget, form1,
                               XmNleftAttachment, XmATTACH_FORM,
                               XmNrightAttachment, XmATTACH_FORM,
                               NULL);

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

  cont=TRUE;
  while (cont)
  {
    XtAppNextEvent(app, &event);
    XtDispatchEvent(&event);
  }
}

void balanceExit(Widget button, caddr_t which, XmPushButtonCallbackStruct *data)
{
  register int i, j;

  if ((int)which)
  {
    j=0;
    for (i=0; i<nmolecule; i++)
    {
      if (molecules[i].reaction == REACTANT)
	{
	  GetInt(stoichioNumber[j++],  (caddr_t)&molecules[i].stoichioNumber, data);
	  molecules[i].stoichioNumber=(-molecules[i].stoichioNumber);
      }
    }
    for (i=0; i<nmolecule; i++)
    {
      if (molecules[i].reaction == PRODUCT)
	  GetInt(stoichioNumber[j++],  (caddr_t)&molecules[i].stoichioNumber, data);
      balancedReactions[i]=molecules[i].stoichioNumber;
    }
    if (!checkReaction())
    {
	ringBell();
      return;
    }
  }
  fremem((void **)&stoichioNumber);
  XtDestroyWidget(dialog);
  cont=FALSE;
}

void GetInt(Widget button, caddr_t dest, XmPushButtonCallbackStruct *data)
{
  char *str;

  str=XmTextGetString(button);
  *(int *)dest=atoi(str);
}

int checkReaction(void)
{
  struct MOLECULE *mol;
  int *count, nelem, found;
  char *elements;
  register int i, j, k;

  elements=(char *)getmem((size_t)ne, 4*sizeof(char));
  count=(int *)getmem((size_t)ne, sizeof(int));
  nelem=0;
  for (i=0; i<nmolecule; i++)
  {
    mol=&molecules[i];
    for (j=0; j<mol->na; j++)
    {
      found=FALSE;
	for (k=0; k<nelem; k++)
	{
  	  if (!strcmp(&elements[4*k], mol->atoms[j].element->symbol))
	  {
	    found=TRUE;
	    count[k]+=mol->stoichioNumber;
        }
	}
	if (!found)
	{
        strcpy(&elements[4*nelem], mol->atoms[j].element->symbol);
	  count[nelem]=mol->stoichioNumber;
	  nelem++;
      }
    }
  }
  found=TRUE;
  for (i=0; i<nmolecule; i++)
  {
    if (count[i] != 0)
    {
      found=FALSE;
	continue;
    }
  }
  fremem((void **)&elements);
  fremem((void **)&count);
  return(found);
}
