// Source: https://github.com/fietkiewicz/PointerBuilder
// Description: Utility for creating "setpointer" instructions in NEURON. See README.md for more details.

objref vbox, vbox1a, vbox1b, vbox1c, vbox1d, hbox1, hbox2, hbox3, hbox4, hbox5
objref vbox2a, vbox2b, vbox2c, vbox2d, vbox3

mapHeight = 75

objref pointer_mod, pointer_section, source_mod, source_section
objref pointer_name, source_name
strdef filename, line, s // Strings used for reading mod file
objref file, strobj
strobj = new StringFunctions()
strdef separator1, separator2
strdef variable // Next variable in POINTER list before a comma
strdef default_text

default_text = "NOT ENTERED"

type = 1 // Source type (1 = state variable, 2 = parameter)

vbox = new VBox()
vbox.intercept(1)

hbox4 = new HBox()
hbox4.intercept(1)
xpanel("############")
xlabel("Pointer Settings")
xpanel()
hbox4.intercept(0)
hbox4.map()

hbox1 = new HBox()
hbox1.intercept(1)
vbox1a = new VBox()
vbox1a.intercept(1)
xpanel("############")
xlabel("NMODL file:")
xpanel()
pointer_mod = new TextEditor(default_text, 1, 30)
pointer_mod.map()
xpanel("", 0)
xbutton("            Read .mod file           ", "print pointer_mod.text()")
xpanel()
vbox1a.intercept(0)
vbox1a.map("", 0, 0, 200, mapHeight)

vbox1b = new VBox()
vbox1b.intercept(1)
xpanel("")
xlabel("Name of pointer:")
xpanel()
pointer_name = new TextEditor(default_text, 1, 30)
pointer_name.map()
vbox1b.intercept(0)
vbox1b.map("", 0, 0, 200, mapHeight)

vbox1c = new VBox()
vbox1c.intercept(1)
xpanel("")
xlabel("Section name:")
xpanel()
pointer_section = new TextEditor(default_text, 1, 30)
pointer_section.map()
vbox1c.intercept(0)
vbox1c.map("", 0, 0, 200, mapHeight)

vbox1d = new VBox()
vbox1d.intercept(1)
vbox1d.intercept(0)
vbox1d.map("", 0, 0, 200, mapHeight)

hbox1.intercept(0)
hbox1.map("", 0, 0, 600, mapHeight)

hbox5 = new HBox()
hbox5.intercept(1)
xpanel("############")
xlabel("State/Parameter Settings")
xpanel()
hbox5.intercept(0)
hbox5.map()

hbox2 = new HBox()
hbox2.intercept(1)
vbox2a = new VBox()
vbox2a.intercept(1)
xpanel("")
xlabel("NMODL file:")
xpanel()
source_mod = new TextEditor(default_text, 1, 30)
source_mod.map()
xpanel("")
xbutton("            Read .mod file           ", "print \"button clicked\"")
xpanel()
vbox2a.intercept(0)
vbox2a.map("", 0, 0, 200, mapHeight)

vbox2b = new VBox()
vbox2b.intercept(1)
xpanel("")
xlabel("Name of state/parameter:")
xpanel()
source_name = new TextEditor(default_text, 1, 30)
source_name.map()
vbox2b.intercept(0)
vbox2b.map("", 0, 0, 200, mapHeight)

vbox2c = new VBox()
vbox2c.intercept(1)
xpanel("")
xlabel("Section name:")
xpanel()
source_section = new TextEditor(default_text, 1, 30)
source_section.map()
vbox2c.intercept(0)
vbox2c.map("", 0, 0, 200, mapHeight)

vbox2d = new VBox()
vbox2d.intercept(1)
xpanel("")
xlabel("Type:")
xradiobutton("State variable", "print \"Radio button clicked\"", 1)
xradiobutton("Parameter", "print \"Radio button clicked\"")
xlabel("")
xlabel("")
xlabel("")
xpanel()
vbox2d.intercept(0)
vbox2d.map("", 0, 0, 200, mapHeight)

hbox2.intercept(0)
hbox2.map("", 0, 0, 800, mapHeight)

vbox3 = new VBox()
vbox3.intercept(1)
xpanel("")
xlabel("Actions")
xpanel()
xpanel("", 1)  // easy horizontal
xbutton("View hoc command", "view_window()")
xbutton("Make .hoc file", "make_hoc_file()")
xbutton("Save settings", "save_settings()")
xbutton("Load settings", "load_settings()")
xbutton("Help", "help_window()")

xpanel()
vbox3.intercept(0)
vbox3.map()

vbox.intercept(0)
vbox.map("Pointer Builder", 0, 0, 800, 310)

// Functions


// Set type as state variable
proc radio_state() {
    type = 1
}

// Set type as parameter
proc radio_parameter() {
    type = 2
}

// Save settings
proc save_settings() {
    // Create file
    objref file
    file = new File()
    file.chooser("w", "Save settings: Select file or", "", "Save")
    if (file.chooser()) {
        file.wopen(file.getname())
        file.printf("%s\n", pointer_section.text())
        file.printf("%s\n", pointer_name.text())
        file.printf("%s\n", pointer_mod.text())
        file.printf("%s\n", source_section.text())
        file.printf("%s\n", source_name.text())
        file.printf("%s\n", source_mod.text())
        // Cleanup
        file.flush()
        file.close()
    }
}

