//genesis
//ConstrainUp.g
/*Functions used to connect a set of spines that are constrained by
 *pathlength from soma
 *sharing a common parent branch, either soma, primary, secondary, or tertiary
*/

function ConnectWithDelay (totalDelay,meanDelay,othercell,compt,branchoffset,randDelayYesNo,mirrorBranch)
	str othercell, compt
	float meanDelay,branchoffset, mirrorBranch
    float totalDelay
    int i
    int randDelayYesNo
    //echo "minDelay: " {minDelay} " maxDelay: " {maxDelay} " StimDelay: " {StimDelay}
	addmsg   {othercell}/spikegen  {compt}/{NMDAname} SPIKE 
	addmsg   {othercell}/spikegen  {compt}/{AMPAname} SPIKE 
	int msgnum = {getfield {compt}/{NMDAname} nsynapses} - 1
    if ({randDelayYesNo}==0)
        setfield {compt}/{NMDAname} synapse[{msgnum}].weight 1 synapse[{msgnum}].delay {totalDelay+meanDelay+branchoffset}
            
        msgnum = {getfield {compt}/{AMPAname} nsynapses} - 1
        setfield {compt}/{AMPAname} synapse[{msgnum}].weight 1 synapse[{msgnum}].delay {totalDelay+meanDelay+branchoffset}
        
        totalDelay = totalDelay+meanDelay
    else
        float randDelay = {{-1.0*{meanDelay}}*{log {rand 0 1}}} // Transform from uniform dist to exp dist: -mean_expdist*ln(uniform(0,1))
        float randBound = {10.0*{meanDelay}}
        if ({randDelay}>{randBound})
            randDelay={randBound}
        end
        echo "random exponential for ISI= " {randDelay}
        setfield {compt}/{NMDAname} synapse[{msgnum}].weight 1 synapse[{msgnum}].delay {totalDelay+randDelay+branchoffset}
        msgnum = {getfield {compt}/{AMPAname} nsynapses} - 1
        setfield {compt}/{AMPAname} synapse[{msgnum}].weight 1 synapse[{msgnum}].delay {totalDelay+randDelay+branchoffset}
        totalDelay = totalDelay+randDelay
    end
    
	return {totalDelay}

end


