/************************************************************
'ca1' model code repository
Written by Marianne Bezaire, marianne.bezaire@gmail.com, www.mariannebezaire.com
In the lab of Ivan Soltesz, www.ivansolteszlab.org
Published and latest versions of this code are available online at:
ModelDB: 
Open Source Brain: http://www.opensourcebrain.org/projects/nc_ca1

Main code file: ../main.hoc
This file: Last updated on April 9, 2015

This file defines and calls proc for writing out a receipt of all the
simulation run parameters. The receipt is written in MATLAB syntax so
that it can be read in by the MATLAB-based SimTracker tool.

The proc also ensures that the name of the run is unique. If
the name passed into the RunName parameter was already used
(ie, if a directory by that name already exists in the 'results'
directory of this model repository), then the proc will find a
new, unique name for the run (either by appending numbers to the
proposed RunName or incrementing already-appended numbers).

Note: the code in this file performs a lot of system calls, including
one to create a new directory in the results folder. If you do not
want this behavior, set the parameter 'OK2executeSysCmds' to 0 in the
set_other_parameters.hoc file. See the warning message in that file
for things to keep in mind if you set 'OK2executeSysCmds' to 0.
************************************************************/

objref frec, strobj

strobj = new StringFunctions()

strdef cmd, dircmd, direx, comper, version, vercomment, vercomment2
strdef mypath, userstr, machname, machnick, outfile, edate, comver
strdef verleft, verright
strdef thisline, getval, result, paramstr, testline, valstr

