#ifndef boolean
typedef unsigned char boolean;
#endif
#include "Phreeqc.h"
#include "phqalloc.h"
#include "Solution.h"
#include "Utils.h"

#define STRING 11
#define NUMBER 12
#define MIXED 13
#define EOL 14

#define OPTION_EOF -1
#define OPTION_KEYWORD -2
#define OPTION_ERROR -3
#define OPTION_DEFAULT -4
#define OPT_1 -5

#if defined(PHREEQCI_GUI)
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#endif

/* ---------------------------------------------------------------------- */
int Phreeqc::
read_solution_spread(void)
/* ---------------------------------------------------------------------- */
{
/*
 *      Reads solution data
 *
 *      Arguments:
 *         none
 *
 *      Returns:
 *         KEYWORD if keyword encountered, input_error may be incremented if
 *                    a keyword is encountered in an unexpected position
 *         EOF     if eof encountered while reading mass balance concentrations
 *         ERROR   if error occurred reading data
 *
 */
	class spread_row *heading, *row_ptr, *units;
	int count, strings, numbers;
	int spread_lines;
	const char* cptr;
	class defaults soln_defaults;
	int return_value, opt;
	const char* next_char;
	const char *opt_list[] = {
		"temp",					/* 0 */
		"temperature",			/* 1 */
		"dens",					/* 2 */
		"density",				/* 3 */
		"units",				/* 4 */
		"redox",				/* 5 */
		"ph",					/* 6 */
		"pe",					/* 7 */
		"unit",					/* 8 */
		"isotope",				/* 9 */
		"water",				/* 10 */
		"isotope_uncertainty",	/* 11 */
		"uncertainty",			/* 12 */
		"uncertainties",		/* 13 */
		"pressure",				/* 14 */
		"press"		  		    /* 15 */
	};
	int count_opt_list = 16;
/*
 * Initialize defaults
 */
	soln_defaults.temp = 25;
	soln_defaults.density = 1.0;
	soln_defaults.calc_density = false;
	soln_defaults.units = string_hsave("mmol/kgw");
	soln_defaults.redox = string_hsave("pe");
	soln_defaults.ph = 7.0;
	soln_defaults.pe = 4.0;
	soln_defaults.water = 1.0;
	soln_defaults.pressure = 1.0;

#ifdef PHREEQCI_GUI
	free_spread();
#endif


	/* fill in soln_defaults.iso */
	soln_defaults.iso.resize(count_iso_defaults);

	/* all iso[i].name is hsave'd, so no conflicts */
	// memcpy(&soln_defaults.iso[0], iso_defaults,
	// 	   soln_defaults.iso.size() * sizeof(class iso));
	for (size_t i = 0; i < count_iso_defaults; ++i) {
		soln_defaults.iso[i].name        = iso_defaults[i].name;
		soln_defaults.iso[i].value       = iso_defaults[i].value;
		soln_defaults.iso[i].uncertainty = iso_defaults[i].uncertainty;
	}

	heading = NULL;
	units = NULL;
	return_value = UNKNOWN;
	spread_lines = 0;
	CParser parser(this->phrq_io);
/*
 *   Loop on solutions
 */
	for (;;)
	{
		std::string token, token1;
		opt = get_option(opt_list, count_opt_list, &next_char);
		if (spread_lines == 0 && opt != OPTION_DEFAULT)
		{
			row_ptr = string_to_spread_row(line);
			cptr = line;
			count = numbers = strings = 0;
			int j;
			while (((j = copy_token(token, &cptr)) != EMPTY))
			{
				count++;
				if (j == UPPER || j == LOWER)
					strings++;
				if (j == DIGIT)
					numbers++;
			}
			/*
			 * Is 2nd token all number
			 */
			cptr = line;
			copy_token(token, &cptr);
			j = copy_token(token, &cptr);
			bool num = false;
			if (j == DIGIT)
			{
				char* ptr;
				(void)strtod(token.c_str(), &ptr);
				cptr = ptr;
				int j1 = copy_token(token1, &cptr);
				if (j1 != EMPTY)
				{
					num = FALSE;
				}
				else
				{
					num = TRUE;
				}
			}
			/*
			 *   Starts with hyphen
			 */
			cptr = line;
			copy_token(token, &cptr);
			if (token[0] == '-')
			{
				/* opt = opt; */
			}
			else
			{
				switch (opt)
				{
				case 0:		/* temp */
				case 1:		/* temperature */
				case 2:		/* dens */
				case 3:		/* density */
				case 10:		/* water */
					if ((count == 2 || count == 3) && num == TRUE)
					{
						/* opt = opt; */
					}
					else
					{
						opt = OPTION_DEFAULT;
					}
					break;
				case 6:		/* ph */
				case 7:		/* pe */
					if ((count == 2 || count == 3 || count == 4)
						&& num == TRUE)
					{
						/* opt = opt; */
					}
					else
					{
						opt = OPTION_DEFAULT;
					}
					break;
				case 5:		/* redox */
				case 4:		/* units */
				case 8:		/* unit */
					if (count == 2)
					{
						/* opt = opt; */
					}
					else
					{
						opt = OPTION_DEFAULT;
					}
					break;
				case 9:		/* isotope */
					if (row_ptr->count > 4)
					{
						opt = OPTION_DEFAULT;
					}
					else
					{
						/* opt = opt; */
					}
					break;
				case 11:		/* isotope_uncertainty */
				case 12:		/* uncertainty */
				case 13:		/* uncertainties */
					if (row_ptr->count > 3)
					{
						opt = OPTION_DEFAULT;
					}
					else
					{
						/* opt = opt; */
					}
					break;
				case 14: /* pressure */
				case 15: /* press */
					(void)sscanf(next_char, SCANFORMAT, &(soln_defaults.pressure));
					break;
				}
			}
			spread_row_free(row_ptr);
		}
		if (opt == OPTION_DEFAULT)
		{
			if (spread_lines == 0)
			{
				opt = 100;
			}
			spread_lines++;
		}
		switch (opt)
		{
		case OPTION_EOF:		/* end of file */
			return_value = EOF;
			break;
		case OPTION_KEYWORD:	/* keyword */
			return_value = KEYWORD;
			break;
		case OPTION_ERROR:
			input_error++;
			error_msg("Unknown input in SOLUTION keyword.", CONTINUE);
			error_msg(line_save, CONTINUE);
			break;
		case OPTION_DEFAULT:	/* solution definition */
			row_ptr = string_to_spread_row(line);
			if (spread_lines == 2)
			{
				numbers = 0;
				strings = 0;
				//for (int i = 0; i < heading->count; i++)
				for (int i = 0; i < row_ptr->count; i++)
				{
					if (row_ptr->type_vector[i] == STRING)
					{
						strings++;
					}
					else if (row_ptr->type_vector[i] == NUMBER)
					{
						numbers++;
					}
				}
				if (numbers == 0)
				{
					units = row_ptr;
					break;
				}
			}
			spread_row_to_solution(heading, units, row_ptr, soln_defaults);
#ifdef PHREEQCI_GUI
			add_row(row_ptr);
#endif
			spread_row_free(row_ptr);
			break;
		case 0:				/* temperature */
		case 1:
			(void)sscanf(next_char, SCANFORMAT, &(soln_defaults.temp));
			break;
		case 2:				/* density */
		case 3:
			//sscanf(next_char, SCANFORMAT, &(soln_defaults.density));
			{
				copy_token(token, &next_char);
				if (sscanf(token.c_str(), SCANFORMAT, &dummy) != 1)
				{
						error_msg("Expecting numeric value for density.", PHRQ_io::OT_CONTINUE);
						error_msg(line_save, PHRQ_io::OT_CONTINUE);
						input_error++;
				}
				else
				{
					soln_defaults.density = dummy;
				}
				int j = copy_token(token, &next_char);
				if (j != EMPTY)
				{
					if (token[0] != 'c' && token[0] != 'C')
					{
						error_msg("Only option following density is c[alculate].", PHRQ_io::OT_CONTINUE);
						error_msg(line_save, PHRQ_io::OT_CONTINUE);
						input_error++;
					}
					else
					{
						soln_defaults.calc_density = true;
					}
				}
			}
			break;
		case 4:				/* units */
		case 8:				/* unit */
			if (copy_token(token, &next_char) == EMPTY)
				break;
			{
				if (check_units(token, FALSE, FALSE, NULL, TRUE) == OK)
				{
					soln_defaults.units = string_hsave(token.c_str());
				}
				else
				{
					input_error++;
				}
			}
			break;
		case 5:				/* redox */
			if (copy_token(token, &next_char) == EMPTY)
				break;
			if (parser.parse_couple(token) == OK)
			{
				soln_defaults.redox = string_hsave(token.c_str());
			}
			else
			{
				input_error++;
			}
			break;
		case 6:				/* ph */
			copy_token(token, &next_char);
			(void)sscanf(token.c_str(), SCANFORMAT, &(soln_defaults.ph));
			if (copy_token(token, &next_char) != EMPTY)
			{
				warning_msg
					("Not possible to use phase name or saturation index in definition of default pH in SOLUTION_SPREAD.");
			}
			break;
		case 7:				/* pe */
			copy_token(token, &next_char);
			(void)sscanf(token.c_str(), SCANFORMAT, &(soln_defaults.pe));
			if (copy_token(token, &next_char) != EMPTY)
			{
				warning_msg
					("Not possible to use phase name or saturation index in definition of default pe in SOLUTION_SPREAD.");
			}
			break;
		case 11:				/* isotope_uncertainty */
		case 12:				/* uncertainty */
		case 13:				/* uncertainties */
			{
				if (copy_token(token, &next_char) != DIGIT)
				{
					input_error++;
					error_string = sformatf( "Expected isotope name to"
						" begin with an isotopic number.");
					error_msg(error_string, CONTINUE);
					error_string = sformatf( "In read_solution_spread isotope_uncertainty\n");
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
					error_string = sformatf( "\t%s\t%s\n", "token:     ", token.c_str());
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
					error_string = sformatf( "\t%s\t%s\n", "next_char: ", next_char);
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
					error_string = sformatf( "\t%s\t%s\n", "line_save: ", line_save);
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
					continue;
				}
				size_t i;
				for (i = 0; i < soln_defaults.iso.size(); i++)
				{
					if (strcmp(token.c_str(), soln_defaults.iso[i].name) == 0)
					{
						break;
					}
				}
				if (i == soln_defaults.iso.size())
				{
					soln_defaults.iso.resize((size_t)i + 1);
					soln_defaults.iso[i].name = string_hsave(token.c_str());
					soln_defaults.iso[i].value = NAN;
					soln_defaults.iso[i].uncertainty = NAN;
				}

				/* read and store isotope ratio uncertainty */
				int j;
				if ((j = copy_token(token, &next_char)) != EMPTY)
				{
					if (j != DIGIT)
					{
						input_error++;
						error_string = sformatf(
							"Expected numeric value for uncertainty in isotope ratio.");
						error_msg(error_string, CONTINUE);
						continue;
					}
					else
					{
						(void)sscanf(token.c_str(), SCANFORMAT,
							&(soln_defaults.iso[i].uncertainty));
					}
				}
				else
				{
					soln_defaults.iso[i].uncertainty = NAN;
				}
			}
			break;
		case 10:				/* water */
			{
				int j = copy_token(token, &next_char);
				if (j != DIGIT)
				{
					input_error++;
					error_string = sformatf(
						"Expected numeric value for mass of water in solution.");
					error_msg(error_string, CONTINUE);
				}
				else
				{
					(void)sscanf(token.c_str(), SCANFORMAT, &(soln_defaults.water));
				}
			}
			break;
		case 9:				/* isotope */
			{
				if (copy_token(token, &next_char) != DIGIT)
				{
					input_error++;
					error_string = sformatf( "Expected isotope name to"
						" begin with an isotopic number.");
					error_msg(error_string, CONTINUE);
					error_string = sformatf( "In read_solution_spread isotope\n");
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
					error_string = sformatf( "\t%s\t%s\n", "token:     ", token.c_str());
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
					error_string = sformatf( "\t%s\t%s\n", "next_char: ", next_char);
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
					error_string = sformatf( "\t%s\t%s\n", "line_save: ", line_save);
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
					continue;
				}
				size_t i;
				for (i = 0; i < soln_defaults.iso.size(); i++)
				{
					if (strcmp(token.c_str(), soln_defaults.iso[i].name) == 0)
					{
						break;
					}
				}
				if (i == soln_defaults.iso.size())
				{
					soln_defaults.iso.resize(i + 1);
					soln_defaults.iso[i].name = string_hsave(token.c_str());
					soln_defaults.iso[i].value = NAN;
					soln_defaults.iso[i].uncertainty = NAN;
				}
				/* read and store isotope ratio */
				if (copy_token(token, &next_char) != DIGIT)
				{
					input_error++;
					error_string = sformatf(
						"Expected numeric value for default isotope ratio.");
					error_msg(error_string, CONTINUE);
					break;
				}
				(void)sscanf(token.c_str(), SCANFORMAT, &(soln_defaults.iso[i].value));
				/* read and store isotope ratio uncertainty */
				int j;
				if ((j = copy_token(token, &next_char)) != EMPTY)
				{
					if (j != DIGIT)
					{
						input_error++;
						error_string = sformatf(
							"Expected numeric value for uncertainty in isotope ratio.");
						error_msg(error_string, CONTINUE);
						continue;
					}
					else
					{
						(void)sscanf(token.c_str(), SCANFORMAT,
							&(soln_defaults.iso[i].uncertainty));
					}
				}
			}
			break;
		case 14: /* pressure */
			(void)sscanf(next_char, SCANFORMAT, &(soln_defaults.pressure));
			break;
		case 100:				/* read headings */
			heading = string_to_spread_row(line);
			{
				int i;
				for (i = 0; i < heading->count; i++)
				{
					while (replace(" ", "", heading->str_vector[i]) == TRUE);
					while (replace(",", "_", heading->str_vector[i]) == TRUE);
				}
			}
			break;
		}
		if (return_value == EOF || return_value == KEYWORD)
			break;
	}
#ifdef PHREEQCI_GUI
	if (heading)
	{
		assert(g_spread_sheet.heading == NULL);
		g_spread_sheet.heading = copy_row(heading);
	}
	if (units)
	{
		assert(g_spread_sheet.units == NULL);
		g_spread_sheet.units = copy_row(units);
	}
	g_spread_sheet.defaults = soln_defaults;
#endif
	spread_row_free(heading);
	spread_row_free(units);

	soln_defaults.iso.clear();
	return (return_value);
}
/* ---------------------------------------------------------------------- */
int Phreeqc::
spread_row_to_solution(class spread_row *heading, class spread_row *units,
					   class spread_row *data, class defaults defaults)