// Load settings
proc load_settings() {
    file = new File()
    file.chooser("r", "Select a file or", "", "Open")
    if (file.chooser()) {
        file.gets(line)
        strobj.left(line, strobj.len(line) - 1) // Remove newline
        pointer_section.text(line)
        file.gets(line)
        strobj.left(line, strobj.len(line) - 1) // Remove newline
        pointer_name.text(line)
        file.gets(line)
        strobj.left(line, strobj.len(line) - 1) // Remove newline
        pointer_mod.text(line)
        file.gets(line)
        strobj.left(line, strobj.len(line) - 1) // Remove newline
        source_section.text(line)
        file.gets(line)
        strobj.left(line, strobj.len(line) - 1) // Remove newline
        source_name.text(line)
        file.gets(line)
        strobj.left(line, strobj.len(line) - 1) // Remove newline
        source_mod.text(line)
        file.close()
    }
}

// Get pointer mod file name and pointer variables
proc get_pointer_mod() {
    // Get pointer mod file name
    file = new File()
    file.chooser("r", "Select a mod file or", "", "Open")
    if (file.chooser()) {
        strobj.tail(file.getname(), file.dir(), filename)
        strobj.head(filename, ".mod", pointer_mod)

        // Find line with POINTER and get the variables
        foundPointer = 0
        while (!file.eof() && !foundPointer) {
             file.gets(line)
             if (strobj.tail(line, "POINTER ", s) > -1) {
                    foundPointer = 1 // Terminate loop because we found the POINTER line
             }
        }
        file.close()
        strobj.left(s, strobj.len(s) - 1) // Remove newline
        variable_window()
    }
}

// Get source mod file name
proc get_source_mod() {
    file = new File()
    file.chooser("r", "Select a mod file or", "*.mod", "Open")
    if (file.chooser()) {
        strobj.tail(file.getname(), file.dir(), filename)
        strobj.head(filename, ".mod", source_mod)
    }
}

// Create hoc file using string of pointer variables
proc make_hoc_file() {
    // Create file
    objref file
    file = new File()
    file.chooser("w", "Choose existing file or", "", "Create file")
    if (file.chooser()) {
        file.wopen(file.getname())
        if (type == 1) {
            // Use syntax for state variable
            file.printf("setpointer %s.%s_%s(0.5), %s.%s_%s(0.5)\n", pointer_section.text(), pointer_name.text(), pointer_mod.text(), source_section.text(), source_name.text(), source_mod.text())
        } else {
            // Use syntax for parameter
            file.printf("setpointer %s.%s_%s(0.5), %s_%s", pointer_section.text(), pointer_name.text(), pointer_mod.text(), source_name.text(), source_mod.text())
        }
        // Cleanup
        file.flush()
        file.close()
    }
}

// Display hoc code in window
objref viewbox
proc view_window() {
    viewbox = new VBox()
    viewbox.intercept(1)    //all following creations go into the box
    xpanel("X", 0)
    xlabel("Use the following setpointer command:")
    xlabel(" ")
    printf("\nUse the following setpointer command:\n")
    strdef output
    if (type == 1) {
        // Use syntax for state variable
        sprint(output, "setpointer %s.%s_%s(0.5), %s.%s_%s(0.5)", pointer_section.text(), pointer_name.text(), pointer_mod.text(), source_section.text(), source_name.text(), source_mod.text())
    } else {
        // Use syntax for parameter
        sprint(output, "setpointer %s.%s_%s(0.5), %s_%s", pointer_section.text(), pointer_name.text(), pointer_mod.text(), source_name.text(), source_mod.text())
    }
    xlabel(output)
    xlabel(" ")
    xlabel("NOTE: This text is also available in the console window for copying. ")
    printf(output)
    printf("\n")
    xpanel()
    viewbox.intercept(0)    //ends intercept mode
    viewbox.map()       //draw the box and its contents
}

