if (name_declared("pkgversions") != 4 ) {  execute("strdef pkgversions") }
sprint(pkgversions,"%sFormatFile = $Revision: 1.15 $, ",pkgversions)
load_file("utils.hoc")
load_file("ObjectClass.hoc")

/*

FormatFile

f = new FormatFile(String file)

Sets up a new FormatFile that will print objects to file in Matlab format.

f = new FormatFile(String file, String format)

Sets up a new FormatFile that will print objects to file in format,
where format can be "R" or "Matlab".

f = new FormatFile(String file, String format, Structname structname)

Sets up a new FormatFile that will print objects to file in format,
where format can be "R" or "Matlab" and will put every object into a
structure called structname.

f.wopen()
f.aopen()
f.ropen()
f.close()

Similar to File object functions except that they don't take an argument.

f.printdouble("double_name", double)

Prints double to f using the name double_name.

f.printvec("vector_name", vector)

Print vector to file_handle in matlab format with name vector_name.
If vector is longer than the local varible chunk_size, it is split 
into lines of chunk_size elements or less, separated by the matlab line 
continuation symbol "...".  

f.printvec("vector_name", vector, start, end)

Same as above, but only print from index start to index end.  
The start and end indeicies are in the range 0 to vector.size()-1 , 
i.e. the NEURON/C convention rather than Matlab convetion.  They 
will, however, be printed in Matlab convention.

f.printvec("vector_name", vector, start, end, row)

Same as above, but print vector as row of matrix.  Again, indicies
are from 0 to vector.size()-1.

f.printmat("matrix_name", matrix)

Prints matrix to f using the name matrix_name.

f.printlistasmat("matrix_name", list [, init_value ])

Prints list where each element is a vector as a matrix to f using the
name matrix_name.  The vectors can be different lengths.  If they are
different lengths, init_value specifies the value to give elements
that have to be filled in; the default is 0.

f.printstr("string_name", string)

Print string to file_handle in matlab format with name string_name.

f.setprintdouble("parameter_name", parameter_value)
 
Set parameter of paramater_name to parameter_value and print the
parameter name and value to file.

f.setprintstr("string_name", string_value)

set string_name to string_value and print
the string name and value to file.

*/


begintemplate FormatFile

// Public functions
public aopen
public ropen
public wopen
public close
public printdouble
public printvec
public printmat
public printlistasmat
public print_VectorList
public printstr
public setprintdouble
public setprintstr
public printf
public printobj
public printlist
public chooser
public unlink
public f


// Private variables

objref f                                // The file object
strdef format                           // The format type
strdef structname                       // Prefix string if structures present
strdef prefix                           // Prefix string if structures present
strdef filename                         // The file name

strdef openind
strdef closeind
strdef assop
strdef openvec
strdef elsep
strdef closevec
strdef linecont
strdef lineend
strdef quot

strdef tmpstr, rowstr, varname, listname

objref tmpmat, this, utils
objref oc

proc init() {
    utils = new Utils()
    // Args: Filename, Format (R or matlab), structur prefix
    start = 0
    end = 0
    row = 0
    wopened_flag = 0
    
    format = "Matlab"
    if ( numarg() >= 2 ) {
        format = $s2
        if (!(!strcmp(format, "Matlab") || !strcmp(format, "R") || !strcmp(format, "hoc"))) {
            utils.error("FormatFile: unrecognised format specified")
            return
        }
    }
    if (!strcmp(format, "Matlab")) {
        openind = "("
        closeind = ")"
        assop = "="
        openvec = "["
        elsep = " "
        closevec = "]"
        linecont = "..."
        lineend = ";"
        quot="'"
        sprint(filename,"%s.m",$s1)
    }
    if (!strcmp(format, "R")) {
        openind = "["
        closeind = "]"
        assop = "<-"
        openvec = "c("
        elsep = ","
        closevec = ")"
        linecont = ""
        lineend = ";"
        quot="\'"
        sprint(filename,"%s.R",$s1)
    }
    if (!strcmp(format, "hoc")) {
        openind = ""
        closeind = ""
        assop = "="
        openvec = ""
        elsep = " "
        closevec = ""
        linecont = ""
        lineend = ""
        quot="\""
        sprint(filename,"%s.hoc",$s1)
    }
    f = new File(filename)
        
    structflag = 0
    if ( numarg() >= 3 ) {
        structflag = 1
        structname = $s3
        if (!strcmp(format, "Matlab")) {
            sprint(prefix, "%s.", structname)
        }
        if (!strcmp(format, "R")) {
            sprint(prefix, "%s$", structname)
        }
    }
    oc = new ObjectClass()
}

// Open and closing functions inherited from File except that we only
// allow opening without a filename
// 

// For R

proc print_structname () {
    if (!strcmp(format, "R") && (wopened_flag==0) && (structflag==1)) {
        f.printf("%s <- list()%s\n", structname,lineend)
    }
    if (!strcmp(format, "hoc")) {
        initialise_hoc_file()
        wopened_flag = 1
    }

}


func aopen() { local ret
    ret = f.aopen()
    print_structname()  
    return ret
}

