/* Create seperate lists for dendrites (basal and apical), soma and axon
   sections for each morphology file */

// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

begintemplate Morph

public name
public full_name
public filename
public dend_name
public dend_basal_name
public numbasal
public dend_apical_name
public numapical
public soma_name
public axon_name
public origin_name
public trunk_criterion // number of branches a trunk section must appear in

objref dend_basal_name,dend_apical_name
objref dend_name,soma_name,axon_name
strdef name,full_name,filename,origin_name

proc init() { local i,j
	 name=$s1
	 full_name=$s2
	 sprint(filename,"../cells/morphologies/%s",$s3)
	 origin_name=$s4
	 soma_name=new List() // for future 'or' enabling
	 soma_name.append(new String($s5))
	 axon_name=new List()
	 axon_name.append(new String($s6))
	 dend_name=new List()
	 dend_name.append(new String($s7))
	 numbasal=$8
	 i=8
	 dend_basal_name=new List()
	 for j=0,numbasal-1 {
	 	 i+=1
	 	 dend_basal_name.append(new String($si))
	 }
	 i+=1
	 numapical=$i
	 dend_apical_name=new List()
	 for j=0,numapical-1 {
	 	 i+=1
	 	 dend_apical_name.append(new String($si))
	 }
	 i+=1
	 trunk_criterion=$i
}
endtemplate Morph

// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

begintemplate Channel_Parameter

public param_name
public name
public selsec_prefix
public var_name				   // name of the global variable used in the control and in the parameter files
//public asgn_name			   // name used when assigning the paramter with a value
public var_value			   // value for the parameter
public default_var_value	   // default value
public user_access			   // 0=no user access (usually whole cell parameters); 1=user access
public type				// 0=other; 1=conductance; 2=membrane parameter
public assignment 			   // expression for paramter value assignment as a function of distance from soma
public x1,x2,c1,c2,c3			// in case the assignment is a function, the arguments are kept here
public xs1, xs2
public arguments	// string containing all relvant arguments to initiate an instance of this template
public selsec_private_varnames	// for each selected section the variable names must be distinguishable 

external selsec

strdef name,var_name,asgn_name,default_value
strdef param_name,assignment, xs1, xs2, c1, c2, c3
strdef cmd, arguments, selsec_prefix

proc init() { local n
	selsec_prefix = ""
	n = numarg()
	user_access=$1
	type=$2
	param_name=$s3
	sprint(arguments,"%d,%d,param.param_name",user_access,type)
	if (user_access==0) { // no user access (usually a parameter affecting entire cell)
		name=""
		var_name=""
		var_value=$4
		assignment=$s5
		sprint(arguments,"%s,param.var_value,param.assignment",arguments)
	} else { // user access
		name=$s4
		var_name=$s5
		var_value=$6
		assignment=$s7
		sprint(arguments,"%s,param.name,param.var_name,param.var_value,param.assignment",arguments)
		if (n > 7) {
			x1type = argtype(8) 
			if (x1type == 0) {
				x1 = $8
				sprint(xs1,"%g",x1)
			} else if (x1type == 2) {
				xs1 = $s8
			}
			x2type = argtype(9) 
			if (x2type == 0) {
				x2 = $9
				sprint(xs2,"%g",x2)
			} else if (x2type == 2) {
				xs2 = $s9
			}
			c1 = $s10
			c2 = $s11
			c3 = $s12
			sprint(arguments,"%s, param.xs1, param.xs2, param.c1, param.c2, param.c3",arguments)
		}
	}
	default_var_value=var_value
}

proc selsec_private_varnames() {
	selsec.sec selsec_prefix = secname()
	sprint(selsec_prefix, "%s_", selsec_prefix)
	if (user_access) {
		sprint(var_name,"%s%s",selsec_prefix, var_name)
		sprint(cmd,"%s = %g",var_name,var_value)
		execute(cmd)
	}
}

endtemplate Channel_Parameter

// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

begintemplate Channel

public name
public numparams
public paramlist
public add_parameter
public inserted
public conductance_id

objref paramlist
strdef name

proc init() {
	 name=$s1
	 numparams=0
	 paramlist=new List()
	 inserted=0
	 conductance_id=-1	 // the parameter id that represents channel's conductance
}

proc add_parameter() {
	 paramlist.append($o1)
//	 if (paramlist.object(numparams).conductance && paramlist.object(numparams).user_access) {
	 if (paramlist.object(numparams).type==1) { // conductance
	 	conductance_id=numparams
	 }
	 numparams+=1
}

endtemplate Channel

// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

begintemplate Subtree

public name
public sec_list
public secref
public numchannels
public channlist
public add_channel
public sign	// -1=basal; 1=apical
public parent_subtree	// just for selsec
public selsec_subtree_destroy

objref channlist,channel,sec_list, secref, str, parent_subtree
strdef name

