#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <math.h>
#include <fstream.h>
#pragma hdrstop

#include "cryosim.h"

/* CryoSimulation v1.01
 *
 * Written by:  Chris Studholme
 * Last Update: 28-Oct-1996
 * Copyright:   GPL (http://www.fsf.org/copyleft/gpl.html)
 * Web Site:    http://www.bus.ualberta.ca/cstudhol/msc/
 */

#define maxLineLen	256
#define maxNumbers	8192

#define SoluteFile	"solutes.dat"
#define SoluteFileId   	'sf'
#define SimFileId	'Sm'

#define commentDelimiter ';'

#define ERROR(method,error) \
  fprintf(stderr,"ERROR in %s, %s\n",method,error)
#define WARNING(method,warning) \
  fprintf(stderr,"WARNING in %s, %s\n",method,warning)
#define ERRORLINET(linenum,warning,text) \
  fprintf(stderr,"ERROR in line %d, %s\n  \"%s\"\n",linenum,warning,text)
#define WARNINGLINE(linenum,warning) \
  fprintf(stderr,"WARNING in line %d, %s\n",linenum,warning)
#define WARNINGLINET(linenum,warning,text) \
  fprintf(stderr,"WARNING in line %d, %s\n  \"%s\"\n",linenum,warning,text)

//  Struct's

struct SimParms {
  REAL maxdt;		// maximum allowed dt (sec)
  REAL mindt;  		// minimum allowed dt (sec)
  REAL endTime;		// simulation run time (sec)
  REAL startTemp;	// starting temperature (K)
  REAL tempRate;	// temperature rate (K/sec)
  REAL endTemp;		// ending temperature (K)
  REAL reportTime;	// maximum time between reports (sec)
  int reportIter;	// maximum number of iterations between reports (int)
  REAL dendriteBreakdown;	// time of dendritic breakdown (sec)
};

struct SoluteWrapper {
  SoluteWrapper *next, *previous;
  SoluteProperties properties;
};

struct Unit {
  int fingerprint;		// unit fingerprint
  char text[8];			// unit spelling
  REAL  factor;			// factor to convert to base unit
};

//  Global Variables

Unit units[] = 
  {{0x00000001,"cm",1}, {0x00000001,"m",100}, {0x00000001,"mm",0.1},
   {0x00000001,"um",1e-4},
   
   {0x00000003,"ml",1}, {0x00000003,"l",1e3}, {0x00000003,"ul",1e-3},
   
   {0x00000010,"sec",1}, {0x00000010,"min",60}, {0x00000010,"hr",3600},
   
   {0x00000100,"mol",1}, {0x00000100,"kmol",1e3}, {0x00000100,"mmol",1e-3},
   
   {0x00001000,"g",1}, {0x00001000,"kg",1e3}, {0x00001000,"mg",1e-3},
   {0x00001000,"ug",1e-6},
   
   {0x00010000,"K",1}, {0x00010000,"C",1},
   
   {0x00100000,"J",1}, {0x00100000,"kJ",1e3}, {0x00100000,"mJ",1e-3},
   {0x00100000,"cal",4.184}, {0x00100000,"kcal",4184},
   
   {0x10000000,"atm",1},
   
   {0,"%",0.01}, {0,"",1}};

char simName[maxLineLen];
SimParms sp;
Compartment *root=0;
Solution *solutionList=0;

int numSolutes=0;
SoluteWrapper *soluteList=0;

int linenum=0;
int isSoluteFile=0;

struct NUMentry {
  Solution *solution;
  uint32 variable;
  uint32 soluteId;
};

int numREALs=0;
NUMentry REALlist[maxNumbers];

int numINT32s=0;
NUMentry INT32list[maxNumbers];

//  Global Methods

void AddVariablePointerToList(uint32, void*) {
}