// This function loops through all spines, determines if each is valid (e.g. meets parent branch, distance requirements,
// and appends valid spine names to a space-separated list (as a single string) which can then be manipulated with arglist, getarg routines
function countSpines(startcomp,parenttype,maxpath)
    int parenttype
    str startcomp
    float maxpath
    
    str spinelist="",compt,compParent,commonBranch
    float compPathLen,rannum,minpath
    int numsp=0

    //extract minpath from starting compartment; maxpath is total distance between start comp and most distant spine
	minpath={getfield {neuronname}/{startcomp} pathlen}-{getfield {neuronname}/{startcomp} len}/2
    maxpath={minpath}+{maxpath}

	//parenttype is either 0:soma, 1:parentprim, 2:parentsec, 3:parenttert
	if ({parenttype}==0)
		commonBranch="soma"
	elif ({parenttype}==1)
		commonBranch={getfield {neuronname}/{startcomp} parentprim}
	elif ({parenttype}==2)
		commonBranch={getfield {neuronname}/{startcomp} parentsec}
	elif ({parenttype}==3)
		commonBranch={getfield {neuronname}/{startcomp} parenttert}
	end

	foreach compt ({el {neuronname}/##[TYPE=compartment]}) 
		//is this a spine head, if so, determine pathlength
		if ({strcmp {getpath {compt} -tail} "head"} == 0)
			compPathLen={getfield {compt} pathlen}
			//is pathlength within min and max?  If so, determine parent branch
			if ((compPathLen > minpath) && (compPathLen < maxpath))
               // echo "within path length" {compt}
				if ({parenttype} == 1)
					compParent={getfield {compt} parentprim}
				elif ({parenttype} == 2)
					compParent={getfield {compt} parentsec}
				elif ({parenttype} == 3)
					compParent={getfield {compt} parenttert}
				else
					compParent="soma"
				end
				//is parent branch correct?  if so count it and append to valid spine list
				if ({strcmp {compParent} {commonBranch}}==0)
					//count valid spines
					echo "valid sp" {compt} {compPathLen} "bt" {minpath} "&" {maxpath} "par:" {compParent} "numsp" {numsp+1}
					numsp=numsp+1
                    //append spinecompartment to spinelist followed by a space for a space-separated list
                    spinelist = spinelist @ {compt} @ " "
                    //echo {spinelist}
				end
			end
		end
	end
	return {spinelist}
end

//This function reads in a list of valid spines and the total number to connect, and connects the total number randomly by passing to connect w/delay 
//Note: this enables user to provide explicit list and option to connect to all in list in order or to use a list of spines generated by countSpines
function connectSpines(spinelist,numstim,meanDelay,randDelayYesNo,branchoffset,mirrorBranch) //will need to add delay terms for when passing to connectwdelay
    str spinelist
    int numstim, randDelayYesNo, mirrorBranch
    float meanDelay, branchoffset
    float totalDelay = 0.0
    
    int numconnected = 0
    while (numconnected < numstim) //randomly select spine to connect until desired number (numstim) are connected
        int listlen= {getarg {arglist {spinelist}} -count} //how many left in list?
        int prob = {rand 1 {listlen+0.99999999}} //randomly select 1 integer within length of list. Note: 0.99... because int(rand b/t 1,1.999...) returns 1, etc., so i want the last item in list (for example, if listlen = 20, item 20) to have same probablility of being selected. Any rand b/t 20-20.999... returns 20 as int
        str connectwhich = {getarg {arglist {spinelist}} -arg {prob}} //which spine (based on index from prob)?
        totalDelay={ConnectWithDelay {totalDelay} {meanDelay} {precell} {connectwhich} {branchoffset} {randDelayYesNo} {mirrorBranch}}
        if ({mirrorBranch}==1)
            str mirrorcompt = {strsub {connectwhich} "tertdend1_" "tertdend2_"}
            ConnectWithDelay {totalDelay} 0.0 {precell} {mirrorcompt} {branchoffset} 0 1 //ConnectwithDelay but don't return new total Delay and pass mean 0, not rando, to match delay on original branch
        end
        echo "connect" {connectwhich} " delay" {totalDelay}
        numconnected = numconnected+1
        // Generate new spinelist that takes the last entry and moves it to the entry just selected and reduces list by 1
        str newlist = ""
        int i
        for (i=1;i<listlen;i=i+1)
            if (i==prob)
                str append = {getarg {arglist {spinelist}} -arg {listlen}}
            else
                str append = {getarg {arglist {spinelist}} -arg {i}}
            end
            newlist = newlist @ append @ " "
        end
        //echo {newlist}
        //echo {listlen}
        spinelist = newlist
    end
end

/* This function loops through all spines to either count the number filling criteria
   or to connect one of those spines */
function spineLoop(startcomp,parenttype,maxpath,connectProb,maxdelay,mindelay,mirrorbranch,branchoffset)
  int parenttype,mirrorbranch
	str startcomp, connectProb
	float maxpath,maxdelay,mindelay,branchoffset

	float compPathLen,ConnProb, rannum,StimDelay,meanDelay=0
	str compt,compParent,commonBranch
	int numsp=0,connect=0
    //echo "spineLoop function minDelay= " {mindelay}
	//extract minpath from the starting compartment
	//maxpath is the total distance between start comp and most distance spine
	float minpath={getfield {neuronname}/{startcomp} pathlen}-{getfield {neuronname}/{startcomp} len}/2
	maxpath={minpath}+{maxpath}

	//strcmp returns 0 if connectProb = 0, so we do this only if NE 0
	if ({strcmp {connectProb} "0"})
		connect=1
		ConnProb={connectProb}
	end
		
	//parenttype is either 0:soma, 1:parentprim, 2:parentsec, 3:parenttert
	if ({parenttype}==0)
		commonBranch="soma"
	elif ({parenttype}==1)
		commonBranch={getfield {neuronname}/{startcomp} parentprim}
	elif ({parenttype}==2)
		commonBranch={getfield {neuronname}/{startcomp} parentsec}
	elif ({parenttype}==3)
		commonBranch={getfield {neuronname}/{startcomp} parenttert}
	end

	foreach compt ({el {neuronname}/##[TYPE=compartment]}) 
		//is this a spine head, if so, determine pathlength
		if ({strcmp {getpath {compt} -tail} "head"} == 0)
			compPathLen={getfield {compt} pathlen}
			//is pathlength within min and max?  If so, determine parent branch
			if ((compPathLen > minpath) && (compPathLen < maxpath))
               // echo "within path length" {compt}
				if ({parenttype} == 1)
					compParent={getfield {compt} parentprim}
				elif ({parenttype} == 2)
					compParent={getfield {compt} parentsec}
				elif ({parenttype} == 3)
					compParent={getfield {compt} parenttert}
				else
					compParent="soma"
				end
				//is parent branch correct?  if so either count it (first time through loop) or connect with probability
				if ({strcmp {compParent} {commonBranch}}==0)
					if (connect)
						//if parenttert of this compartment is parenttert of start comp then connect for the mirrorbranch case
                        //if ((mirrorbranch) && ({strcmp {getfield {compt} parenttert} {getfield {neuronname}/{startcomp} parentter}}}==0))
                        rannum={rand 0 1}
						if ({rannum}<{ConnProb})
							StimDelay={ConnectWithDelay {mindelay} {maxdelay} {precell} {compt} {meanDelay} {branchoffset}}
							//count connected spines
							numsp=numsp+1
							meanDelay=meanDelay+StimDelay
							echo "connect" {compt} "ranum" {rannum} "stimdelay" {StimDelay} 
                            if (mirrorbranch)
                                str mirrorcompt = {strsub {compt} "tertdend1_" "tertdend2_"}
						 	    StimDelay={ConnectWithDelay {mindelay} {maxdelay} {precell} {mirrorcompt} {meanDelay} {branchoffset}}
                                echo "connect" {compt} "ranum" {rannum} "stimdelay" {StimDelay}
                                numsp=numsp+1
                            end
						else
							echo "no connect" {compt} {rannum} "gt" {ConnProb}
						end
					else
						//count valid spines
						echo "valid sp" {compt} {compPathLen} "bt" {minpath} "&" {maxpath} "par:" {compParent} "numsp" {numsp+1}
						numsp=numsp+1
					end
				end
			end
		end
	end
	if (connect)
		echo "meanDelay=" {meanDelay/numsp} "num connect=" {numsp}
	end
	return {numsp}
end

function ConstrainUp(numstim,startcomp,parenttype,maxpath,meanDelay,file,mirrorbranch,branchoffset,randDelayYesNo)
    int numstim,parenttype,mirrorbranch,randDelayYesNo
	str startcomp,file
	float maxpath,meanDelay,branchoffset
    
    // note: if mirrorbranch=1, the connected spines (location, and relative temporal order) should be equivalent for the case of multiple branches
	str noConnect="0"
	int nummsg=0
	float initSim=0.1,postSim=0.62

    //*********set up presynaptic element for stimulating
	PreSynStim {precell}
	//addmsg {precell}/spikegen /output/{Vmfile} SAVE state
    if ({mirrorbranch==1})
        numstim = numstim/2 //Only works for if mirroring on 2 total branches, need to make more extensible in future
        // meanDelay/2 as well??
    end
	//call spineloop to count number of valid spines
    str spinelist = {countSpines {startcomp} {parenttype} {maxpath}}
    //echo {spinelist}
    connectSpines {spinelist} {numstim} {meanDelay} {randDelayYesNo} {branchoffset} {mirrorbranch}

end

function connectGABA(totalDelay,compt,othercell,delay,offset)
    str compt, othercell
    float delay,offset, totalDelay
    addmsg {othercell}/spikegen {compt}/{GABAname} SPIKE
    int msgnum = {getfield {compt}/{GABAname} nsynapses} - 1
    setfield {compt}/{GABAname} synapse[{msgnum}].weight 1 synapse[{msgnum}].delay {offset+delay}
    echo "connecting " {compt} "/" {GABAname} " with delay " {offset + delay}
end


function runConstrainUp(numstim,startcomp,parenttype,maxpath,meanDelay,file,mirrorbranch,branchoffset,randDelayYesNo)
    int numstim,parenttype,mirrorbranch,randDelayYesNo
	str startcomp,file
	float maxpath,meanDelay,branchoffset

    int run
  // Set up Hsolve if Using that Method
    if (hsolveYesNo==1)
        call {neuronname}/solve SETUP
    end
    reset
    reset


	//if ({numsp}<{numstim})
      //  echo "PROBLEM in SpineLoop:" {numsp} "spines < " {numstim} ": not enough spines fill criteria" 
//	else
		//calculate connection prob, then call spineloop to connect spines
        //If mirroring branches, need to divide total spines by number of branches
        // also, if mirroring, change parent type to p3 HERE (after counting) so that only 1st branch is connected with spineloop
        // Then, if mirroring, connect with delay will perform the mirrored connection on the 2nd branch
		//float nstim=numstim
        //if (mirrorbranch)
          //  nstim = numstim/2
            //parenttype = 3
        //end
		//float prob=nstim/numsp
		//echo "spines" {numsp} "stim" {numstim} "connecting with probability" {prob}
		//numsp = {spineLoop {startcomp} {parenttype} {maxpath} {prob} {maxdelay} {mindelay} {mirrorbranch} {branchoffset}}
	//end

	//**********set the file name and headers.  Must be after spines connected
    
    spinehead={add_outputMultiSpines {spinefile} {0.1}}
    str saveComps= "secdend11,tertdend1_1,tertdend1_2,tertdend1_3,tertdend1_4,tertdend1_5,tertdend1_6,tertdend1_7,tertdend1_8,tertdend1_9,tertdend1_10,tertdend1_11,tertdend2_1,tertdend2_2,tertdend2_3,tertdend2_4,tertdend2_5,tertdend2_6,tertdend2_7,tertdend2_8,tertdend3_1,tertdend3_2,tertdend3_3,tertdend3_4,tertdend3_5,tertdend3_6,tertdend3_7,tertdend3_8,tertdend3_9,tertdend3_10,tertdend3_11" 
   // // echo {saveComps}
    multispinehead={add_outputOneSpinePerComp {multispinefile} {neuronname} {saveComps} {chans}} // to look at spines in non stim compartments

    str filenam={file}@"p"@{parenttype}@{startcomp}@{maxpath}@"TimeDelay"@{meanDelay}@"Mirror_"@{mirrorbranch}@"BranchOffset_"@{branchoffset}
    spinehead={add_outputMultiSpines {spinefile} {2} {chans}}

    setfilename {Vmfile} {filenam} 1 {Vmhead}
    setfilename {Cafile} {filenam} 1 {Cahead} 
    setfilename {Gkfile} {filenam} 1 {Gkhead}
    setfilename {spinefile} {filenam} 1 {spinehead}
    setfilename {Ikfile} {filenam} 1 {Ikhead}
    setfilename {multispinefile} {filenam} 1 {multispinehead}

  
    float initSim=0.1,postSim=.6

	// //run the simulation
    step {initSim} -time
    setfield {precell} Vm 10
    step 1
    showfield {precell}/spikegen state lastevent
    setfield {precell} Vm 0
	step {postSim} -time

    fileFLUSH {Vmfile} 
    fileFLUSH {Cafile} 
    fileFLUSH {Gkfile} 
    fileFLUSH {spinefile}
    fileFLUSH {Ikfile}
    fileFLUSH {multispinefile}
	// //disconnect the spike generator, in preparation for another stimulation paradigm
	 int nummsg={getmsg {precell}/spikegen -out -count}
	 int i
	 for (i=0; i<nummsg; i=i+1)
	 	deletemsg {precell}/spikegen 0 -outgoing 
	 end

end