proc init() { local strcheck, n
	n = numarg()
	 // subtree name; reference to its section list; pairs of: ion,current
	str=new StringFunctions()
	sec_list=new SectionList()
	if (n == 1) { // seclist subtree
		parent_subtree = $o1
		name = parent_subtree.name
		secref = new SectionRef()
		sec_list.append()
		parent_subtree.sec_list.remove()
	} else if (n == 2) { // regular subtree
		name=$s1
		sec_list=$o2
	}
	sign=check_sign()	// check if this happens to be a basal subtree
	numchannels=0
	channlist=new List()
}

proc add_channel() {
	 channel=$o1
	 channlist.append(channel)
	 numchannels+=1
}

func check_sign() {
	strcheck=str.substr(name,"basal")
	if (strcheck==-1) { strcheck=str.substr(name,"Basal")
	} else { return -1 }
	if (strcheck==-1) { strcheck=str.substr(name,"BASAL")
	} else { return -1 }
	if (strcheck==-1) { return 1
	} else { return -1 }
}

proc selsec_subtree_destroy() {
	secref.sec parent_subtree.sec_list.append()
}

endtemplate Subtree

// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

begintemplate Channel_Layout

public name
public full_name
public filename
public numsubtrees
public subtree_list
public add_subtree
public loaded
public g_pas_name
public e_pas_name
public current_names
public ion_names
public numions
public axon_type

objref subtree_list,subtree
objref current_names,ion_names
strdef name,full_name,filename
strdef g_pas_name,e_pas_name

proc init() { local n,last,j,i
	 n=numarg()
	 name=$s1
	 full_name=$s2
	 sprint(filename,"../cells/channels/%s",$s3)
	 g_pas_name=$s4
	 e_pas_name=$s5
	 axon_type=$6
	 last=6 // last argument number before ions and currents
	 numions=(n-last)/2
	 current_names=new List()
	 ion_names=new List()
	 for j=0,numions-1 {
	 	 i=last+2*j+1
		 ion_names.append(new String($si))
		 i+=1
		 current_names.append(new String($si))
	 }
	 subtree_list=new List()
	 numsubtrees=0
	 loaded=0 // channel file not loaded
}

proc add_subtree() {
	 subtree=$o1
	 subtree_list.append(subtree)
	 numsubtrees+=1
}


proc print_to_file() {
}

endtemplate Channel_Layout

// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

// Morphology file specific section names
// ----------------------------------------------------------------------------------

proc prepare_morphlist() {
	 // name; display name; file name; origin; soma; axon; dendrite; num of basal names; basals; num apical names; apicals; trunk criterion
	 // (trunk criterion: number of branches a section appears in in order to count as belonging to a trunk)
	 morphlist=new List()
	 morphlist.append(new Morph("cable","Cable","cable.hoc","soma","soma","XX",".*dend.*",0,1,".*dend.*",0))
	 morphlist.append(new Morph("ri04","Spruston1","ri04.nrn","somaA",".*soma.*",".*axon.*",".*dend.*",3,".*dendA1.*",".*dendA2.*",".*dendA3.*",2,".*dendA4.*",".*dendA5.*",13))
	 morphlist.append(new Morph("ri05","Spruston 2","ri05.nrn","somaA",".*soma.*",".*axon.*",".*dend.*",3,".*dendA1.*",".*dendA2.*",".*dendA3.*",1,".*dendA4.*",13))
	 morphlist.append(new Morph("ri06","Spruston 3","ri06.nrn","somaA",".*soma.*",".*axon.*",".*dend.*",4,".*dendA1.*",".*dendA2.*",".*dendA3.*",".*dendA4.*",1,".*dendA5.*",10))
	 morphlist.append(new Morph("Purk-vetter1","Purkinje V1","Purk-vetter1.hoc","soma","soma",".*axon.*",".*dend.*",0,1,".*dend.*",10))
//	 morphlist.append(new Morph("n160","Gasparini","n160.nrn","soma[5]",".*soma.*",".*axon.*",".*al.*",1,".*basal.*",1,".*apical.*",13))
	 morphlist.append(new Morph("n400","Seminar","n400ns_su133_07.hoc","soma[2]",".*soma.*",".*axon.*",".*dend.*",0,1,".*dend.*",13))
//	 morphlist.append(new Morph("n160","Haifa","n160_mod.nrn","soma[2]",".*soma.*",".*axon.*",".*al.*",1,".*basal.*",1,".*apical.*",13))
	 morphlist.append(new Morph("602c","Roth 1","602c.hoc","soma",".*soma.*",".*axon.*",".*dend.*",0,1,".*dend.*",13))
	 morphlist.append(new Morph("822","Roth 2","822.hoc","soma",".*soma.*",".*axon.*",".*dend.*",0,1,".*dend.*",5))
	 morphlist.append(new Morph("913c","Roth 3","913c.hoc","soma",".*soma.*",".*axon.*",".*dend.*",0,1,".*dend.*",11))
	 morphlist.append(new Morph("n123","Poirazi","n123.hoc","soma","soma",".*axon.*",".*dend.*",0,1,".*dend.*",13))
	 morph_select(SLCT_MORPH)
}