void WriteVariablePointersToStream(const Solution *solution, opstream &os) {
  int i;
  for (i=0; i<numREALs; i++)
    if (REALlist[i].solution==solution) {
      os.writeWord32(REALlist[i].variable);
      os.writeWord32(i);
      if ((REALlist[i].variable&0xFF000000)==0)
	os.writeWord32(solution->FindSolute(REALlist[i].soluteId));
    }
  
  for (i=0; i<numINT32s; i++)
    if (INT32list[i].solution==solution) {
      os.writeWord32(INT32list[i].variable);
      os.writeWord32(i|0x80000000);
      if ((INT32list[i].variable&0xFF000000)==0)
	os.writeWord32(solution->FindSolute(INT32list[i].soluteId));
    }
}


int isSomething(const char* line) {
  if (!line) return 0;
  while (line[0]) {
    if ((line[0]>' ')&&(line[0]!=']')) 
      return 1;
    line++;
  }
  return 0;
}


char *strstrx(char *string, const char *sub) {
  char *value=strstr(string,sub);
  if ((value)&&((value==string)||(value[-1]<=' '))) return value;
  return 0;
}


int AddRealPointer(uint32 index, uint32 variable, Solution *solution, 
		   int soluteId=0) {
  index&=0x7FFFFFFF;
  if (index>=maxNumbers) {
    WARNINGLINE(linenum, "maximum pointer index exceeded");
    return 0;
  }
  if (((variable&0xFF000000)==0)&&(soluteId==0)) {
    WARNINGLINE(linenum, "invalid solute pointer index");
    return 0;
  }

  if (index>=numREALs) 
    numREALs=index+1;
  REALlist[index].variable = variable;
  REALlist[index].solution = solution;
  REALlist[index].soluteId = soluteId;

  return 1;
}


int AddInt32Pointer(uint32 index, uint32 variable, Solution *solution, 
		    int soluteId=0) {
  index&=0x7FFFFFFF;
  if (index>=maxNumbers) {
    WARNINGLINE(linenum, "maximum pointer index exceeded");
    return 0;
  }
  if (((variable&0xFF000000)==0)&&(soluteId==0)) {
    WARNINGLINE(linenum, "invalid solute pointer index");
    return 0;
  }

  if (index>=numINT32s) 
    numINT32s=index+1;
  INT32list[index].variable = variable;
  INT32list[index].solution = solution;
  INT32list[index].soluteId = soluteId;
  
  return 1;
}


SoluteProperties* FindSolute(int id) {
  SoluteWrapper *s = soluteList;
  while (s) {
    if (s->properties.id==id) return &s->properties;
    s=s->next;
  }
  
  return 0;
}

// note: returns factor=0 if celcius is the only unit found
int GetUnitsFromLine(const char *line, REAL &factor) {
  factor=1;
  int unit=0, celciusFound=0;
  while ((*line)&&(*line<=' ')) line++;
  while (*line>' ') {
    int isDenom=0;
    if (*line=='/') 
      isDenom=1, line++;
    if (*line=='*') 
      line++;
    
    const char *end=line+1;	// unit is at least one character
    while (*end>='A') 
      end++;
    
    int unitIndex=0;
    while ((units[unitIndex].text[0])&&
	   ((strlen(units[unitIndex].text)!=end-line)||
	    (strnicmp(line, units[unitIndex].text, end-line)!=0)))
      unitIndex++;
    
    if (units[unitIndex].text[0]==0)
      WARNINGLINE(linenum,"unrecognized unit");
    
    else if (strcmp(units[unitIndex].text,"C")==0) 
      celciusFound=1;
    
    int power = isDenom ? -1 : 1;
    
    if ((*end>='1')&&(*end<='9')) {
      power *= (*end-'0');
      end++;
    }
    
    factor *= pow(units[unitIndex].factor, power);
    
    // multiply fingerprint by power, then add to unit
    int fingerprint = units[unitIndex].fingerprint;
    for(int i=0; i<32; i+=4)
      if ((fingerprint&(0x0F<<i))) {
	int newunit = (unit>>i)&0x0F;
	if (newunit&8) 
	  newunit|=0xFFFFFFF0;
	
	int add = (fingerprint>>i)&0x0F;
	if (add&8) 
	  add|=0xFFFFFFF0;
	
	newunit+=add*power;
	
	if ((newunit&0xFFFFFFF0)&&((~newunit)&0xFFFFFFF0))
	  WARNINGLINE(linenum,"unit overflow");
	
	unit = (unit&(~(0x0F<<i))) + ((newunit&0x0F)<<i);
      }
    
    line=end;
  }
  
  if ((celciusFound)&&(unit==0x00010000)) factor=0;
  return unit;
}

