/*
 * The permission is granted to use the software as is and redistribute it in
 * its original and complete form. If you find this program useful and
 * publish results obtained by it, please cite the program as: Michael
 * Whitbeck, Desert Research Institute, 1991, "REACT - Program to solve
 * kinetic equations for chemical systems". Version 1.00, this code is
 * available from the authors or from Dr. Whitbeck.
 */

/*
 * reaction parser
 */

#include "rxn.h"

static SPECIES *species;	/* species name/ident list */

extern double   atof();

char           *
species_word(id)
	int             id;
{
	SPECIES        *p;

	for (p = species; p != NULL; p = p->next)
		if (p->id == id)
			return p->name;
	return NULL;
}

int
species_id(word)
	char           *word;
{
	SPECIES        *p;

	for (p = species; p != NULL; p = p->next)
		if (strcmp(p->name, word) == NULL)
			return p->id;
	return 0;
}

/*-
 * add_species_node()
 * parameters: address of pointer to head of list,
 *	initialization data for new node;
 * return: pointer to new node;
 *
 * new node added to end of list;
 * head node maintains pointer to current last node in list;
 * first call (with pointer to head node == NULL) initializes pointer
 *	with address of new node == head node;
 * circular doubly linked list, with pointer from last node to next
 *	set to NULL instead of to head, for terminating traversal from 
 * 	head to last;
 */

SPECIES        *
add_species_node(p_head_node, word, id)
	SPECIES       **p_head_node;
	char           *word;
	int             id;
{
	SPECIES        *next_node;

	if ((next_node = (SPECIES *) malloc(
					 (size_t) sizeof(SPECIES))) == NULL)
		bail("insufficient memory");
	if ((next_node->name = (char *) malloc(
		   (size_t) ((strlen(word) + 1) * sizeof(char *)))) == NULL)
		bail("insufficient memory");
	strcpy(next_node->name, word);
	next_node->id = id;
	next_node->next = NULL;	/* last node has NULL list terminator */
	if (*p_head_node != NULL) {
		next_node->last = (*p_head_node)->last;	/* link new last node to
							 * old last node */
		(*p_head_node)->last->next = next_node;	/* link old last node to
							 * new last node */
		(*p_head_node)->last = next_node;	/* maintain head node
							 * pointer to last in
							 * list */
	} else {
		*p_head_node = next_node;	/* initialize list */
		next_node->last = next_node;
	}
	return next_node;
}

/*-
 * add_reaction_node()
 * as for add_species_node();
 */

RXN            *
add_reaction_node(p_head_node, id, k)
	RXN           **p_head_node;
	int             id;
	double          k;
{
	RXN            *next_node;

	if ((next_node = (RXN *) malloc(
					(size_t) sizeof(RXN))) == NULL)
		bail("insufficient memory");
	next_node->k = k;
	next_node->reactant = NULL;
	next_node->product = NULL;
	next_node->rate = 0;
	next_node->id = id;
	next_node->next = NULL;
	if (*p_head_node != NULL) {
		next_node->last = (*p_head_node)->last;
		(*p_head_node)->last->next = next_node;
		(*p_head_node)->last = next_node;
	} else {
		*p_head_node = next_node;
		next_node->last = next_node;
	}
	return next_node;
}

void
rxnparse(root, p_reaction, p_num_reactions, p_num_species)
	char           *root;
	RXN           **p_reaction;	/* global address of pointer to head
					 * of RXN list */
	int            *p_num_reactions;
	int            *p_num_species;
{
	RXN            *reaction;	/* local name for pointer to last
					 * node added to RXN list */
	FILE           *mech;
	int             product_flag;
	int             id;
	char           *word;
	char            line[BUFSIZ];
	static char    *tokens = " $\t\n,+;:(){}[]";	/* newline in tokens */
	static char    *arrow = "->";

	mech = fopen(strcat(strcpy(line, root), MECH_SUFFIX), "r");
	if (mech == NULL)
		bail("Could not open mechanism file");
	*p_num_reactions = *p_num_species = 0;
	species = NULL;
	*p_reaction = NULL;
	while (fgets(line, BUFSIZ, mech)) {	/* reuse of line */
		if ((word = strtok(line, " $\t\n")) == NULL)
			break;
		/* first word = rate constant */
		reaction = add_reaction_node(p_reaction,
					     ++*p_num_reactions, atof(word));
		product_flag = 0;
		/* rest of line = reaction description */
		while ((word = strtok((char *) NULL, tokens))) {
			if (strcmp(arrow, word) == 0) {
				product_flag = 1;
				continue;
			}
			if ((id = species_id(word)) == 0) {
				id = ++*p_num_species;
				add_species_node(&species, word, id);
			}
			if (product_flag)
				add_species_node(&reaction->product, word, id);
			else
				add_species_node(&reaction->reactant, word, id);
		}
		if (reaction->reactant == NULL || reaction->product == NULL)
			bail("no reactants or products");
	}
	if (!*p_num_reactions || !*p_num_species)
		bail("no reactions or species");
	fclose(mech);
}