proc prepare_layoutlist() {
	 layoutlist=new List()
	 layoutlist.append(new Channel_Layout("Cable","Cable","cable.hoc","g_pas","e_pas",0,"na_ion","ina","k_ion","ik"))
//	 layoutlist.append(new Channel_Layout("Gasparini","Gasparini et al. 2005","Gasparini.hoc","g_pas","e_pas",1,"na_ion","ina","k_ion","ik","hd_G","i_hd_G"))
	 layoutlist.append(new Channel_Layout("Migliore2","Migliore et al.","Migliore_modified.hoc","g_pas","e_pas",1,"na_ion","ina","k_ion","ik","hd_G","i_hd_G"))
//	 layoutlist.append(new Channel_Layout("Haifa","Haifa (Migliore)","Haifa.hoc","g_pas","e_pas",1,"na_ion","ina","k_ion","ik"))
//	 layoutlist.append(new Channel_Layout("Poirazi","Poirazi","Poirazi.hoc","gl_hha2_P","el_hha2_P",1,"ca_ion","ica","Ca_ion","iCa","k_ion","ik","na_ion","ina"))
//	 layoutlist.append(new Channel_Layout("Migliore","Migliore","Migliore.hoc","g_pas","e_pas",1,"na_ion","ina","k_ion","ik","hd_M","i_hd_M"))
//	 layoutlist.append(new Channel_Layout("Migliore_iso","Migliore ISO","Migliore_iso.hoc","g_pas","e_pas",1,"na_ion","ina","k_ion","ik"))
//	 layoutlist.append(new Channel_Layout("Vetter","Vetter et al. 2001","Vetter.hoc","g_pas","e_pas",1,"na_ion","ina","k_ion","ik"))
//	 layoutlist.append(new Channel_Layout("Mainen","Mainen & Sejnowski","Mainen.hoc","g_pas","e_pas",2,"na_ion","ina","k_ion","ik","ca_ion","ica"))
//	 layoutlist.append(new Channel_Layout("Golding","Golding et al. 2001","Golding.hoc","g_pas","e_pas",2,"na_ion","ina","k_ion","ik"))
//	 layoutlist.append(new Channel_Layout("Golding3","3Golding et al. 2001","Golding3.hoc","g_pas","e_pas",3,"na_ion","ina","k_ion","ik"))
//	 layoutlist.append(new Channel_Layout("Scale","Scaling","Scale.hoc","g_pas","e_pas",1,"na_ion","ina","k_ion","ik"))
	 layout_select(SLCT_LAYOUT)
}

proc channel_write_parameters() { local i,j,k
	 pfile=$o1
	 for i=0,LAYOUT.numsubtrees-1 {
	 	 subtree=LAYOUT.subtree_list.object(i)
		 for j=0,subtree.numchannels-1 {
		 	 channel=subtree.channlist.object(j)
			 for k=0,channel.numparams-1 {
			 	 param=channel.paramlist.object(k)
				 if (param.user_access) {
				 	pfile.printf("%s=%g\n",param.var_name,param.var_value)
				 }
			 }
		 }
	 }
}

// called from simulation.hoc
proc current_balance() { local i,j
	current_sum=0
	finitialize($1)
	fcurrent()
	for i=0,LAYOUT.numsubtrees-1 {
		subtree_balance(LAYOUT.subtree_list.object(i))
	}
	for i = 0, NSELSECS-1 {
		subtree_balance(selsec_subtree_list.object(i))
	}
}

proc subtree_balance() {
	subtree = $o1
	forsec subtree.sec_list { // loop subtree sections
		for (x,0) {
			sprint(cmd,"current_sum = %s(x)*v(x)",LAYOUT.g_pas_name)
			execute(cmd)
			for j=0,LAYOUT.numions-1 {
				if (ismembrane(LAYOUT.ion_names.object(j).s)) {
					sprint(cmd,"current_sum += %s(x)",LAYOUT.current_names.object(j).s)
					execute(cmd)
				}
			}
			sprint(cmd,"%s(x) = current_sum / %s(x)",LAYOUT.e_pas_name,LAYOUT.g_pas_name)
			execute(cmd)
			fcurrent()
		}
	}
}
// =====================================================================

proc subtree_channel_update() { local j, k, id, g
	subtree = $o1
	for j=0,subtree.numchannels-1 { // loop subtree channels
		channel=subtree.channlist.object(j)
		// update user accessible variable values
		for k=0,channel.numparams-1 {
	 		param=channel.paramlist.object(k)
			if (param.user_access) {
				sprint(cmd,"param.var_value=%s",param.var_name)
				execute(cmd)
			}
		}
		// if conductance of a channel <= 0, uninsert it and if it was <=0 and now is >0, re-insert it
		id=channel.conductance_id // conductance parameter id
		if (id>=0) { // if this channel has a conductance parameter (just in case...)
			g=channel.paramlist.object(id).var_value
			// if conductance is zero or smaller and channel is inserted, uninsert it
			if (channel.inserted && g<=0) {
				forsec subtree.sec_list { // loop subtree sections
					sprint(cmd,"uninsert %s",channel.name)
					execute(cmd)
				}
				channel.inserted=0
			} else if (!channel.inserted && g>0) {
				forsec subtree.sec_list { // loop subtree sections
					sprint(cmd,"insert %s",channel.name)
					execute(cmd)
					// update all non-user access paramters
					for k=0,channel.numparams-1 {
						param=channel.paramlist.object(k)
						if (!param.user_access) {
							execute(param.assignment)
						}
					}
				}
				channel.inserted=1
			}
		}
		// update user accessible parameters
		if (channel.inserted) {
			for k=0,channel.numparams-1 {
				param=channel.paramlist.object(k)
				if (param.user_access) {
					forsec subtree.sec_list { // loop subtree sections
							execute(param.assignment)
					}
				}
			}
		}
	}
}