REAL GetRealFromLine(const char *line, int &unit) {
  REAL r = atof(line);
  
  while(((*line<'A')||(*line=='E')||(*line=='e'))&&
	(*line!='*')&&(*line!='/')&&(*line))
    line++;
  
  REAL f=0;
  unit = GetUnitsFromLine(line, f);
  
  if (f==0) {		// unit is celcius
    r += 273.15;
    f = 1;
  }

  return r*f;
}

REAL GetRealAndCheckUnit(const char *line, int desiredUnits) {
  int unit=0;
  REAL r = GetRealFromLine(line,unit);
  if ((unit)&&(unit!=desiredUnits))
    WARNINGLINET(linenum,"incorrect units",line);
  return r;
}

TStreamableBase* ReadObjectFromTextFile(char *name, ifstream &is, 
					Solution *parent=0) {
  char line[maxLineLen];
  
  Solute *solute=0;
  SoluteProperties &sp = *(new SoluteProperties());
  
  REAL aqueous=0, solid=0, liquid=0;
  
  Solution *solution=0;
  REAL T=295;
  
  Compartment *compartment=0;
  CompartmentContents cc;	
  memset(&cc, 0, sizeof(cc));
  cc.nCompartments=1;
  
  JWCompartment *jwcompartment=0;
  JWParameters jwp;			
  memset(&jwp, 0, sizeof(jwp));

  DCompartment *dcompartment=0;
  DParameters dp;			
  memset(&dp, 0, sizeof(dp));
  int nSubInts=0;

  if (strnicmp(name,"solute",6)==0)
    solute = new Solute(sp, *parent);
  
  else if (strnicmp(name,"solution",8)==0)
    solution = new Solution();
  else if (strnicmp(name,"compartment",11)==0)
    solution = compartment = new Compartment((Compartment*)parent);
  else if (strnicmp(name,"jwcompartment",13)==0)
    solution = compartment = jwcompartment = 
      new JWCompartment((Compartment*)parent);
  else if (strnicmp(name,"dcompartment",12)==0)
    solution = compartment = dcompartment = 
      new DCompartment((Compartment*)parent);
  else {
    ERRORLINET(linenum,"unknown object",name);
    return 0;	// unknown object
  }
  
  do {
    is.getline(line, maxLineLen);  linenum++;
    char *value;
    if ((value=strchr(line,commentDelimiter))!=0) 
      *value=0;
    
    char *comma=strchr(line,',');
    if (comma) {
      *comma=0; 
      comma++; 
    }
    
// Solute
    if ((value=strstrx(line, "name="))!=0) {
      strcpy(sp.name,value+5);
      if (solution) solution->SetName(value+5);
    }
    else if ((value=strstrx(line, "id="))!=0) {
      sp.id = atoi(value+3);
      SoluteProperties *s = FindSolute(sp.id);
      if (s) memcpy(&sp, s, sizeof(SoluteProperties));
      else if (!isSoluteFile)
	WARNINGLINE(linenum, "new solute");
    }
    else if ((value=strstrx(line, "MW="))!=0)
      sp.MW = GetRealAndCheckUnit(value+3, 0x00001F00);		// g/mol
    else if ((value=strstrx(line, "dHf="))!=0)
      sp.dHf = GetRealAndCheckUnit(value+4, 0x00100F00);	// J/mol
    else if ((value=strstrx(line, "FP="))!=0)
      sp.FP = GetRealAndCheckUnit(value+3, 0x00010000);		// K
    else if ((value=strstrx(line, "Tn="))!=0)
      sp.Tn = GetRealAndCheckUnit(value+3, 0x00010000);		// K
    else if ((value=strstrx(line, "D="))!=0)
      sp.D = GetRealAndCheckUnit(value+2, 0x000000F2);		// cm2/sec
    else if ((value=strstrx(line, "a="))!=0)
      sp.a = GetRealAndCheckUnit(value+2, 0x00010F03);		// K*ml/mol
    else if ((value=strstrx(line, "b="))!=0)
      sp.b = GetRealAndCheckUnit(value+2, 0x00010E06);		// K*ml2/mol2
    else if ((value=strstrx(line, "aMV="))!=0)
      sp.aMV = GetRealAndCheckUnit(value+4, 0x00000F03);	// ml/mol
    else if ((value=strstrx(line, "aCp="))!=0)
      sp.aCp = GetRealAndCheckUnit(value+4, 0x001F0F00);	// J/K/mol
    else if ((value=strstrx(line, "aTC="))!=0)
      sp.aTC = GetRealAndCheckUnit(value+4, 0x001F00FF);	// J*cm/cm2/K/sec
    else if ((value=strstrx(line, "sMV="))!=0)
      sp.sMV = GetRealAndCheckUnit(value+4, 0x00000F03);	// ml/mol
    else if ((value=strstrx(line, "sCp="))!=0)
      sp.sCp = GetRealAndCheckUnit(value+4, 0x001F0F00);	// J/K/mol
    else if ((value=strstrx(line, "sTC="))!=0)
      sp.sTC = GetRealAndCheckUnit(value+4, 0x001F00FF);	// J*cm/cm2/K/sec
    else if ((value=strstrx(line, "lMV="))!=0)
      sp.lMV = GetRealAndCheckUnit(value+4, 0x00000F03);	// ml/mol
    else if ((value=strstrx(line, "lCp="))!=0)
      sp.lCp = GetRealAndCheckUnit(value+4, 0x001F0F00);	// J/K/mol
    else if ((value=strstrx(line, "lTC="))!=0)
      sp.lTC = GetRealAndCheckUnit(value+4, 0x001F00FF);	// J*cm/cm2/K/sec
    else if ((value=strstrx(line, "gMV="))!=0)
      sp.gMV = GetRealAndCheckUnit(value+4, 0x00000F03);	// ml/mol
    else if ((value=strstrx(line, "gCp="))!=0)
      sp.gCp = GetRealAndCheckUnit(value+4, 0x001F0F00);	// J/K/mol
    else if ((value=strstrx(line, "gTC="))!=0)
      sp.gTC = GetRealAndCheckUnit(value+4, 0x001F00FF);	// J*cm/cm2/K/sec
    else if ((value=strstrx(line, "vMV="))!=0)
      sp.vMV = GetRealAndCheckUnit(value+4, 0x00000F03);	// ml/mol
    else if ((value=strstrx(line, "vCp="))!=0)
      sp.vCp = GetRealAndCheckUnit(value+4, 0x001F0F00);	// J/K/mol
    else if ((value=strstrx(line, "vTC="))!=0)
      sp.vTC = GetRealAndCheckUnit(value+4, 0x001F00FF);	// J*cm/cm2/K/sec
    
    else if ((value=strstrx(line, "aqueous="))!=0) {
      int unit=0;
      aqueous = GetRealFromLine(value+8, unit);				// mol
      if ((unit==0x00000003)&&(sp.aMV))
	aqueous /= sp.aMV;
      else if ((unit==0x00001000)&&(sp.MW))
	aqueous /= sp.MW;
      else if ((unit)&&(unit!=0x00000100))
	WARNINGLINET(linenum,"incorrect units",line);
      if (comma) AddRealPointer(atoi(comma), 0x00100000, parent, sp.id);
    }
    else if ((value=strstrx(line, "solid="))!=0) {
      int unit=0;
      solid = GetRealFromLine(value+6, unit);				// mol
      if ((unit==0x00000003)&&(sp.sMV))
	solid /= sp.sMV;
      else if ((unit==0x00001000)&&(sp.MW))
	solid /= sp.MW;
      else if ((unit)&&(unit!=0x00000100))
	WARNINGLINET(linenum,"incorrect units",line);
      if (comma) AddRealPointer(atoi(comma), 0x00100001, parent, sp.id);
    }
    else if ((value=strstrx(line, "liquid="))!=0) {
      int unit=0;
      liquid = GetRealFromLine(value+7, unit);				// mol
      if ((unit==0x00000003)&&(sp.lMV))
	liquid /= sp.lMV;
      else if ((unit==0x00001000)&&(sp.MW))
	liquid /= sp.MW;
      else if ((unit)&&(unit!=0x00000100))
	WARNINGLINET(linenum,"incorrect units",line);
      if (comma) AddRealPointer(atoi(comma), 0x00100002, parent, sp.id);
    }
    
    // Solution
    else if ((value=strstrx(line, "T="))!=0) {
      T = GetRealAndCheckUnit(value+2, 0x00010000);			// K
      if (comma) AddRealPointer(atoi(comma), 0x01000001, solution);
    }
    else if ((value=strstrx(line, "F="))!=0) {
      if (comma) AddRealPointer(atoi(comma), 0x01000003, solution);
    }
    else if ((value=strstrx(line, "Vs="))!=0) {
      if (comma) AddRealPointer(atoi(comma), 0x01000002, solution);
    }
    else if ((value=strstrx(line, "ms="))!=0) {
      if (comma) AddRealPointer(atoi(comma), 0x01000004, solution);
    }
    
    // Compartment
    else if ((value=strstrx(line, "nCompartments="))!=0)
      cc.nCompartments = atoi(value+14);
    else if ((value=strstrx(line, "width="))!=0) {
      cc.width = GetRealAndCheckUnit(value+6, 0x00000001);	// cm
      if (comma) AddRealPointer(atoi(comma), 0x02000001, solution);
    }
    else if ((value=strstrx(line, "SA="))!=0) {
      cc.SA = GetRealAndCheckUnit(value+3, 0x00000002);		// cm2
      if (comma) AddRealPointer(atoi(comma), 0x02000002, solution);
    }
    else if ((value=strstrx(line, "pos="))!=0) {
      cc.pos = GetRealAndCheckUnit(value+4, 0x00000000);	// fraction of width
      if (comma) AddRealPointer(atoi(comma), 0x02000003, solution);
    }
    
    // JWCompartment
    else if ((value=strstrx(line, "lambda="))!=0)
      jwp.lambda = GetRealAndCheckUnit(value+7, 0x001F00FE);	// J/cm2/K/sec
    else if ((value=strstrx(line, "Lpg="))!=0) {
      jwp.Lpg = GetRealAndCheckUnit(value+4, 0xF00000F1);		// cm3/cm2/atm/sec
      if (comma) AddRealPointer(atoi(comma), 0x03000001, solution);
    }
    else if ((value=strstrx(line, "LpgTg="))!=0)
      jwp.LpgTg = GetRealAndCheckUnit(value+6, 0x00010000);		// K
    else if ((value=strstrx(line, "LpgEa="))!=0) {
      jwp.LpgEa = GetRealAndCheckUnit(value+6, 0x00100F00);		// J/mol
      if (comma) AddRealPointer(atoi(comma), 0x03000002, solution);
    }
    else if ((value=strstrx(line, "Psg="))!=0) {
      jwp.Psg = GetRealAndCheckUnit(value+4, 0x000000F1);		// cm/sec
      if (comma) AddRealPointer(atoi(comma), 0x03000004, solution);
    }
    else if ((value=strstrx(line, "PsgTg="))!=0)
      jwp.PsgTg = GetRealAndCheckUnit(value+6, 0x00010000);		// K
    else if ((value=strstrx(line, "PsgEa="))!=0) {
      jwp.PsgEa = GetRealAndCheckUnit(value+6, 0x00100F00);		// J/mol
      if (comma) AddRealPointer(atoi(comma), 0x03000005, solution);
    }
    else if ((value=strstrx(line, "sigma="))!=0) {
      jwp.sigma = GetRealAndCheckUnit(value+6, 0x00000000);		// fraction
      if (comma) AddRealPointer(atoi(comma), 0x03000007, solution);
    }
    else if ((value=strstrx(line, "Verror="))!=0)
      jwp.Verror = GetRealAndCheckUnit(value+7, 0x000000F0);	// fraction/sec
    else if ((value=strstrx(line, "permeantId="))!=0)
      jwp.permeantId = atoi(value+11);
    else if ((value=strstrx(line, "impermeantId="))!=0)
      jwp.impermeantId = atoi(value+13);
    else if ((value=strstrx(line, "dVdt="))!=0) {
      if (comma) AddRealPointer(atoi(comma), 0x03000101, solution);
    }
    else if ((value=strstrx(line, "dSdt="))!=0) {
      if (comma) AddRealPointer(atoi(comma), 0x03000102, solution);
    }
    
    // DCompartment and JWCompartment
    else if ((value=strstrx(line, "isSphere="))!=0)
      dp.isSphere = jwp.isSphere = atoi(value+9);
    else if ((value=strstrx(line, "Herror="))!=0)
      dp.Herror = jwp.Herror = GetRealAndCheckUnit(value+7, 0x000000F0);
    // fraction/sec
    else if ((value=strstrx(line, "Serror="))!=0)
      dp.Serror = jwp.Serror = GetRealAndCheckUnit(value+7, 0x000000F0);
    // fraction/sec

    // DCompartment
    else if ((value=strstrx(line, "nSubInts="))!=0)
      nSubInts = atoi(value+9);
    else if ((value=strstrx(line, "dXmax="))!=0)
      dp.dXmax = GetRealAndCheckUnit(value+6, 0x00000000);		// fraction/iteration
    else if ((value=strstrx(line, "initialConditions="))!=0)
      dp.initialConditions = atoi(value+18);
    else if ((value=strstrx(line, "X="))!=0) {
      if (comma) AddRealPointer(atoi(comma), 0x03000001, solution);
    }
    
    else if ((strchr(line, '['))&&(solution)) {
      TStreamableBase *object = 
	ReadObjectFromTextFile(strchr(line,'[')+1, is, solution);
      if (!object) 
	return 0;	// fatal error
      
      Solute *asolute = dynamic_cast<Solute*>(object);
      if (asolute) {
	solution->InsertSolute(*asolute);
	delete asolute;
      }

      else if (compartment) {
	Compartment *acompartment = dynamic_cast<Compartment*>(object);
	if (acompartment) compartment->AddCompartment(acompartment);
      }

      else
	WARNINGLINET(linenum, "unknown object", line);
    }

    else if (isSomething(line))
      WARNINGLINET(linenum, "unrecognized parameter", line);
    
  } while ((!strchr(line, ']'))&&(!is.eof()));

  if (solute) {
    solute->SetSolute(aqueous, solid, liquid);
    if (!sp.id)
      WARNINGLINET(linenum, "solute has null id", sp.name);
  }

  if (solution) 
    solution->SetTemperature(T);
  if (compartment) 
    compartment->SetCompartment(cc);
  if (jwcompartment) 
    jwcompartment->jwParameters = jwp;
  if (dcompartment) 
    dcompartment->dParameters = dp;
  if (dcompartment) 
    dcompartment->SetNumberOfSubInts(nSubInts);

  return solute ? (TStreamableBase*)solute : (TStreamableBase*)solution;
}

