// Author: Etay Hay 2014
// Dendritic excitability and gain control in recurrent cortical microcircuits (Hay and Segev, 2014, Cerebral Cortex)
//
// Cell template of L5 thick-tufted pyramidal cell (TTC)
//
// Modified by Tuomo Maki-Marttunen 2015-2016
//
// Copied from TTC.hoc 9.11.2016. Made the following changes:
//  -distributeSyn takes as an argument a list of numbers of synapses in each compartment

begintemplate TTC

public init, biophys, geom_nseg, biophys_inhomo
public synlist, connect2target, APC
public locateSites, getLongestBranch, distributeSyn
public initRand,insertMCcons
public preconlist ,synLocList, rList, preTrainList
public rd1
public setnetworkparameters,initPreSynTrain,queuePreTrains,setpretrains
public pA

public soma, dend, apic, stimThreshold, ASCIIrpt, HDF5rpt, getAbsSecIndex
public all, somatic, apical, basal, dends, nSecSoma, nSecApical, nSecBasal, nSecAll, SecSyn

objref synlist, SecSyn, ASCIIrpt, HDF5rpt, APC
objref all, somatic, apical, basal, dends
objref preconlist,synLocList, rList,preTrainList
objref rd1
objref this
objref sref,fih
objref locVec
objref synvarVecList, treenameList

strdef tstr

create soma[1], dend[1], apic[2]


proc init() {localobj nl,import
  all = new SectionList()
  somatic = new SectionList()
  basal = new SectionList()
  apical = new SectionList()
  dends = new SectionList()

  apic[0] apical.append()
  apic[1] apical.append()
  soma[0] somatic.append()
  dend[0] basal.append()

  apic[0] all.append()
  apic[1] all.append()
  soma[0] all.append()
  dend[0] all.append()

  apic[0] dends.append()
  apic[1] dends.append()
  dend[0] dends.append()
  
  connect dend[0](0), soma[0](0)
  connect apic[0](0), soma[0](1)
  connect apic[1](0), apic[0](1)

  access soma

  soma insert pas
  dend insert pas
  apic[0] insert pas
  apic[1] insert pas

  soma {nseg = 1 Ra = 100 diam = 13.47 L = 23.17 cm = 1 V = -80 e_pas = -90}
  dend {nseg = 1 Ra = 100 diam = 10.28 L = 282.13 cm = 2 V = -80 e_pas = -90}
  apic[0] {nseg = 1 Ra = 100 diam = 5.04 L = 700.0 cm = 2 V = -80 e_pas = -90}
  apic[1] {nseg = 1 Ra = 100 diam = 5.04 L = 600.53 cm = 2 V = -80 e_pas = -90}

  soma {
    insert Ca_LVAst
    insert Ca_HVA
    insert SKv3_1
    insert SK_E2
    insert K_Tst
    insert K_Pst
    insert Nap_Et2
    insert NaTa_t
    insert CaDynamics_E2
    insert Ih
    ek = -85
    ena = 50
    gIhbar_Ih = 0.0002
    g_pas = 0.0000338
    decay_CaDynamics_E2 = 460.0
    gamma_CaDynamics_E2 = 0.000501
    gCa_LVAstbar_Ca_LVAst = 0.00343
    gCa_HVAbar_Ca_HVA = 0.000992
    gSKv3_1bar_SKv3_1 = 0.693
    gSK_E2bar_SK_E2 = 0.0441
    gK_Tstbar_K_Tst = 0.0812
    gK_Pstbar_K_Pst = 0.00223
    gNap_Et2bar_Nap_Et2 = 0.00172
    gNaTa_tbar_NaTa_t = 2.04
  }
  dend {
    insert Ih
    gIhbar_Ih = 0.0002
    g_pas = 0.0000467
  }
  apic[0] {
    insert Ca_LVAst
    insert Ca_HVA
    insert SKv3_1
    insert SK_E2
    insert NaTa_t
    insert Im
    insert CaDynamics_E2
    insert Ih
    ek = -85
    ena = 50
    decay_CaDynamics_E2 = 122
    gamma_CaDynamics_E2 = 0.000509
    gSK_E2bar_SK_E2 = 0.0012
    gSKv3_1bar_SKv3_1 = 0.000261
    gNaTa_tbar_NaTa_t = 0.0213
    gImbar_Im = 0.0000675
    g_pas = 0.0000589
    gIhbar_Ih = 0.0004
    gCa_LVAstbar_Ca_LVAst = 0.0187
    gCa_HVAbar_Ca_HVA = 0.000555
  }
  apic[1] {
    insert Ca_LVAst
    insert Ca_HVA
    insert SKv3_1
    insert SK_E2
    insert NaTa_t
    insert Im
    insert CaDynamics_E2
    insert Ih
    ek = -85
    ena = 50
    decay_CaDynamics_E2 = 122
    gamma_CaDynamics_E2 = 0.000509
    gSK_E2bar_SK_E2 = 0.0012
    gSKv3_1bar_SKv3_1 = 0.000261
    gNaTa_tbar_NaTa_t = 0.0213
    gImbar_Im = 0.0000675
    g_pas = 0.0000589
    gIhbar_Ih = 0.0004
    gCa_LVAstbar_Ca_LVAst = 0.0187
    gCa_HVAbar_Ca_HVA = 0.000555
  }
  geom_nseg()
  area(0.5)
  distance()
  access soma
 
  synlist = new List()
  preconlist = new List()
  synLocList = new List()
  stimThreshold =0
  preTrainList = new List()
  synvarVecList = new List()

  pA = 0.5
}