proc subtree_channel_restore() { local j, k
	subtree = $o1
	for j=0,subtree.numchannels-1 { // loop subtree channels
		channel=subtree.channlist.object(j)
		// update user accessible variable values
		for k=0,channel.numparams-1 {
			param=channel.paramlist.object(k)
			if (param.user_access) {
				sprint(cmd,"%s=param.var_value",param.var_name)
				execute(cmd)
			}
		}
	}
}

proc channel_update_parameters() { local ok, i
	if (!LAYOUT.loaded) {
		xopen(LAYOUT.filename)
		LAYOUT.loaded=1
	}
	ok=$1
	if (ok) {
		UNBALANCED=1 // flag signaling that cell may be unbalanced and will be balanced upon simulation run
		for i = 0, LAYOUT.numsubtrees-1 { // loop subtrees
			subtree=LAYOUT.subtree_list.object(i)
			subtree_channel_update(subtree)
		}	
	} else {
		for i=0,LAYOUT.numsubtrees-1 { // loop subtrees
			subtree=LAYOUT.subtree_list.object(i)
			subtree_channel_restore(subtree)
		}
	}
}

// ===================================================================================

proc channel_reset_all() { local i,j,k,ok,command,type,go
	ok=$1 // regularly gets either 0 or 1 from RIP/RDP check; 2 signals initial default reset
	if (ok) {
		command=$2 // 1=default; 2=zero
		type=$3 // 1=conductance; 2=memebrane
		if (ok!=2) {
			dbox=new VBox()
			go=dbox.dialog("Are you sure you want to reset ALL channel parameters?","Yes","No")
		} else { go=1 } // read_default_parameters() in configuration.hoc
		if (go) {
			for i=0,LAYOUT.numsubtrees-1 {
				subtree=LAYOUT.subtree_list.object(i)
				for j=0,subtree.numchannels-1 {
					channel=subtree.channlist.object(j)
					for k=0,channel.numparams-1 {
						param=channel.paramlist.object(k)
						if (param.user_access && param.type==type) { // conductance/membrane
							if (command==1) { param.var_value=param.default_var_value
							} else if (command==2) { param.var_value=0 }
							sprint(cmd,"%s=param.var_value",param.var_name)
							execute(cmd)
						}
			 		}
		 		}
	 		}
	 		channel_update_parameters(1)
		}
	}
}

proc channel_reset() { local ok,command
	ok=$1
	if (ok) {
		param=$o2
		command=$3 // type: 1=reset to default value; 2=reset to zero
		if (param.user_access) {
			if (command==1) { param.var_value=param.default_var_value
			} else if (command==2) { param.var_value=0 }
			sprint(cmd,"%s=param.var_value",param.var_name)
			execute(cmd)
		}
	 	channel_update_parameters(1)
	}
}

// plot all channel conductances of a section list
proc scan_subtree_channels() { local j, k, erase, type
	erase = $1
	type = $2	// conductance: 1 = density; 0 = kinematics
	subtree = $o3
	if (erase) { graphItem.erase_all() }
	xmax=0
	xmin=0
	ymax=0
	for j=0,subtree.numchannels-1 {
		channel=subtree.channlist.object(j)
		if (channel.inserted) {
			for k=0,channel.numparams-1 {
				param=channel.paramlist.object(k)
				if (param.user_access && param.type==type) {
					plot_subtree_channels(param.name,param.param_name,subtree.sec_list,subtree.sign,j+1) // plots.hoc
				}
			}
		}
	}
//	graphItem.exec_menu("View = plot")
//	graphItem.size(0,xmax,0,ymax*1.5)
	graphItem.size(-300,900,0,ymax*1.1)
}

proc plot_tree() { local i, type
	type = $1
	graphItem.erase_all()
	for i=0,LAYOUT.numsubtrees-1 {
		subtree=LAYOUT.subtree_list.object(i)
		scan_subtree_channels(0, type, subtree) // don't erase the graph, plot subtree over subtree
	}
	for i=0,NSELSECS-1 {
		subtree=selsec_subtree_list.object(i)
		scan_subtree_channels(0, type, subtree) // don't erase the graph, plot subtree over subtree
	}
}