// Help window
objref helpbox
strdef helpseparator
helpseparator = "--------------------------------------------------------------------------------------------------------------------------------"
proc help_window() {
    helpbox = new VBox()
    helpbox.intercept(1)    //all following creations go into the box
    xpanel("X", 0)

    xlabel("Contents: ")
    xlabel("1. Description ")
    xlabel("2. The 'setpointer' statement")
    xlabel("3. Pointers in NMODL")
    xlabel("4. State variables and parameters in NMODL")
    xlabel(" ")
    xlabel(helpseparator)

    xlabel("1. Description ")
    xlabel(helpseparator)
    xlabel("PointerBuilder.hoc is a tool that assists the user in constructing a 'setpointer' statement. Additional support is available at this site: ")
    xlabel("                                                                       https://github.com/fietkiewicz/PointerBuilder ")
    xlabel("")
    xlabel("The following are required fields. (See code examples in the sections that follow.)")
    xlabel("1. NMODL file with pointer: ")
    xlabel("     Name of a .mod file that contains the pointer. (Example: myMod1)")
    xlabel("2. Name of pointer variable: ")
    xlabel("     Name of the variable declared in the NMODL file using the POINTER command. (Example: xPointer)")
    xlabel("3. Section name with pointer: ")
    xlabel("     Name of section which was used to insert the mechanism containing the pointer. (Example: mySection1)")
    xlabel("4. NMODL file with state variable or parameter: ")
    xlabel("     Name of a .mod file that contains the source state variable or paramater. (Example: myMod2)")
    xlabel("5. Name of state variable or parameter: ")
    xlabel("     Name of state variable or parameter which the pointer will reference. (Example: x)")
    xlabel("6. Section name with source variable or parameter: ")
    xlabel("     Name of section which was used to insert the mechanism containing the state variable or parameter. (Example: mySection2)")
    xlabel("7. Type: ")
    xlabel("     Selection of type determines 'setpointer' syntax which is defferent for a state variable vs. a parameter.")
    xlabel("")
    xlabel("The following action buttons are available:")
    xlabel("1. Make .hoc file: Saves hoc statement for 'setpointer' instruction to a file.")
    xlabel("2. View hoc command: Displays hoc statement for 'setpointer' instruction on the screen.")
    xlabel("3. Save settings: Saves settings to a file for later use.")
    xlabel("4. Load settings: Loads settings from a previously saved file.")
    xlabel("5. Help: Displays this window.")
    xlabel(" ")
    xlabel(helpseparator)

    xlabel("2. The 'setpointer' statement")
    xlabel(helpseparator)
    xlabel("A 'setpointer' statement requires sections that must be created and mechanisms that must be inserted. This is demonstracted by ")
    xlabel("the .hoc code segment below using two sections and mechanisms from the NMODL examples shown on the right. PointerBuilder ")
    xlabel("can generate the last line that contains 'setpointer'. The user must copy it into their own .hoc file. ")
    xlabel(" ")
    xlabel("Sample hoc code:")
    xlabel(" ")
    xlabel("// Create sections and insert mechanisms")
    xlabel("create mySection1 ")
    xlabel("access mySection1 ")
    xlabel("insert myMod1 ")
    xlabel("create mySection2 ")
    xlabel("access mySection2 ")
    xlabel("insert myMod2 ")
    xlabel(" ")
    xlabel("// Set pointer (must be manually entered by the user)")
    xlabel("setpointer mySection1.xPointer_myMod1(0.5), mySection2.x_myMod2(0.5)")
    xlabel(" ")
    xlabel("NOTE: Sample code shown here is available as a file provided with the PointerBuilder app. ")
    xlabel(" ")
    xlabel(helpseparator)

    xlabel("3. Pointers in NMODL")
    xlabel(helpseparator)
    xlabel("The following NMODL code contains a POINTER: ")
    xlabel(" ")
    xlabel("NEURON {")
    xlabel("    SUFFIX myMod1")
    xlabel("    POINTER xPointer")
    xlabel("}")
    xlabel("")
    xlabel("ASSIGNED { xPointer }")
    xlabel(" ")
    xlabel(helpseparator)

    xlabel("4. State variables and parameters in NMODL")
    xlabel(helpseparator)
    xlabel("Each NMODL pointer can point to either a state variable or a parameter. ")
    xlabel(" ")
    xlabel("The following NMODL code demonstrates a state variable: ")
    xlabel(" ")
    xlabel("NEURON {")
    xlabel("    SUFFIX myMod2")
    xlabel("}")
    xlabel("STATE { x }")
    xlabel(" ")
    xlabel("The following NMODL code demonstrates a parameter: ")
    xlabel(" ")
    xlabel("NEURON {")
    xlabel("    SUFFIX myMod2")
    xlabel("}")
    xlabel("PARAMETER { x }")

    xpanel(1)
    helpbox.intercept(0)    //ends intercept mode
    helpbox.map()       //draw the box and its contents
}

// Variable selection window
objref variablebox
strdef button_command
proc variable_window() {
    variablebox = new VBox()
    variablebox.intercept(1)    //all following creations go into the box
    xpanel("X", 0)
    xlabel("Choose one of the following POINTER variables:")

    // Remove leading spaces before first variable
    while (strobj.substr(s, " ") == 0) {
         strobj.right(s, 1)
    }

    // Loop while string has a comma
    while (strobj.head(s, ",", variable) > -1) {
         sprint(button_command, "pointer_name = \"%s\" variablebox.unmap()", variable)
         xbutton(variable, button_command)
         // Remove comma
         index = strobj.substr(s, ",")
         strobj.right(s, index + 1)
         // Remove leading spaces before next variable
         while (strobj.substr(s, " ") == 0) {
              strobj.right(s, 1)
         }
    }

    // Remove leading spaces before next variable
    while (strobj.substr(s, " ") == 0) {
         strobj.right(s, 1)
    }
    sprint(button_command, "pointer_name = \"%s\" variablebox.unmap()", s)
    xbutton(s, button_command)

    xpanel()
    variablebox.intercept(0)    //ends intercept mode
    variablebox.map()       //draw the box and its contents
}