int ReadTextFile(ifstream& is) {
  char line[maxLineLen];
  Solution *previousSolution=0;
  
  memset(&sp, 0, sizeof(SimParms));
  
  while (!is.eof()) {
    is.getline(line, maxLineLen);
    linenum++;
    char *value;
    if ((value=strchr(line,commentDelimiter))!=0) *value=0;
    
    if ((value=strstrx(line, "name="))!=0)
      strcpy(simName,value+5);
    
    else if (strstrx(line, "version=")) {
    }

    else if ((value=strstrx(line, "maxdt="))!=0)
      sp.maxdt = GetRealAndCheckUnit(value+6, 0x00000010);		// sec
    else if ((value=strstrx(line, "mindt="))!=0)
      sp.mindt = GetRealAndCheckUnit(value+6, 0x00000010);		// sec
    else if ((value=strstrx(line, "endTime="))!=0)
      sp.endTime = GetRealAndCheckUnit(value+8, 0x00000010);		// sec
    else if ((value=strstrx(line, "startTemp="))!=0)
      sp.startTemp = GetRealAndCheckUnit(value+10, 0x00010000);		// K
    else if ((value=strstrx(line, "tempRate="))!=0)
      sp.tempRate = GetRealAndCheckUnit(value+9, 0x000100F0);	       // K/sec
    else if ((value=strstrx(line, "endTemp="))!=0)
      sp.endTemp = GetRealAndCheckUnit(value+8, 0x00010000);		// K
    else if ((value=strstrx(line, "reportTime="))!=0)
      sp.reportTime = GetRealAndCheckUnit(value+11, 0x00000010);	// sec
    else if ((value=strstrx(line, "reportIter="))!=0)
      sp.reportIter = atoi(value+11);
    else if ((value=strstrx(line, "dendriteBreakdown="))!=0)
      sp.dendriteBreakdown = GetRealAndCheckUnit(value+18, 0x00000010);	// sec
    
    else if (strchr(line, '[')) {
      TStreamableBase *object = ReadObjectFromTextFile(strchr(line,'[')+1, is);
      if (!object) return 0;	// fatal error
      Compartment *compartment = dynamic_cast<Compartment*>(object);
      if (compartment) root=compartment;
      else {
	Solution *solution = dynamic_cast<Solution*>(object);
	if (solution) {
	  if (previousSolution) 
	    previousSolution->next = solution;
	  else 
	    solutionList=solution;
	  previousSolution=solution;
	}
      }
    }
    
    else if ((isSomething(line))||(strchr(line,']')))
      WARNINGLINET(linenum, "unrecognized parameter", line);
    
  } // while

  return 1;
}