proc channel_conductance_control_panel() { local i, j, k
	ch1box=new VBox()
	ch1box.intercept(1)
	xpanel("Buttons",1)
	xbutton("all default","channel_reset_all(RIP_check(),1,1)")
	xbutton("all zero","channel_reset_all(RIP_check(),2,1)")
	xbutton("plot g","plot_tree(1)")
	xbutton("plot kinematics","plot_tree(0)")
	xpanel()	
	xpanel("Fields")
	for i=0, LAYOUT.numsubtrees-1 {
		subtree = LAYOUT.subtree_list.object(i)
		xlabel(subtree.name)
		for j=0, subtree.numchannels-1 {
			channel=subtree.channlist.object(j)
			for k=0, channel.numparams-1 {
				 param=channel.paramlist.object(k)
				 if (param.user_access && param.type!=2) { // not a membrane parameter
					xvalue(param.name,param.var_name,1,"channel_update_parameters(RIP_check())")
				}
			}
		}
	}
	xpanel()
	ch1box.intercept(0)
	ch1box.map("Channel Conductances")
}

// basal dendrite symmetry option for "Migliore" means that it has the same non-uniform channel distribution as the apical tree
proc membrane_properties_control_panel() { local i,j,k,check
	ch2box=new VBox()
	ch2box.intercept(1)
	strdef cmd0
	xpanel("Buttons",1)
	xbutton("all default","channel_reset_all(RIP_check(),1,2)")
	xbutton("all zero","channel_reset_all(RIP_check(),2,2)")
	xpanel()
	xpanel("Fields")
	for i=0,LAYOUT.numsubtrees-1 {
		subtree=LAYOUT.subtree_list.object(i)
		xlabel(subtree.name)
		check = 0
		for j=0,subtree.numchannels-1 {
			channel=subtree.channlist.object(j)
			for k=0,channel.numparams-1 {
				 param=channel.paramlist.object(k)
				 if (param.user_access && param.type==2) { // membrane parameter
					xvalue(param.name,param.var_name,1,"channel_update_parameters(RIP_check())")
					check += 1
				 }
			}
		}
		if (!check) { xlabel("  No membrane parameters") }
	}
	xpanel()
	ch2box.intercept(0)
	ch2box.map("Membrane Properties")
}
// ===================================================================================
// create section lists
// --------------------
proc create_section_lists() { local i
	xopen(MORPH.filename)
	ORIGIN_NAME=MORPH.origin_name
	sprint(cmd,"../cells/axons/axon%d.hoc",LAYOUT.axon_type)
	xopen(cmd)
	create_axon()
	dAREA=0
	dLENG=0
	dNSEC=0
	dNSEG=0
	dMAXDIST=0
	NUMSPINES=NSYNS		// assume a spine for each synapse
	dendrite_section_list=new SectionList()
	dendrite_basal_section_list=new SectionList()
	dendrite_apical_section_list=new SectionList()
	soma_section_list=new SectionList()
	axon_section_list=new SectionList()
	strdef scname
	scname=MORPH.dend_name.object(0).s
	forsec scname {
		dendrite_section_list.append()
		nseg = int(L/SEGLEN)+1	// default, additional segmentation in section_segmentation() based on Distribution in synapses.hoc
	  	dLENG+=L
	  	dNSEC+=1
	  	for (x,0) {
			dAREA+=area(x)
		}
   	}
	 for i=0,MORPH.numbasal-1 {
	 	scname=MORPH.dend_basal_name.object(i).s
	 	forsec scname { dendrite_basal_section_list.append() }
	 }
	 for i=0,MORPH.numapical-1 {
	 	 scname=MORPH.dend_apical_name.object(i).s
	 	 forsec scname { dendrite_apical_section_list.append() }
	 }
	 scname=MORPH.soma_name.object(0).s
	 forsec scname { soma_section_list.append() }
	 // axon lists are defined in axon.hoc (axon section name remains for now in MORPH)
   	 scname=MORPH.axon_name.object(0).s
	 forsec scname { axon_section_list.append() }
   	 sprint(cmd,"access %s",ORIGIN_NAME)
   	 execute(cmd)
   	 distance()
	 SPIKE=new Spike(0.5)
}

// special additional segmentation of sections (called from main.hoc)
proc section_segmentation() { local xdist
	execute(DISTRIB.special_cmd)	// additional special segmentation synapses.hoc
	// appending section lists changes tree, so a distance() intialization is required
	sprint(cmd,"%s distance()",ORIGIN_NAME)
	execute(cmd)
	forsec dendrite_section_list {
		dNSEG += nseg
	  	for (x,0) {
			xdist=distance(x)
			if (xdist>dMAXDIST) { dMAXDIST=xdist }
		}
	}
}

// prepare vector of NLEAFSEC longest sections
proc sort_leafsecs() { local i
	secL = new Vector()
	leafsec_section_list = new SectionList()
	for i = 0, tree.numbranch-1 {
		branch = tree.branchlist.object(i)
		branch.last_section.sec secL.append(L)
	}
	secLsorti = new Vector()
	secL.sortindex(secLsorti)
	if (secLsorti.size > NLEAFSECS) {
		secLsorti.remove(0, tree.numbranch-1 - NLEAFSECS) // leave longest NLEAFSECS sections
	}
	for i = 0, NLEAFSECS-1 {
		branch = tree.branchlist.object(secLsorti.x(i))
		branch.last_node.long_leafsec = 1	// mark this node as belonging to a  long leafsec
	}
}