proc typeout() {local i, myline, rn, rank, gid, srcid localobj tgt, f, cell, f2c
	if (pc.id == 0) {	// Only one processor needs to write the receipt file
	
		// First, check to make sure RunName has not already been used.
		rn = -1	// rn will tell us what number to append to the RunName to make it unique
		
		sprint(dircmd, "[ -d \"../results/%s\" ] && echo \"AlreadyExists\" ", RunName)

		{system(dircmd, direx)}	// Execute the list command and store the standard output
								//  from it in the direx string		
		
		// If the RunName has already been used, we will append/increment a number at
		//  the end of the RunName to make a new RunName, then test that RunName for 
		//  uniqueness.
		while (strcmp(direx,"AlreadyExists\n")==0 ) {
			rn = rn + 1
			sprint(dircmd, "[ -d \"../results/%s_%02.0f\" ] && echo \"AlreadyExists\" ", RunName, rn)		
			{system(dircmd, direx)}
		}
		
		// If we needed to change the RunName to make it unique, the rn flag will be >-1
		if (rn>-1) {
			sprint(RunName, "%s_%02.0f", RunName, rn)
		}		
		
		// Create the RunName directory within the results directory; all results from the simulation
		//  run will be stored in this directory.
		sprint(dircmd, "mkdir ../results/%s", RunName)
		{system(dircmd, direx)}

		// check for Mercurial on the computer running this code
		sprint(dircmd, "hg parent --template '{rev}: {desc}\n'")
		{system(dircmd, direx)}
		
		// Check the length of the output string (direx)
		index = strobj.len(direx)

		if (index<1) { 	// If  the output string contains nothing (length == 0),
						//  then we can't access Mercurial commands from this
						//  computer, and we must resort to using placeholder
						//  files generated on the local computer that were copied
						//  over to the executing computer, and hope that they have
						//  the up-to-date information about which code version will
						//  be used to run this simulation.
			sprint(dircmd, "cp ../hg_status.out  ../results/%s/hg_status.out", RunName)
			{system(dircmd, direx)}	// A file that contains the output of the 'hg status' command
			
			sprint(dircmd, "cp ../hg_diff.out  ../results/%s/hg_diff.out", RunName)
			{system(dircmd, direx)}	// A file that contains the output of the 'hg diff' command
				
			f2c = new File()		
			
			{system("ls vercomment.txt", direx)}
			if (strcmp(direx,"ls: cannot access vercomment.txt: No such file or directory")==0 || strcmp(direx,"")==0 ) {
				// if backup files do not exist, fill in placeholder info
				vercomment="unknown"
			} else {
				f2c.ropen("../vercomment.txt")	// A file that contains the comment associated with
												//  the Mercurial commit of the code version presumably
												//  being used to run this simulation
				myline=f2c.gets(vercomment)	
				if (myline>1) {
					strobj.left(vercomment, myline-1)	
				}		
				f2c.close
			}
			
			{system("ls version.txt", direx)}
			if (strcmp(direx,"ls: cannot access version.txt: No such file or directory")==0 || strcmp(direx,"")==0 ) {
				// if backup files do not exist, fill in placeholder info
				version="unknown"
			} else {
				f2c.ropen("../version.txt")	// A file that contains the version number associated with
											//  the Mercurial commit of the code version presumably
											//  being used to run this simulation
				f2c.scanstr(version)			
				f2c.close	
			}	
		} else {	// If the original check returns something, then we can access Mercurial commands
					//  from this computer (which is ideal).
					
			sprint(dircmd, "hg status")
			{system(dircmd, direx)}
			if (strcmp(direx,"")>0) {	// Check to see if there are any changes to the code on this
										//  computer to make it differ from the active version. If
										//  so, then record those changes (via the 'hg status' and
										//  'hg diff' commands) into files.
				comver = "Yes"
				sprint(dircmd, "hg status > ../results/%s/hg_status.out", RunName)
				{system(dircmd, direx)}
				
				sprint(dircmd, "hg diff > ../results/%s/hg_diff.out", RunName)
				{system(dircmd, direx)}
			} else {
				comver = "No"
			}
			
			sprint(dircmd, "hg parent --template '{rev}: {desc}\n'")
			{system(dircmd, direx)}	// This will return the 'friendly' version number and
									//  the comment associated with this commit of the code.
									//  Beware that the friendly version number is not 
									//  necessarily consistent between computers. It is safer
									//  to only work in terms of the full version number, though
									//  that number is hard to appreciate when you just want a
									//  list of incrementing numbers corresponding to subsequent
									//  code versions.
			
			if (strobj.len(direx)<2) {
				vercomment="unknown"
			} else {
				strobj.left(direx, strobj.len(direx)-1) // hg parent rev/desc
				vercomment=direx
			}
			
			// Parse out the friendly version number and comment (this may not be necessary if we
			//  just separately executed commands to return the version number and the comment).
			i = strobj.substr(vercomment, "'")
			while (i>0) {
				verleft = vercomment
				strobj.left(verleft, i)

				verright = vercomment
				strobj.right(verright, i+1)
				
				sprint(vercomment,"%s''%s", verleft, verright)
				
				i = strobj.substr(verright, "'")
				if (i>0) {
					i = i + strobj.len(verleft)+2
				}
			}

			sprint(dircmd, "hg parent --template '{node}\n'")
			{system(dircmd, direx)}
			if (strobj.len(direx)<2) {
				version="unknown"
			} else {
				strobj.left(direx, strobj.len(direx)-1)
				version=direx
			}
		}

		system("cd ..;pwd",mypath)
		if (strobj.len(mypath)<2) {
			mypath="unknown"
		} else {
			strobj.left(mypath, strobj.len(mypath)-1) // path to the model repository
		}
		system("whoami", userstr)
		if (strobj.len(userstr)<2) {
			userstr="unknown"
		} else {
			strobj.left(userstr, strobj.len(userstr)-1) // person logged into computer
		}
		{system("hostname", machname)}
		if (strobj.len(machname)<2) {
			machname="unknown"
		} else {
			strobj.left(machname, strobj.len(machname)-1) // name of the computer
		}
		machnick =  machname	// nickname for host machine, usually second term in address		
		i = strobj.substr(machnick, ".")
		if (i>0) {
			strobj.right(machnick, i+1)
			i = strobj.substr(machnick, ".")
			if (i>0) {
				strobj.left(machnick, i)
			}
		}

		{system("date \"+%d-%b-%Y %H:%M:%S\"", edate)}	// Current date-time, will be considered
		if (strobj.len(edate)<2) {						//  the execution date-time.
		edate="unknown"
		} else {
			strobj.left(edate, strobj.len(edate)-1)
		}

		// Now create a receipt file, open it, and write all the run metadata as well as the parameter
		//  values to the file.
		sprint(outfile, "../results/%s/runreceipt.txt", RunName)
		frec = new File(outfile)
		frec.wopen()
		frec.printf("NumProcessors = %g;\n", pc.nhost)
		frec.printf("ExecutionDate = '%s';\n", edate)
		frec.printf("ExecutedBy = '%s';\n", userstr)
		if (strcmp(machnick,"local")!=0) {
			frec.printf("Machine = '%s';\n", machnick)
		}
		frec.printf("MachineFull = '%s';\n", machname)
		frec.printf("ModelVerComment='%s';\n", vercomment)
		frec.printf("ModelVersion='%s';\n", version)
		frec.printf("ModelDirectory ='%s';\n", mypath)
		frec.printf("NEURONVersion ='%s';\n", nrnversion(2)) // version number and mercurial changeset
		frec.printf("WorkDirChg ='%s';\n", comver) // whether this job was run using a model working directory that
														// had been changed since the last version update													
														// If no, the run can therefore be easily reproduced(No)
														// If yes, we won't have any record of the changes and that
														// will limit our ability to reproduce the run unless
														// we record the changes, which we do in that case (Yes)
														
		printmyvars()	// Prints all the parameter values into the receipt file (parameters found in parameters.hoc)
		frec.close()
	}

	pc.barrier()					// Wait for all ranks to get to this point

	// Also generate a results file that contains a table of all the cell types in the model.
	sprint(cmd,"../results/%s/celltype.dat", RunName)
	f = new File(cmd)
	if (pc.id == 0) { 				// Write header to file 1 time only
		f.wopen()
		f.printf("celltype\ttechtype\ttypeIndex\trangeStart\trangeEnd\n")
		for i=0, numCellTypes-1 {
			f.printf("%s\t%s\t%d\t%d\t%d\n", cellType[i].cellType_string, cellType[i].technicalType, i, cellType[i].cellStartGid, cellType[i].cellEndGid)
		}
		f.close()
	}
}


