// ATF.hoc
// Writes data in axon text file (*.atf) format
// Last revision: 11/17/2006
// CSH, University of Freiburg
// modified by JB 6.9.2014 
//
// Usage:
// 1. Create a file:
//	objref myFile
//	myFile=new ATF_File("myFilename.atf")
//	or
//	myFile=new ATF_File() // default file name is "unnamed.atf"
// 2.	Set members:
//	myFile.x_scale=dt	    // sampling interval; default is 1.0
//	myFile.x_units="us"   // default is "ms"
//	myFile.y_units="pA"   // default is "mV"
// 3.	Add vectors:
//	objref myVec
//	myVec=new Vector(20,0.2)
//	myFile.addVec(myVec)
//	or
//	myFile.addVec(myVec,"myVecTitle")
// 4a Write everything to file and close it:
//	myFile.flush()
// 4b Or close file without writing:
//	myFile.close()
//
// The individual vectors will be written into columns,
// the first column will contain the time values.
// Note that nothing will be written to your file
// as long as you don't call flush().
//
// Known restrictions:
// 1. Write-only; existing ATF files can currently not be read
//	into a List of Vectors
// 2.	No access to the List other than appending Vectors is currently
//	granted; this could easily be implemented by forwarding
//	List member functions (described as "emulation of 
//	inheritance" in chapter 13.5 of the NEURON book).
// 3. Once flush() has been called, the file will be closed and
//	can't be re-opened.
// 4. File name can't be changed once an object has been created
// 5. Only the minimum required by the ATF specification is
//	written into the header.
//

// Wrapper class for strings so they can be treated like objects
// (can't be sure the standard library's String is loaded):
begintemplate StringClass
public myString
strdef myString
endtemplate StringClass

begintemplate ATF_File
// public:
	public addVec, flush, filename, x_scale, x_units, y_units 
	strdef x_units, y_units

// private:
	// a list of vectors:
	objref vecList
	// the vectors' names:
	objref vecNameList
	// a File object:
	objref file
	// the file name:
	strdef filename

// Constructor:
proc init() {
	vecList=new List()
	vecNameList=new List()
	if (numarg()==1) {
		filename=$s1
	} else {
		filename="unnamed.atf"
	}
	file=new File(filename)
	file.wopen()
	x_scale=1.0
	x_units="ms"
	y_units="mV"
}

// public member functions:	
// Append a Vector to the Vector List:
proc addVec() {local allowed localobj vecName
	// check whether the vector can be added:
	allowed=1
	if (!file.isopen()) {
		print "ATF file has been closed and can't be re-opened."
		allowed=0
	} else {
		if (vecList.count()!=0) {
			// can't put this into one single if statement,
			// because vecList.o(0) does not exist if
			// vecList.count()==0 - but will still be 
			// tested if within the same statement
			if ($o1.size()!=vecList.o(0).size()) {
				print "Vectors must have equal sizes"
				allowed=0
			}
		}
	}
	if (allowed) {
		vecList.append($o1)
		vecName=new StringClass()
		if (numarg()==2) {
			vecName.myString=$s2
		} else {
			sprint(vecName.myString,"Vector[%d]",vecList.count()-1)
		}
		vecNameList.append(vecName)
	}
}

//*****************************************************
// Write everything to the file, erase the Vector List and close the file
//*****************************************************

proc flush() {local m, n // m collums, n lines
	if (!file.isopen()) {
		print "ATF file has been closed and can't be re-opened."
		} else {
		// Write Header:
		file.printf("ATF\t1.0\n%d \t%d \n",0, vecList.count()+1)
		
		// Write column titles:
		file.printf("\"Time (%s)\"\t",x_units)
		for m=0,vecList.count()-1 {
			file.printf("\"%s (%s)\"\t",vecNameList.o(m).myString,y_units)
		}
		file.printf("\n")
		
		// Write data columns:
		for n=0,vecList.o(0).size()-1 {
			// First column is time:
			file.printf("%f\t",x_scale*n)
			for m=0,vecList.count()-1 {
				file.printf("%f\t",vecList.o(m).x(n))
			}		
			file.printf("\n")
		}
		vecList.remove_all()	
		file.close()
	}
}		

proc close() {
	if (!file.isopen()) {
		print "ATF file has already been closed."
	} else {
		file.close()
	}
}

endtemplate ATF_File