func ropen() {
    return f.ropen()
}

func wopen() { local ret
    wopened_flag=0
    ret = f.wopen()
    print_structname()
    return ret
}

func close() {
    return f.close()
}

//
// Utility functions
//

//
// x = min(double a, double b)
// 
// return minimum of a and b
//

func min() {
    if ( $1 < $2 ) { return $1 } else { return $2 }
}

//
// x = max(double a, double b)
// 
// return maximum of a and b
//

func max() {
    if ( $1 > $2 ) { return $1 } else { return $2 }
}


//
// Format-independent printing functions 
// 

//
// printdouble("double_name", double)
// 
proc printdouble() {
    sprint(varname, "%s%s", prefix, $s1) 
    f.printf("%s %s ", varname, assop)
    f.printf("%.10g ", $2)
    f.printf("%s\n",lineend)

}

//
// printvec
// 

proc printvec() {
    //
    // Deal with input arguments
    //
    
    sprint(varname, "%s%s", prefix, $s1) 
    
    if (numarg() == 2) {
        start = 0
        end = $o2.size() - 1
    }

    if (numarg() >= 4) {
        start = $3
        end = $4
        if (start != int(start)) { error("utils.hoc:printmvec(): start value is not an integer")}
        if (end != int(end)) { error("utils.hoc:printmvec(): end value is not an integer") }
        if (end < start ) { error("utils.hoc:printmvec(): end value is lower than start value") }
    }
    
    rowstr = ""
    if (numarg() == 5) {
        row = $5
        sprint(rowstr,"%g,",row + 1)
    }
    
    //     if (!strcmp(format, "Matlab")) {
    
    //     }
    if (!strcmp(format, "R")) {
        f.printf("%s <- c()%s\n",varname,lineend)
        f.printf("%s <- rep(NA,%g)\n",varname,$o2.size(),lineend)
    }
    
    if (!strcmp(format, "hoc")) {
        printvec_hoc(varname, $o2) 
    } else {
        printvec_generic(varname, $o2) 
    }
    
}

proc printvec_generic () { local i, chunk_size,  total_size, pos, name, blockend
    chunk_size = 10             // How many elements per line to print
    block_size = 100            // How many elements per vector to print
    
//    print "row", row, start, end 
    
    //
    // Print out values
    //
    
    // Only print if there are values to print
    if ( (end-start) >= 0 ) {

        // Loop throgh values
        for j = start, end {

            // Split vector into new vector every block_size elements 
            // as long as we're not at the end of the vector
            if (((j-start) % block_size) == 0) {

                // If not at the start of the vector, need to finish old 
                // vector
                if ( j != start ) {
                    f.printf("%s%s\n",closevec,lineend) 
                }
                f.printf("%s%s%s%g:%g%s %s %s ", varname, openind, rowstr, j+1, min((j+block_size),end+1),closeind, assop, openvec)
            } else {
                // Print separator if not end of block
                f.printf("%s", elsep)
            }
            
            // Newline and ... every chunk_size elements
            if ( ( ( j - start  ) % chunk_size ) == 0 ) {
                f.printf("%s\n", linecont) 
            }
            
            // Print out element itself 
            f.printf("%.10g", $o2.x(j))
        }
        
        // Print end of vector
        f.printf("%s%s\n",closevec,lineend) 
    }
}

proc printvec_hoc() { 
    f.printf("scan_vec_()\n")
    f.printf("%s\n", $s1)
    f.printf("%g\n", $o2.size())
    $o2.printf(f,"%.10g ")
}

//
// printmat
//
proc printmat () { local i, j, nrows, ncols
    sprint(varname, "%s%s", prefix, $s1) 
    
    if (!strcmp(format, "hoc")) {
        printmat_hoc(varname, $o2)
    } else { 
        if (numarg() == 2) {
            nrows = $o2.nrow()
            ncols = $o2.ncol()
        }
        
        // Naughty communication to printvec_generic
        start = 0
        end = ncols-1
        
        // 
        // Need to initialise matrix
        // 
        if (!strcmp(format, "R")) {
            f.printf("%s <- matrix(0, nrow=%g, ncol=%g)%s\n", varname, nrows, ncols,lineend)
        }
        
        //
        // Print out values
        //
        
        // Only print if there are values to print
        if ( (nrows >= 1)  && (ncols >=1) ) {
            
            // Loop throgh rows
            for i = 0, nrows-1 {
                
                // Use printvec_generic
                sprint(rowstr,"%g,",i + 1)
                printvec_generic(varname,$o2.getrow(i))
            }
        }
    }
}

proc printmat_hoc() { 
    f.printf("scan_mat_()\n")
    f.printf("%s\n", $s1)
    $o2.fprint(f,"%.10g ")
}