// This function reads in the parameters.hoc file to get a list of all parameters, and then for
//  each parameter, calls a function that gets the current value of the parameter (which may be
//  different from the value listed in parameters.hoc if someone passed in a new value when
//  calling the main superdeep.hoc file to run), and then writes it to the receipt file.
proc printmyvars() {local strlen localobj pfobj
	pfobj = new File()
	pfobj.ropen("../setupfiles/parameters.hoc")
	while (pfobj.gets(thisline)>-1) {
		testline=thisline
		strobj.left(testline, 1) // testline
		if (strcmp(testline,"d")==0) {
			result=thisline
			strobj.right(result, 13)
			strlen = strobj.head(result, "\",", paramstr)
			if (strobj.len(paramstr)>0) {
				{sprint(getval, "printnow(%s, paramstr)", paramstr)}
				{execute1(getval)} 	// This has the effect of passing in the parameter value
			}						//  as the first argument and the parameter name as the
		}							//  second argument.
	}
	pfobj.close
}

// This function gets the current value of the passed in parameter (argument $1 or $s1), taking
//  into account the variable type of the parameter, and writes it out to the receipt file
//  along with the name of the parameter (argument $s2), using MATLAB syntax so the file can
//  be read in by the SimTracker later.
proc printnow() {
	if (argtype(1)==0) {
		{frec.printf("%s = %g;\n", $s2, $1)}
	} else {
		if (argtype(1)==2) {
			{frec.printf("%s = '%s';\n", $s2, $s1)}
		} 
	}
}

typeout()	// Call the function we defined in this file.

{pc.barrier()}				// Wait for all processors to get to this point
							//  to ensure that the next command is not run
							//  before processor 0 (which just did all the work)
							//  is ready.

{pc.broadcast(RunName, 0)} 	// Send RunName parameter to all processors,
							//  in case host 0 changed it to ensure a
							//  unique value.