proc setnetworkparameters(){
  rcpWeightFactor = $1 //how stronger reciprocal connections are on average
  EsynConductance = $2
  IsynConductance = $3
  NsynE = $4
  NsynI = $5
  contactsNumE = $6
  weight_AMPA = $7
  weight_factor_NMDA = $8
  weight_GABA = $9
  gNoiseCoeff = $10
}

proc geom() {
}

proc geom_nseg() {local nSec, L1, L2, D1, D2, nSeg1, nSeg2
  soma area(.5) // make sure diam reflects 3d points
  nSec = 0
  forsec all {
    nseg = 1 + 2*int(L/40)
    nSec = nSec + 1
  }

  nSecAll = 5
  nSecSoma = 1
  nSecApical = 2
  nSecBasal = 1
}

proc connect2target() { //$o1 target point process, $o2 returned NetCon
  soma $o2 = new NetCon(&v(1), $o1)
  $o2.threshold = -30
}

// $s1 section
func getLongestBranch(){local maxL,d localobj distallist,sref
    sprint(tstr,"%s distance()",$s1)
    execute(tstr,this)    
    
		maxL = 0
		d = 0
		distallist = new SectionList()
		forsec $s1 {
			sref = new SectionRef()
			if (sref.nchild==0) distallist.append()
		}
		forsec distallist{
			d = distance(1)
			if(maxL<d) maxL = d
		}
		// for the soma case
		if (maxL == 0) {
      $s1 {
        maxL = L
      }
    }
		return maxL
	}

// $s1 section
// $2 distance x in micrometers
// return list of [1,2] vectors  - of the appropriate section and the location in each vector
obfunc locateSites() {local maxL,site,d0,d1,siteX,i localobj vv,ll
	ll = new List()

  sprint(tstr,"%s distance()",$s1)
  execute(tstr,this)    
    
	maxL = getLongestBranch($s1)
	site = $2
	i = 0
	forsec $s1 {
    if (distance(0) < distance(1)) {
  		d0 = distance(0)
  		d1 = distance(1)
  	} else {
  		d1 = distance(0)
  		d0 = distance(1)
  	}

    if (site <= d1 && site >= d0) {
      siteX = (site-d0)/(d1-d0)
      secNum = i
      vv = new Vector()
      ll.append(vv.append(secNum,siteX))
		}
		i = i+1
	}
  return ll
}


//========================================================================================
//================================= microcircuit related stuff============================
//========================================================================================

// $1 some number that is function of the TTC index
//
// Have each TTC with unique sites, but certain portion of inputs identical to root TTC
proc initRand() {
  rList = new List() //for stochastic synapses

	rd1 = new Random($1) // unique to this TTC
	rd1.uniform(0,1)
}

double siteVec[2]