//
// printlistasmat
// 
proc printlistasmat () { local i, ncol, init_value
    //
    // Deal with input arguments
    //
    if (numarg()==3) {
        init_value = $3 
    } else {
        init_value = 0
    }
    //
    // Make a matrix big enough
    //
    if ($o2.count() > 0) {
        ncol = 0
        for i=0,$o2.count()-1 {
            if ($o2.object(i).size()>ncol) {
                ncol = $o2.object(i).size()
            }
        }
        tmpmat = new Matrix($o2.count(), ncol)
        //
        // Fill it with the vectors in the list
        //
        for i=0,$o2.count()-1 {
            tmpmat.setrow(i, init_value)
            tmpmat.setrow(i, $o2.object(i))
        }
        printmat($s1, tmpmat)
    }
}

//
// f.printstr("string_name", string)
// 

proc print_VectorList () { local i
    f.printf("%s%s %s ", prefix, $s1, assop)
    if (!strcmp(format, "R")) {
        f.printf("list()\n")
    }
    for i=0, $o2.count()-1 {
        sprint(tmpstr,"%s[[%g]]",$s1,i+1)
        printvec(tmpstr, $o2.object(i))
    }
}

proc printstr () { 
    if (!strcmp(format,"hoc")) {
        f.printf("strdef %s%s\n", prefix, $s1)
    }
    f.printf("%s%s %s ", prefix, $s1, assop)
    f.printf("%s%s%s ", quot,$s2,quot)
    f.printf(" %s\n",lineend)
}


//
// f.setprintdouble("parameter_name", parameter_value)
// 
// set parameter of paramater_name to parameter_value and print
// the parameter name and value to file.
// 

proc setprintdouble () {
    sprint(tmpstr, "%s = %.10g", $s1, $2)
    execute(tmpstr)
    printdouble($s1, $2)
}

//
// f.setprintmstr("string_name", string_value)
// 

proc setprintstr () {
    sprint(tmpstr, "strdef %s", $s1)
    execute(tmpstr)
    sprint(tmpstr, "%s = \"%s\"", $s1, $s2)
    execute(tmpstr)
    printstr($s1, $s2)
}

//
// Sometimes useful to have limited printf fucntionality
// 

proc printf() {
    if (numarg() == 2) { f.printf($s1,$s2) }
    if (numarg() == 1) { f.printf($s1) }
}

// 
// Print regardless of type
// printobj("name", object)
// 
proc printobj() {
    if (argtype(2) == 0) {               // double
        printdouble($s1, $2)
    }
    if (argtype(2) == 1) {               // object
        if (oc.object_is($o2, "Vector")) {
            printvec($s1, $o2)
        }
        if (oc.object_is($o2, "Matrix")) {
            printmat($s1, $o2)
        }
    }
    if (argtype(2) == 2) {               // string
        printstr($s1, $s2)
    }
}

//
// Print a list
// printlist("name", object)
//
proc printlist() {
    //
    // Deal with input arguments
    //
    
    sprint(listname, "%s", $s1) 
    
    if (!strcmp(format, "R")) {
        f.printf("%s <- list()%s\n", listname, lineend)
        for i=0, $o2.count()-1 {
            sprint(tmpstr,"%s[[%d]]", listname, i+1)
            printobj(tmpstr, $o2.o(i))
        }
    } else {
        utils.warning("FormatFile: printlist only supported for R format")
    }

}

proc chooser() { local i 
    sprint(tmpstr,"f.chooser(")
    for i=1,numarg() {
        if (i>=2) { 
            sprint(tmpstr,"%s,", tmpstr)
        }
        sprint(tmpstr,"%s\"%s\"", tmpstr, $si)
    } 
    
    sprint(tmpstr,"%s)",tmpstr)
    execute(tmpstr,this)
    filename = f.getname()
}

proc unlink() {
    f.unlink()
}

proc initialise_hoc_file() {
    f.printf("\
strdef tmp_name_\
strdef tmp_cmd_\
objref tmp_obj_\
")
    f.printf("\
proc scan_vec_() { local i, n\
    ")
    f.printf("\
    getstr(tmp_name_,1)\
    n = fscan()\
    tmp_obj_ = new Vector(n)\
    for i=0,n-1 {\
        tmp_obj_.x(i) = fscan()\
    }\
")    
    f.printf("\
    sprint(tmp_cmd_, \"{objref %%s}\", tmp_name_)\
    execute(tmp_cmd_)\
    ")    
    f.printf("\
    sprint(tmp_cmd_, \"{%%s = tmp_obj_}\", tmp_name_)\
    execute(tmp_cmd_)\
}\
")
    f.printf("\
proc scan_mat_() { local i, j, nrow, ncol\
    ")
    f.printf("\
    getstr(tmp_name_,1)\
    nrow = fscan()\
    ncol = fscan()\
    tmp_obj_ = new Matrix(nrow,ncol)\
    ")
    f.printf("\
    for i=0,nrow-1 {\
        for j=0,ncol-1 {\
            tmp_obj_.x[i][j] = fscan()\
        }\
    }\
")
    f.printf("\
    sprint(tmp_cmd_, \"{objref %%s}\", tmp_name_)\
    execute(tmp_cmd_)\
    ")    
    f.printf("\
    sprint(tmp_cmd_, \"{%%s = tmp_obj_}\", tmp_name_)\
    execute(tmp_cmd_)\
}\
")    
}

endtemplate FormatFile