// additional section lists that take branch structure into consideration (e.g. apical trunk)
proc branch_dependent_section_lists() { local i,j,nmbrnch
	criterion=$1 // criterion for number of branches a section appears in to count as a trunk
	dendrite_apical_trunk_section_list=new SectionList()
	dendrite_apical_obliques_section_list=new SectionList()
	str=new StringFunctions()
	strdef s1,s2
	for i=0,nodelist.count()-1 {
		nodelist.object(i).sec.sec {
			nmbrnch=nodelist.object(i).branchlist.count()
			// check to see if this is the apical tree 
			for j=0,MORPH.numapical-1 {
				scname=MORPH.dend_apical_name.object(j).s
				// check if section belongs to apical tree
				ifsec scname {
				// if a section appears in more than 'criterion' branches, then it belongs to the trunk
					if (nmbrnch>criterion) {
						dendrite_apical_trunk_section_list.append()
						nodelist.object(i).main_trunk=1 // update node
					} // the oblique sections are to be arranged in a specific order: trunk-oblique_connection()
					break // a match has been found, no need to continue checking other possible apical dendrite names
				}
			}
		}
	 }
	 trunk_oblique_connection()
}

proc trunk_oblique_connection() { local loc
	 tcs=new List() // trunk connection section
	 tcl=new Vector() // trunk connection location
	 tcd=new Vector() // trunk connection distance
	 forsec dendrite_apical_trunk_section_list { // for each trunk section
	 	secref=new SectionRef() // mark current section
		child_list=new SectionList()
		child_list.children() // list its child sections
		child_list.remove(dendrite_apical_trunk_section_list) // remove the child section that is the continuation of the trunk
		forsec child_list { // now scan the oblique roots
			loc=parent_connection() // note connection point x location
			dist=distance(section_orientation()) // note distance from soma at connection point
			temp_section_list=new SectionList()
			temp_section_list.subtree() // list all oblique sections from this root
			forsec temp_section_list {
				dendrite_apical_obliques_section_list.append() // append them to the oblique list
				tcs.append(secref)
				tcl.append(loc)
				tcd.append(dist)
			}
		}
	 }
}

proc correct_SPINEAREA() { local ok
	 ok=$1
	 if (ok) {
//	 print "spine correction under construction"
	 } else { SPINEAREA=copySPINEAREA }
}

// ===================================================================================

proc cell_cfg() { local answer
	 if (CELL_CFG) {
	 	strdef question
		question="Cell configuration will no longer be modifiable. Proceed?"
	 	dbox=new HBox()
	 	answer = 1
		if (numarg() == 0) { answer=dbox.dialog(question,"yes","no") }
		if (answer) {
			cellbox.unmap()
		   CELL_CFG=0
		   sprint(CELL,"%s-%s-%s-%d-%d",MORPH.name,LAYOUT.name,DISTRIB.name,SYNINT,SEGLEN)
		   if (SLCT_DISTRIB == 4) {
			sprint(CELL, "%s-%d-%d", CELL, SEGLEN2, NLEAFSECS)
		}	
	 	sprint(celldir,"../records/files/%s",CELL)
		sprint(sys,"mkdir -p %s",celldir)
		system(sys)
		   cellfile=new File()
		   cellfile.wopen("../main/defaults/cell-default.hoc")
		   cellfile.printf("// cell DEFAULT Parameters\n")
		   cellfile.printf("// =======================\n\n")
		   cellfile.printf("SLCT_MORPH = %d\n",SLCT_MORPH)
		   cellfile.printf("SLCT_LAYOUT = %d\n",SLCT_LAYOUT)
		   cellfile.printf("SLCT_DISTRIB = %d\n",SLCT_DISTRIB)
		   cellfile.printf("SYNINT = %d\n",SYNINT)
		   cellfile.printf("SEGLEN = %d\n",SEGLEN)
		   cellfile.printf("SEGLEN2 = %d\n",SEGLEN2)
		   cellfile.printf("NLEAFSECS = %d\n",NLEAFSECS)
		   cellfile.close()
		   distrib_load_file() // synapse.hoc
		   proceed()
		}
	 } else {
	    strdef notice
		notice="Cell configuration may no longer be changed!"
		dbox=new HBox()
		dbox.dialog(notice)
	 }
}

// ===================================================================================
proc axon_connection() { local ok
	ok=$1
	if (ok) {
		if (AXON_ATTACH && !AXON_ATTACHED) {
			axon_attach()
			AXON_ATTACHED=1
		} else if (!AXON_ATTACH && AXON_ATTACHED) {
			axon_detach()
			AXON_ATTACHED=0
		}
		copyAXON_ATTACH=AXON_ATTACH
		sprint(cmd,"%s distance()",ORIGIN_NAME)
		execute(cmd)
	} else { AXON_ATTACH=copyAXON_ATTACH }
}

// ===================================================================================

proc morph_select() {
	 if (CELL_CFG) {
	 	SLCT_MORPH=$1
	 	MORPH=morphlist.object(SLCT_MORPH)
	 } else {
	    morphlist.select(SLCT_MORPH)
	 }
}