int ReadSoluteText(ifstream& is) {
  char line[maxLineLen];
  SoluteWrapper *lastSolute=0;
  
  Solution dummySolution;
  
  while (!is.eof()) {
    is.getline(line, maxLineLen);	
    linenum++;
    char *value;
    if ((value=strchr(line,commentDelimiter))!=0) 
      *value=0;
    
    if ((value=strstrx(line, "name="))!=0)
      strcpy(simName,value+5);

    else if (strstrx(line, "version=")) {
    }
    
    else if (strchr(line, '[')) {
      Solute *solute = 
	dynamic_cast<Solute*>(ReadObjectFromTextFile(strchr(line,'[')+1, 
						     is, &dummySolution));
      if (!solute) 
	return 0;	// fatal error
      
      SoluteWrapper *sol = new SoluteWrapper;
      sol->previous=sol->next=0;
      memcpy (&sol->properties, &solute->properties, sizeof(SoluteProperties));
      
      if (lastSolute) 
	lastSolute->next=sol;
      else soluteList=sol;
      lastSolute=sol;
      numSolutes++;
    }
    
    else if ((isSomething(line))||(strchr(line,']')))
      WARNINGLINET(linenum, "unrecognized parameter", line);
    
  } // while

  return 1;
}


int ReadSoluteFile(ipstream& is) {
  if (!is.good()) 
    return 0;

  int id=is.readWord16();	// file type identifier
  if (id!=SoluteFileId)
    ERROR("MAKESIM::ReadSoluteFile", "invalid file id");
  int ver=is.readWord16();	// file version
  if (ver!=0x101)
    ERROR("MAKESIM::ReadSoluteFile", "invalid file version");

  is.readString(simName, sizeof(simName));
  
  numSolutes = is.readWord32();

  SoluteWrapper *solute, *lastSolute=0;
  for(int i=0; i<numSolutes; i++) {
    solute = new SoluteWrapper;
    solute->next=solute->previous=0;
    if (!is.readObject(solute->properties))
      return 0;  // fatal error

    if (lastSolute) 
      lastSolute->next=solute;
    else 
      soluteList=solute;
    lastSolute=solute;
  }

  return numSolutes;
}