/* ---------------------------------------------------------------------- */
{
	Keywords::KEYWORDS next_keyword_save;
	int n_user, n_user_end;
	std::string description;
	std::string token, token1, string;
	CParser parser(this->phrq_io);

	int return_value, opt;
	const char* next_char;
	const char *opt_list[] = {
		"temp",					/* 0 */
		"temperature",			/* 1 */
		"dens",					/* 2 */
		"density",				/* 3 */
		"units",				/* 4 */
		"redox",				/* 5 */
		"ph",					/* 6 */
		"pe",					/* 7 */
		"unit",					/* 8 */
		"isotope",				/* 9 */
		"water",				/* 10 */
		"description",			/* 11 */
		"desc",					/* 12 */
		"descriptor",			/* 13 */
		"pressure",				/* 14 */
		"press",			    /* 15 */
		"potential"			    /* 16 */
	};
	int count_opt_list = 17;

/*
 *      look for solution number
 */
	n_user = n_user_end = -1;
	{
		int i; 
		for (i = 0; i < heading->count; i++)
		{
			if (strcmp_nocase(heading->str_vector[i].c_str(), "number") == 0)
			{
				break;
			}
		}
		if (i == heading->count || data->type_vector[i] == EMPTY
			|| data->count <= i)
		{
			n_user = -1;
		}
		else if (data->type_vector[i] == STRING)
		{
			input_error++;
			error_string = sformatf(
				"Expected solution number or number range in 'number' column, found:  %s.",
				data->str_vector[i].c_str());
			error_msg(error_string, CONTINUE);
		}
		else
		{
			string = "solution_s ";
			string.append(data->str_vector[i]);
			next_keyword_save = next_keyword;
			next_keyword = Keywords::KEY_SOLUTION_SPREAD;
			cxxNumKeyword nk;
			nk.read_number_description(string);
			n_user = nk.Get_n_user();
			n_user_end = nk.Get_n_user_end();
			description = nk.Get_description();
			next_keyword = next_keyword_save;
			Rxn_new_solution.insert(n_user);
		}
	}
/*
 *   set up solution
 */
	
	cxxSolution temp_solution;
	temp_solution.Set_n_user(n_user);
	temp_solution.Set_n_user_end(n_user_end);
	temp_solution.Set_new_def(true);
	temp_solution.Create_initial_data();
	cxxISolution * initial_data_ptr = temp_solution.Get_initial_data();
	if (use.Get_solution_in() == FALSE)
	{
		use.Set_solution_in(true);
		use.Set_n_solution_user(n_user);
	}
/*
 *   Set default ph, temp, density, pe, units
 */
	temp_solution.Set_description(description);
	temp_solution.Set_tc(defaults.temp);
	temp_solution.Set_patm(defaults.pressure);
	temp_solution.Set_potV(0);
	temp_solution.Set_ph(defaults.ph);
	temp_solution.Set_density(defaults.density);
	initial_data_ptr->Set_calc_density(defaults.calc_density);
	temp_solution.Set_pe(defaults.pe);
	temp_solution.Set_mass_water(defaults.water);
	temp_solution.Set_ah2o(1.0);
	temp_solution.Set_mu(1e-7);
	initial_data_ptr->Set_units(defaults.units);
	initial_data_ptr->Set_default_pe(defaults.redox);
	{
		CReaction temp_chem_reaction;
		initial_data_ptr->Get_pe_reactions()[defaults.redox] = temp_chem_reaction;
	}
/*
 *   Read concentration data
 */
	return_value = UNKNOWN;
	for (int i = 0; i < heading->count; i++)
	{
		if (strcmp_nocase(heading->str_vector[i].c_str(), "number") == 0)
			continue;
		if (strcmp_nocase(heading->str_vector[i].c_str(), "uncertainty") == 0)
			continue;
		if (strcmp_nocase(heading->str_vector[i].c_str(), "uncertainties") == 0)
			continue;
		if (strcmp_nocase(heading->str_vector[i].c_str(), "isotope_uncertainty") ==
			0)
			continue;
		/*
		 *  Copy in element name
		 */
		if (heading->type_vector[i] == EMPTY)
			continue;
		string = heading->str_vector[i];
		string.append(" ");
		/*
		 *  Copy in concentration data
		 */
		if (i >= data->count || data->type_vector[i] == EMPTY)
			continue;
		string.append(data->str_vector[i]);
		string.append(" ");
		/*
		 *  Copy in concentration data
		 */
		if (units != NULL && i < units->count
			&& units->type_vector[i] != EMPTY)
		{
			string.append(units->str_vector[i]);
		}
/*
 *   Parse string just like read_solution input 
 */
		char * char_string = string_duplicate(string.c_str());
		next_char = char_string;
		opt = get_option_string(opt_list, count_opt_list, &next_char);
		if (opt == OPTION_DEFAULT && heading->type_vector[i] == NUMBER)
		{
			opt = 9;
		}
		switch (opt)
		{
		case OPTION_EOF:		/* end of file */
			return_value = EOF;
			break;
		case OPTION_KEYWORD:	/* keyword */
			return_value = KEYWORD;
			break;
		case OPTION_ERROR:
			input_error++;
			error_msg("Unknown input in SOLUTION keyword.", CONTINUE);
			error_msg(line_save, CONTINUE);
			break;
		case 0:				/* temperature */
		case 1:
			(void)sscanf(next_char, SCANFORMAT, &dummy);
			temp_solution.Set_tc(dummy);
			break;
		case 2:				/* density */
		case 3:
			{
				int j = copy_token(token, &next_char);
				(void)sscanf(token.c_str(), SCANFORMAT, &dummy);
				temp_solution.Set_density(dummy);
				j = copy_token(token, &next_char);
				if (j != EMPTY)
				{
					if (token[0] != 'c' && token[0] != 'C')
					{
						error_msg("Only option following density is c[alculate].", PHRQ_io::OT_CONTINUE);
						error_msg(line_save, PHRQ_io::OT_CONTINUE);
						input_error++;
					}
					else
					{
						initial_data_ptr->Set_calc_density(true);
					}
				}
			}
			break;
		case 4:				/* units */
		case 8:				/* unit */
			if (copy_token(token, &next_char) == EMPTY)
				break;
			{
				if (check_units(token, false, false, initial_data_ptr->Get_units().c_str(), true) == OK)
				{
					initial_data_ptr->Set_units(token);
				}
				else
				{
					input_error++;
				}
			}
			break;
		case 5:				/* redox */
			if (copy_token(token, &next_char) == EMPTY)
				break;
			if (parser.parse_couple(token) == OK)
			{
				const char * pe_str = string_hsave(token.c_str());
				initial_data_ptr->Set_default_pe(pe_str);
				CReaction temp_chem_reaction;
				initial_data_ptr->Get_pe_reactions()[token] = temp_chem_reaction;
			}
			else
			{
				input_error++;
			}
			break;
		case 6:				/* ph */
			{
				cxxISolutionComp temp_comp(this->phrq_io);
				if (temp_comp.read(char_string, &temp_solution) == CParser::PARSER_ERROR)
				{
					input_error++;
					break;
				}
				
				temp_solution.Set_ph(temp_comp.Get_input_conc());
				if (temp_comp.Get_equation_name().size() == 0)
				{
					break;
					
				}
				temp_comp.Set_description("H(1)");
				initial_data_ptr->Get_comps()[temp_comp.Get_description()] = temp_comp;
			}
			break;
		case 7:				/* pe */
			{
				cxxISolutionComp temp_comp(this->phrq_io);
				if (temp_comp.read(char_string, &temp_solution) == CParser::PARSER_ERROR)
				{
					input_error++;
					break;
				}
				temp_solution.Set_pe(temp_comp.Get_input_conc());
				if (temp_comp.Get_equation_name().size() == 0)
				{
					break;
				}
				temp_comp.Set_description("E");
				initial_data_ptr->Get_comps()[temp_comp.Get_description()] = temp_comp;
			}
			break;
		case 9:				/* isotope */
			{
				next_char = char_string;
				cxxSolutionIsotope temp_isotope;
				if (copy_token(token, &next_char) !=  CParser::TT_DIGIT)
				{
					input_error++;
					error_string = sformatf( "Expected isotope name to"
						" begin with an isotopic number.");
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
					error_string = sformatf( "In spread_row_to_solution isotope\n");
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
					error_string = sformatf( "\t%s\t%s\n", "token:       ", token.c_str());
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
					error_string = sformatf( "\t%s\t%s\n", "next_char:   ", next_char);
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
					error_string = sformatf( "\t%s\t%s\n", "char_string: ", char_string);
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
					error_string = sformatf( "\t%s\t%s\n", "line_save:   ", line_save);
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
//class spread_row
//{
//	int count;
//	int empty, string, number;
//	char **char_vector;
//	LDBLE *d_vector;
//	int *type_vector;
//};
					error_string = sformatf("Heading spread_row\n");
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
					if (heading)
					{
						for (int ii = 0; ii < heading->count; ii++)
						{
							error_string = sformatf("%d\t%s\n",ii,heading->str_vector[ii].c_str());
							error_msg(error_string, PHRQ_io::OT_CONTINUE);
						}
					}
					else
					{
						error_msg("heading is null", PHRQ_io::OT_CONTINUE);
					}

					error_string = sformatf("Data spread_row\n");
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
					if (data)
					{
						for (int ii = 0; ii < data->count; ii++)
						{
							error_string = sformatf("%d\t%s\t%d\n",ii,data->str_vector[ii].c_str(),data->type_vector[ii]);
							error_msg(error_string, PHRQ_io::OT_CONTINUE);
						}
					}
					else
					{
						error_msg("Data is null", PHRQ_io::OT_CONTINUE);
					}
					error_string = sformatf("Units spread_row\n");
					error_msg(error_string, PHRQ_io::OT_CONTINUE);
					if (units)
					{
						for (int ii = 0; ii < units->count; ii++)
						{
							error_string = sformatf("%d\t%s\n",ii,units->str_vector[ii].c_str());
							error_msg(error_string, PHRQ_io::OT_CONTINUE);
						}
					}
					else
					{
						error_msg("Units is null", PHRQ_io::OT_CONTINUE);
					}
					free_check_null(char_string);
					continue;
				}
				temp_isotope.Set_isotope_name(token.c_str());

				/* read and save element name */
				{
					char *temp_iso_name = string_duplicate(token.c_str());
					const char* cptr1 = temp_iso_name;
					get_num(&cptr1, &dummy);
					temp_isotope.Set_isotope_number(dummy);
					if (cptr1[0] == '\0' || isupper((int)cptr1[0]) == FALSE)
					{
						error_msg("Expecting element name.", PHRQ_io::OT_CONTINUE);
						error_msg(line_save, PHRQ_io::OT_CONTINUE);
						input_error++;
						temp_iso_name = (char*)free_check_null(temp_iso_name);
						char_string = (char*)free_check_null(char_string);
						return (CParser::PARSER_ERROR);
					}
					temp_isotope.Set_elt_name(cptr1);
					temp_iso_name = (char*)free_check_null(temp_iso_name);
				}
				/* read and store isotope ratio */
				if (copy_token(token, &next_char) != CParser::TT_DIGIT)
				{
					input_error++;
					error_string = sformatf(
						"Expected numeric value for isotope ratio.");
					error_msg(error_string, CONTINUE);
					free_check_null(char_string);
					continue;
				}
				(void)sscanf(token.c_str(), SCANFORMAT, &dummy);
				temp_isotope.Set_ratio(dummy);
				temp_isotope.Set_ratio_uncertainty(NAN);

				/* read and store isotope ratio uncertainty */
				int j;
				if ((j = copy_token(token, &next_char)) != CParser::TT_EMPTY)
				{
					if (j != DIGIT)
					{
						input_error++;
						error_string = sformatf(
							"Expected numeric value for uncertainty in isotope ratio.");
						error_msg(error_string, PHRQ_io::OT_CONTINUE);
						free_check_null(char_string);
						continue;
					}
					(void)sscanf(token.c_str(), SCANFORMAT, &dummy);
					temp_isotope.Set_ratio_uncertainty(dummy);
				}
				temp_solution.Get_isotopes()[temp_isotope.Get_isotope_name()] = temp_isotope;
			}
			break;
		case 10:				/* water */
			{
				int j = copy_token(token, &next_char);
				if (j == EMPTY)
				{
					temp_solution.Set_mass_water(1.0);
				}
				else if (j != DIGIT)
				{
					input_error++;
					error_string = sformatf(
						"Expected numeric value for mass of water in solution.");
					error_msg(error_string, CONTINUE);
				}
				else
				{
					(void)sscanf(token.c_str(), SCANFORMAT, &dummy);
					temp_solution.Set_mass_water(dummy);
				}
			}
			break;
		case 11:				/* description */
		case 12:				/* desc */
		case 13:				/* descriptor */
			{
				temp_solution.Set_description(next_char);
			}
			break;
		case 14:				/* pressure */
		case 15:				/* press */
			{
				if (sscanf(next_char, SCANFORMAT, &dummy) == 1)
				{
					temp_solution.Set_patm(dummy);
				}
			}
			break;
		case 16:				/* pote, V */
			{
				if (sscanf(next_char, SCANFORMAT, &dummy) == 1)
				{
					temp_solution.Set_potV(dummy);
				}
			}
			break;
		case OPTION_DEFAULT:
/*
 *   Read concentration
 */
			{
				next_char = char_string;
				if (copy_token(token, &next_char) == LOWER)
				{
					free_check_null(char_string);
					continue;
				}
				cxxISolutionComp temp_comp(this->phrq_io);
				if (temp_comp.read(char_string, &temp_solution) == CParser::PARSER_ERROR)
				{
#ifdef SKIP
					input_error++;
					break;
#endif
				}
				initial_data_ptr->Get_comps()[temp_comp.Get_description()] = temp_comp;
				if (temp_comp.Get_pe_reaction().size() > 0)
				{
					CReaction temp_chem_reaction;
					initial_data_ptr->Get_pe_reactions()[temp_comp.Get_pe_reaction()] = temp_chem_reaction;
				}
			}
			break;
		}
		free_check_null(char_string);
		if (return_value == EOF || return_value == KEYWORD)
			break;
	}
/*
 *   fix up default units and default pe
 */
	std::map < std::string, cxxISolutionComp >::iterator it;
	for (it = initial_data_ptr->Get_comps().begin(); it != initial_data_ptr->Get_comps().end(); it++)
	{
		token = it->first;
		Utilities::str_tolower(token);
		if (it->second.Get_units().size() == 0)
		{
			it->second.Set_units(initial_data_ptr->Get_units().c_str());
		}
		else
		{
			bool alk = false;
			if (strstr(token.c_str(), "alk") == token.c_str())
				alk = true;
			std::string token1 = it->second.Get_units();
			if (check_units(token1, alk, true, initial_data_ptr->Get_units().c_str(), true) ==	CParser::PARSER_ERROR)
			{
				input_error++;
			}
			else
			{
				it->second.Set_units(token1.c_str());
			}
		}
		if (it->second.Get_pe_reaction().size() == 0)
		{
			it->second.Set_pe_reaction(initial_data_ptr->Get_default_pe());
		}
	}
	if (n_user >= 0)
	{
		Rxn_solution_map[n_user] = temp_solution;
	}
	else
	{
		unnumbered_solutions.push_back(temp_solution);
	}
	return (return_value);
}
/* ---------------------------------------------------------------------- */
class spread_row * Phreeqc::
string_to_spread_row(char *string)
/* ---------------------------------------------------------------------- */
{
	int j;
	std::string token;
	const char* cptr;
/*
 *   Allocate space
 */
	class spread_row* spread_row_ptr = new class spread_row;
	if (spread_row_ptr == NULL)
	{
		malloc_error();
		return spread_row_ptr;
	}
	spread_row_ptr->count = 0;
	spread_row_ptr->empty = 0;
	spread_row_ptr->string = 0;
	spread_row_ptr->number = 0;
	cptr = string;
/*
 *   Split by tabs, reallocate space
 */
	for (;;)
	{
		j = copy_token_tab(token, &cptr);
		if (j == EOL)
			break;
		spread_row_ptr->str_vector.push_back(token);
		if (j == EMPTY || token.size() == 0)
		{
			spread_row_ptr->empty++;
			spread_row_ptr->type_vector.push_back(EMPTY);
		}
		else if (j == UPPER || j == LOWER)
		{
			spread_row_ptr->string++;
			spread_row_ptr->type_vector.push_back(STRING);
		}
		else if (j == DIGIT)
		{
			spread_row_ptr->number++;
			spread_row_ptr->type_vector.push_back(NUMBER);
		}
		else
		{
			input_error++;
			error_msg("Unknown input in string_to_spread_row keyword.", CONTINUE);
			error_string = sformatf("\tcopy_token j: %d, token: %s\n", j, token.c_str());
			error_msg(error_string, CONTINUE);
			error_msg(line_save, CONTINUE);
		}
		spread_row_ptr->count++;
	}
	assert(spread_row_ptr->count == spread_row_ptr->str_vector.size());
	assert(spread_row_ptr->count == spread_row_ptr->type_vector.size());
	assert(spread_row_ptr->count == spread_row_ptr->empty + spread_row_ptr->string + spread_row_ptr->number);
	return (spread_row_ptr);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
spread_row_free(class spread_row *spread_row_ptr)
/* ---------------------------------------------------------------------- */
{
	if (spread_row_ptr == NULL)
		return (OK);
	spread_row_ptr->str_vector.clear();
	spread_row_ptr->type_vector.clear();
	delete spread_row_ptr;
	return (OK);
}

/* ---------------------------------------------------------------------- */
int Phreeqc::
copy_token_tab(std::string& token, const char **cptr)
/* ---------------------------------------------------------------------- */
{
/*
 *   Copies from **cptr to *token until first tab is encountered.
 *
 *   Arguments:
 *      *token       output, place to store token
 *
 *     **cptr        input, character string to read token from
 *                   output, next position after token
 *   Returns:
 *      UPPER,
 *      LOWER,
 *      DIGIT,
 *      EMPTY,
 *      EOL,
 *      UNKNOWN.
 */
	int i, return_value;
	char c;
/*
 *   Strip leading spaces
 */
	token.clear();
	while ((c = **cptr) == ' ')
		(*cptr)++;
/*
 *   Check what we have
 */
	if (isupper((int) c) || c == '[')
	{
		return_value = UPPER;
	}
	else if (islower((int) c))
	{
		return_value = LOWER;
	}
	else if (isdigit((int) c) || c == '.' || c == '-')
	{
		return_value = DIGIT;
	}
	else if (c == '\0')
	{
		return_value = EOL;
		return (return_value);
	}
	else if (c == '\t')
	{
		return_value = EMPTY;
	}
	else
	{
		return_value = UNKNOWN;
	}
/*
 *   Begin copying to token
 */
	i = 0;
	for (;;)
	{
		c = **cptr;
		if (c == '\t')
		{
			(*cptr)++;
			break;
		}
		else if (c == '\0')
		{
			break;
		}
		else
		{
			token.push_back(c);
			(*cptr)++;
			i++;
		}
	}
	return (return_value);
}

/* ---------------------------------------------------------------------- */
 int Phreeqc::
get_option_string(const char **opt_list, int count_opt_list, const char **next_char)
/* ---------------------------------------------------------------------- */
{
/*
 *   Read a line and check for options
 */
	int j;
	int opt_l, opt;
	const char *opt_ptr;
	char option[MAX_LENGTH];

	opt_ptr = *next_char;
	if (opt_ptr[0] == '-')
	{
		opt_ptr++;
		copy_token(option, &opt_ptr, &opt_l);
		if (find_option(&(option[1]), &opt, opt_list, count_opt_list, FALSE)
			== OK)
		{
			j = opt;
			*next_char = opt_ptr;
		}
		else
		{
			error_msg("Unknown option.", CONTINUE);
			error_msg(*next_char, CONTINUE);
			input_error++;
			j = OPTION_ERROR;
		}
	}
	else
	{
		copy_token(option, &opt_ptr, &opt_l);
		if (find_option(&(option[0]), &opt, opt_list, count_opt_list, TRUE)
			== OK)
		{
			j = opt;
			*next_char = opt_ptr;
		}
		else
		{
			j = OPTION_DEFAULT;
		}
	}
	return (j);
}

#if defined(PHREEQCI_GUI)
/* ---------------------------------------------------------------------- */
void Phreeqc::
free_spread(void)
/* ---------------------------------------------------------------------- */
{
	int i;
	spread_row_free(g_spread_sheet.heading);
	spread_row_free(g_spread_sheet.units);
	for (i = 0; i < g_spread_sheet.rows.size(); ++i)
	{
		spread_row_free(g_spread_sheet.rows[i]);
	}
	g_spread_sheet.rows.clear();
	g_spread_sheet.defaults.iso.clear();
	g_spread_sheet.defaults.redox = NULL;
	g_spread_sheet.defaults.units = NULL;

	g_spread_sheet.heading = NULL;
	g_spread_sheet.units   = NULL;
	g_spread_sheet.defaults.iso.clear();
}

/* ---------------------------------------------------------------------- */
void Phreeqc::
add_row(class spread_row *spread_row_ptr)
/* ---------------------------------------------------------------------- */
{
	g_spread_sheet.rows.push_back(copy_row(spread_row_ptr));
}

/* ---------------------------------------------------------------------- */
class spread_row * Phreeqc::
copy_row(class spread_row *spread_row_ptr)
/* ---------------------------------------------------------------------- */
{
	spread_row *copy = new spread_row(*spread_row_ptr);
	if (copy == NULL)
		malloc_error();
	return copy;
}
#endif