proc distributeSyn() {local sitenum,syni,preconi,i localobj sl,nilstim

        strdef treename,cmd2
	//objref treenameList
	treenameList = new List()

	treenameList.append(new String("dend[0]"))
	treenameList.append(new String("apic[0]"))
	treenameList.append(new String("apic[1]"))

	for(i=0;i<$o1.count();i+=1){
            sprint(cmd2,"access %s",treenameList.o[i].s)
   	    execute(cmd2,this)
	    sprint(cmd2,"%s sref = new SectionRef()",treenameList.o[i].s)
	    execute(cmd2,this)
            
	    for(iseg=0;iseg<$o1.o[i].size();iseg+=1) {
	        segx = (0.5+iseg)/$o1.o[i].size()
		sref {
			synlist.append(new ProbAMPANMDA2group(segx))
			syni = synlist.count()-1 //synapse index
			rList.append(new Random(int(1000000*rd1.repick())))
			rList.o[syni].negexp(1)
			synlist.o[syni].setRNG(rList.o[syni])
			synlist.o[syni].tau_r_AMPA = 0.3
			synlist.o[syni].tau_d_AMPA = 3
			synlist.o[syni].tau_r_NMDA = 2
			synlist.o[syni].tau_d_NMDA = 65
			synlist.o[syni].e = 0
			synlist.o[syni].Dep = 800
			synlist.o[syni].Fac = 0
			synlist.o[syni].Use = 0.6
			synlist.o[syni].u0 = 0
                        synlist.o[syni].gmax = EsynConductance*gNoiseCoeff
                        synlist.o[syni].weight_factor_NMDA = weight_factor_NMDA

			Nsyns_thissyn = $o1.o[i].x[iseg]
			synvarVecList.append(new Vector(3*Nsyns_thissyn))
			for i1=0,Nsyns_thissyn-1 {
			  synvarVecList.o[synvarVecList.count()-1].x(i1) = 0
			  synvarVecList.o[synvarVecList.count()-1].x(Nsyns_thissyn+i1) = 1
			  synvarVecList.o[synvarVecList.count()-1].x(2*Nsyns_thissyn+i1) = 0
			  }
			synlist.o[syni].setVec(synvarVecList.o[synvarVecList.count()-1])
			preconlist.append(new NetCon(nilstim, synlist.o[syni]))
			preconi = preconlist.count()-1 //connection index
			preconlist.o[preconi].weight = weight_AMPA
			preconlist.o[preconi].delay = 0
		}
            }
	}
	for(i=0;i<$o2.count();i+=1){
            sprint(cmd2,"access %s",treenameList.o[i].s)
   	    execute(cmd2,this)
	    sprint(cmd2,"%s sref = new SectionRef()",treenameList.o[i].s)
	    execute(cmd2,this)

	    for(iseg=0;iseg<$o2.o[i].size();iseg+=1) {
	        segx = (0.5+iseg)/$o2.o[i].size()
		sref {
			synlist.append(new ProbUDFsyn2group(segx))
			syni = synlist.count()-1 //synapse index
			rList.append(new Random(int(1000000*rd1.repick())))
			rList.o[syni].negexp(1)
			synlist.o[syni].setRNG(rList.o[syni])
			synlist.o[syni].tau_r = 1
			synlist.o[syni].tau_d = 20
			synlist.o[syni].e = -80
			synlist.o[syni].Dep = 800
			synlist.o[syni].Fac = 0
			synlist.o[syni].Use = 0.25
			synlist.o[syni].u0 = 0
			synlist.o[syni].gmax = IsynConductance*gNoiseCoeff

			Nsyns_thissyn = $o2.o[i].x[iseg]
			synvarVecList.append(new Vector(3*Nsyns_thissyn))
			for i1=0,Nsyns_thissyn-1 {
			  synvarVecList.o[synvarVecList.count()-1].x(i1) = 0
			  synvarVecList.o[synvarVecList.count()-1].x(Nsyns_thissyn+i1) = 1
			  synvarVecList.o[synvarVecList.count()-1].x(2*Nsyns_thissyn+i1) = 0
			  }
			synlist.o[syni].setVec(synvarVecList.o[synvarVecList.count()-1])
			preconlist.append(new NetCon(nilstim, synlist.o[syni]))
			preconi = preconlist.count()-1 //connection index
			preconlist.o[preconi].weight = weight_GABA
			preconlist.o[preconi].delay = 0
		}
            }
	}
}

// adds the microcircuit connections
// $o1 conVec - where 0 (no connection), 1 (one way), rcpWeightFactor (reciprocated)
proc insertMCcons(){local sitenum,syni,ii,jj localobj TconVec,sl
	strdef cmd2,treename
	
	TconVec = $o1

	for(ii=0;ii<TconVec.size();ii+=1){
		if(TconVec.x[ii]!=0){
			for(jj=0;jj<contactsNumE;jj+=1){
				if (rd1.repick()<pA){
					treename = "apic"
				} else {
					treename = "dend"
				}
	
				sl = locateSites(treename,rd1.repick()*getLongestBranch(treename))
	
				sitenum = int((sl.count()-1)*rd1.repick())
				siteVec[0] = sl.o[sitenum].x[0]
				siteVec[1] = sl.o[sitenum].x[1]
	
				sprint(cmd2,"access %s[siteVec[0]]",treename)
				execute(cmd2,this)
		
				sprint(cmd2,"%s[siteVec[0]] sref = new SectionRef()",treename)
				execute(cmd2,this)
	
				sref {
					synlist.append(new ProbAMPANMDA2(siteVec[1]))
					syni = synlist.count()-1 //synapse index
					rList.append(new Random(int(1000000*rd1.repick())))
					rList.o[syni].negexp(1)
					synlist.o[syni].setRNG(rList.o[syni])
					synlist.o[syni].tau_r_AMPA = 0.3
					synlist.o[syni].tau_d_AMPA = 3
					synlist.o[syni].tau_r_NMDA = 2
					synlist.o[syni].tau_d_NMDA = 65
					synlist.o[syni].e = 0
					synlist.o[syni].Dep = 0
					synlist.o[syni].Fac = 0
					synlist.o[syni].Use = 0.25
					synlist.o[syni].u0 = 0
					synlist.o[syni].gmax = TconVec.x[ii] * EsynConductance
				}
			}
		}
	}
}

//$o1 list of vectors
proc setpretrains(){local j
  for(j=0;j<$o1.count();j+=1){
    preTrainList.append($o1.o[j])
  }
}

proc queuePreTrains(){
	fih = new FInitializeHandler("initPreSynTrain()",this)
}

// sets presynaptic spike events
proc initPreSynTrain(){local ti,si
	for(ti=0;ti<preTrainList.count();ti+=1){
		for(si=0;si<preTrainList.o[ti].size();si+=1){
			preconlist.o[ti].event(preTrainList.o[ti].x[si])
		}
	}
}

endtemplate TTC