int WriteSimulationFile(opstream& os) {
  if (!os.good()) 
    return 0;

  os.writeWord16(SimFileId);		// file type identifier
  os.writeWord16(0x102);		// file version

  os.writeString(simName);		//simulation name

  os.writeDouble(sp.maxdt);
  os.writeDouble(sp.mindt);
  os.writeDouble(sp.endTime);
  os.writeDouble(sp.startTemp);
  os.writeDouble(sp.tempRate);
  os.writeDouble(sp.endTemp);
  os.writeDouble(sp.reportTime);
  os.writeWord32(sp.reportIter);
  os.writeDouble(sp.dendriteBreakdown);

  os.writeWord32(numREALs);		// number of REALs needed
  os.writeWord32(numINT32s);		// number of INT32s needed

  if (!os.good()) return 0;
  os.writeObject(root);			// root object

  if (!os.good()) return 0;

  Solution *s = solutionList;
  while (s) {
    os.writeObject(s);
    s = s->next;
  }
  os.writeObject(s);
  
  return 1;
}


int WriteSoluteFile(opstream& os) {
  if (!os.good()) 
    return 0;
  
  os.writeWord16(SoluteFileId);	// file type identifier
  os.writeWord16(0x101);		// file version

  os.writeString(simName);		// solute file name

  os.writeWord32(numSolutes);

  if (!os.good()) 
    return 0;

  SoluteWrapper *s = soluteList;
  for(int i=0; i<numSolutes; i++) {
    os.writeObject(&(s->properties));
    s = s->next;
  }
  
  return 1;
}