proc layout_select() {
	 if (CELL_CFG) {
	 	SLCT_LAYOUT=$1
	 	LAYOUT=layoutlist.object(SLCT_LAYOUT)
	 } else {
	    layoutlist.select(SLCT_LAYOUT)
	 }
}

proc SYNINT_select() {
	 if (!CELL_CFG) {
	 	SYNINT=copySYNINT
	 } else {
	    copySYNINT=SYNINT
	 }
}

proc SEGLEN_select() {
	 if (!CELL_CFG) {
	 	SEGLEN=copySEGLEN
	 } else {
	    copySEGLEN=SEGLEN
	 }
}

proc SEGLEN2_select() {
	 if (!CELL_CFG) {
	 	SEGLEN2=copySEGLEN2
	 } else {
	    copySEGLEN2=SEGLEN2
	 }
}

proc NLEAFSECS_select() {
	 if (!CELL_CFG) {
	 	NLEAFSECS=copyNLEAFSECS
	 } else {
	    copyNLEAFSECS=NLEAFSECS
	 }
}

// ==========================================================================
// ==========================================================================

// selected sections list
proc prepare_selsec_list() {
	NSELSECS = 0
	selsecparambox=new VBox()
	selsec_list=new List()				// selsec name list for browser
	selsec_section_list=new SectionList()		// SectionList of selsecs
	selsec_subtree_list = new List()		// each selsec is considered as a subtree copied from its parent subtree
	str_selsec = "Select a Section"
}

// ---------------------------------------------------------------------------------

// delete the list for a newly selected RDP
proc clear_selsecs() { local i, total
	if (NSELSECS) {
		total = NSELSECS
		for i = 1, total {
			select_selsec(total-i)
			remove_selsec(1)
			str_selsec = "Select a Section"
		}
	}
}

proc user_clear_selsecs() { local ok, response
	ok = $1
	if (ok) {
		dbox=new HBox()
		response = dbox.dialog("Are you sure you want to clear all selected sections?","Yes","No")
		if (response) { clear_selsecs() }
	}
}

// ---------------------------------------------------------------------------------

proc select_selsec() { local input, i
	input=$1
	if (input>=0) {
		SELSEC = input
		selsec_list.select(SELSEC)
		selsec.sec shape_selsec(1) // plot.hoc
		selsec_subtree_list.object(SELSEC).secref.sec selsec = new SectionRef()
		selsec.sec shape_selsec(2) // plot.hoc
		selsec.sec sprint(str_selsec,"%s %g - %g um",selsec_subtree_list.object(SELSEC).name,distance(0),distance(1))
	}
	selsecparambox.unmap()
}

// ---------------------------------------------------------------------------------

proc shape_section_selection() {
	selsec_flag = 1	// a section has been selected
	selsec = new SectionRef()	
}

// ---------------------------------------------------------------------------------

// copy data structure and values from the parent subtree to the selsec subtree
proc selsec_copy_subtree() { local i,j
	subtree = selsec_subtree_list.object(SELSEC)
	parent_subtree = subtree.parent_subtree
	for i = 0, parent_subtree.numchannels-1 {
		channel = parent_subtree.channlist.object(i)
		subtree.add_channel(new Channel(channel.name))
		for j = 0, channel.numparams-1 {
			param = channel.paramlist.object(j)
			sprint(cmd,"subtree.channlist.object(%d).add_parameter(new Channel_Parameter(%s))",i,param.arguments)
			execute(cmd)
			subtree.channlist.object(i).paramlist.object(j).selsec_private_varnames() // make distinguishable variable names
		}
	}
}

proc selsec_create_subtree() { local i, parent_subtree_id	
	for i = 0, LAYOUT.numsubtrees-1 {
		parent_subtree = LAYOUT.subtree_list.object(i)
		selsec.sec ifsec parent_subtree.sec_list { // if the selected section is in the subtree section list
			parent_subtree_id = i
			i = LAYOUT.numsubtrees	// end loop
		}
	}
	parent_subtree = LAYOUT.subtree_list.object(parent_subtree_id)
	selsec.sec selsec_subtree_list.append(new Subtree(parent_subtree))
	subtree = selsec_subtree_list.object(SELSEC)
	selsec_copy_subtree()
}

proc add_selsec() { local i, ok, parent_subtree_id
	ok=$1
	if (ok && selsec_flag) {
		selsec.sec ifsec selsec_section_list { ok = 0 } // if the selected section has already been added to the list
		if (ok) {
			NSELSECS += 1
			SELSEC = NSELSECS-1
			selsec.sec selsec_list.append(new String(secname()))
			selsec.sec selsec_section_list.append()
			selsec_create_subtree()
			selsec_list.select(SELSEC)
			selsec.sec sprint(str_selsec,"%s %g - %g um",selsec_subtree_list.object(SELSEC).name,distance(0),distance(1))
/*			
			for i = 0, NLOGSYNS - 1 {
				logsyn = logsynlist.object(i)
				logsyn.sec.sec {
					ifsec selsec_list.object(SELSEC).s {
						logsyn.selsec_flag = 1
					}
				}
			}
*/		
		}
		selsec_flag = 0
	}
}