main(int argc, char*argv[]) {
  if (argc!=3) {
    fprintf(stderr,"Usage:\n  makesim outfile infile\n  makesim -s solutefile\n");
    return 1;
  }

  fprintf(stderr,"Opening Text File %s...\n", argv[2]);
  ifstream infile(argv[2]);
  if (!infile.good()) {
    ERROR("MAKESIM::main", "failed to open file");
    infile.close();
    return 1;
  }

  linenum=0;
  memset(&REALlist, 0, sizeof(REALlist));
  memset(&INT32list, 0, sizeof(INT32list));

  if (stricmp(argv[1],"-s")==0)	{
    isSoluteFile=1;
    fprintf(stderr,"Reading Solutes...\n");
    if (!ReadSoluteText(infile)) {
      ERROR("MAKESIM::main", "failed to read solute file");
      infile.close();
      return 1;
    }
    infile.close();
    
    fprintf(stderr,"Writing Solutes to file %s...\n", SoluteFile);
    opstream outfile(SoluteFile);
    if (!WriteSoluteFile(outfile))
      ERROR("MAKESIM::main", "failed to write solute file");
    outfile.close();
  }
  
  else {
    fprintf(stderr,"Reading Solute File %s...\n", SoluteFile);
    ipstream solutefile(SoluteFile);
    int n;
    if ((n=ReadSoluteFile(solutefile))!=0)
      fprintf(stderr,"Read %d solutes.\n", n);
    else ERROR("MAKESIM::main", "failed to read solute file");
    solutefile.close();
    
    fprintf(stderr,"Creating Simulation...\n");
    if (!ReadTextFile(infile)) {
      ERROR("MAKESIM::main", "failed to read text file");
      infile.close();
      return 1;
    }
    infile.close();
    
    fprintf(stderr, "Writing Simulation to file %s...\n", argv[1]);
    opstream outfile(argv[1]);
    if (!WriteSimulationFile(outfile))
      ERROR("MAKESIM::main", "failed to write simulation file");
    outfile.close();
  }

  fprintf(stderr,"Done.\n");
  return 0;
}