proc remove_selsec() { local ok, i
	ok=$1
	if (ok && SELSEC>=0) {
		selsec.sec ifsec selsec_section_list {  // if the selected section appears in the list
			selsecparambox.unmap()
/*			
			for i = 0, NLOGSYNS - 1 {
				logsyn = logsynlist.object(i)
				logsyn.sec.sec {
					ifsec selsec_list.object(SELSEC).s {
						logsyn.selsec_flag = 0
					}
				}
			}
*/
			selsec_list.remove(SELSEC)
			selsec.sec selsec_section_list.remove()
			selsec_subtree_list.object(SELSEC).selsec_subtree_destroy()
			selsec_subtree_list.remove(SELSEC)
			NSELSECS -= 1
			select_selsec(NSELSECS - 1)
			selsec.sec shape_selsec(1) // plot.hoc
			channel_update_parameters(1) // restore removed selsec values
		}
	}
}

// ---------------------------------------------------------------------------------

proc selsec_plot_subtree() { local type
	type = $1
	subtree = selsec_subtree_list.object(SELSEC)
	scan_subtree_channels(1,type,subtree)
	scan_subtree_channels(0,type,subtree.parent_subtree)
}

// ---------------------------------------------------------------------------------

proc selsec_channel_update_parameters() { local ok, i
	ok=$1
	subtree = selsec_subtree_list.object(SELSEC)
	if (ok) {
		UNBALANCED=1 // flag signaling that cell may be unbalanced and will be balanced upon simulation run
		subtree_channel_update(subtree)
	} else {
		subtree_channel_restore(subtree)
	}
}

// ---------------------------------------------------------------------------------

proc selsec_show() { local i, j
	for i = 0, NSELSECS-1 {
		selsec_subtree_list.object(i).secref.sec shape_selsec(5) // plot.hoc
//		selsec.sec shape_selsec(2)
	}
	selsec_line()	// plot.hoc
}

// ---------------------------------------------------------------------------------

proc selsec_parameter_panel() { local j, k
	selsecparambox.unmap()
	selsecparambox=new VBox()
	selsecparambox.intercept(1)
	xpanel("params")
		subtree = selsec_subtree_list.object(SELSEC)
		xlabel(subtree.name)
		for j=0, subtree.numchannels-1 {
			channel=subtree.channlist.object(j)
			for k=0, channel.numparams-1 {
				 param=channel.paramlist.object(k)
//				 if (param.user_access && param.type!=2) { // not a membrane parameter
				 if (param.user_access) { // not a membrane parameter
					xvalue(param.name,param.var_name,1,"selsec_channel_update_parameters(RDP_check())")
				}
			}
		}
	xpanel()
	selsecparambox.intercept(0)
	selsecparambox.map("Section Parameters")
}

proc selsec_control() {
	selsecbox=new VBox()
	selsecbox.intercept(1)
		xpanel("data")
			xvarlabel(str_selsec)
		xpanel()
		xpanel("buttons",1)
			xbutton("add","add_selsec(RDP_check())")
			xbutton("remove","remove_selsec(RDP_check())")
			xbutton("clear","user_clear_selsecs(RDP_check())")
			xbutton("show","selsec_show()")
		xpanel()
		xpanel("selsecs",1)
			selsec_list.browser("Name","s")
			xbutton("parameters", "selsec_parameter_panel()")
			xbutton("plot g","selsec_plot_subtree(1)")
			xbutton("plot kinematics","selsec_plot_subtree(0)")
		xpanel()
	selsecbox.intercept(0)
	selsecbox.map("Selected Sections")
	selsec_list.select_action("select_selsec(hoc_ac_)")
	selsec_list.select(SELSEC)
}

// ---------------------------------------------------------------------------------

proc write_selsecs() { local i, j
	selsec_file=$o1
	selsec_file.printf("NSELSECS = %d",NSELSECS) // number of selected sections
	for i = 0, NSELSECS-1 {
		subtree = selsec_subtree_list.object(i)
		selsec_file.printf("\n%s",selsec_list.object(i).s)
		for j = 0, subtree.numchannels-1 {
			channel = subtree.channlist.object(j)
			for k = 0, channel.numparams-1 {
				param = channel.paramlist.object(k)
				if (param.user_access) {
					selsec_file.printf("\n%s=%g",param.var_name,param.var_value)
				}
			}
		}
		selsec_file.printf("\n*")
	}
}


proc read_selsecs() { local i, total
	clear_selsecs()
	selsec_file = $o1
	selsec_filename = $s2
	selsec_file.ropen(selsec_filename)
	total = selsec_file.scanvar() // number of selected sections
 	for i = 0, total-1 {
		selsec_file.scanstr(strng)
		sprint(cmd,"%s selsec = new SectionRef()",strng)
		execute(cmd)
		selsec_flag = 1
		add_selsec(1)
		while(1) {
			selsec_file.scanstr(strng)
			if (!strcmp(strng,"*")) { break }
			execute(strng)
		}
		selsec_channel_update_parameters(1)
	}
	selsec_file.close
}