// The axon is the venerable Mainen 1996 axon.  Not perfect, yet very stable.  
// Modified by Tuomo Maki-Marttunen, 2016, basing on TTC.hoc by Etay Hay, Dendritic excitability and gain control in recurrent cortical microcircuits (Hay and Segev, 2014, Cerebral Cortex)
// Modified by Tuomo Maki-Marttunen, 2020, added functions that distribute synapses to particular sections

create a_soma

n_axon_seg = 2
create iseg,hill,myelin[n_axon_seg],node[n_axon_seg]

objref preconlist,synLocList, rList, preTrainList
objref rd1
objref sref, fih
objref synlist

proc create_axon() {
  n_axon_seg = 2
  create iseg,hill,myelin[n_axon_seg],node[n_axon_seg]

  a_soma {
    equiv_diam = sqrt(area(.5)/(4*PI))
  }

  iseg {               // initial segment between hillock + myelin
     L = 15
     nseg = 5
     diam = equiv_diam/10        // see Sloper and Powell 1982, Fig.71
  }

  hill {                
    L = 20
    nseg = 5
    diam(0:1) = 2*iseg.diam:iseg.diam
  }

  // construct myelinated axon with nodes of ranvier

  for i=0,n_axon_seg-1 {
    myelin[i] {         // myelin element
      nseg = 2
      L = 100
      diam = iseg.diam
    }
    node[i] {           // nodes of Ranvier
      nseg = 1
      L = 1.0
      diam = iseg.diam*.75       // nodes are thinner than axon
    }
  }

  a_soma connect hill(0), 0.5
  hill connect iseg(0), 1
  iseg connect node[0](0), 1
  node[0] connect myelin[0](0), 1

  for i=0,n_axon_seg-2  { 
      myelin[i] connect node[i+1](0), 1
      node[i+1] connect myelin[i+1](0), 1 
  }
}

// --------
// Spines
// --------
proc add_spines() { local a
  forsec "dend" {
    cm*=$1
    g_pas*=$1
  }
  forsec "apic" {
    cm*=$1
    g_pas*=$1
  }
	
}


proc load_3dcell() {

// $s1 filename

  forall delete_section()
  xopen($s1)
  access a_soma
  
  // make sure no compartments exceed 50 uM length
  forall {
    diam_save = diam
    n = int(L/20)    
    if (n==0) n=1
    nseg = n 
    if (n3d() == 0) diam = diam_save
  }    
  create_axon()
  init_cell()
}


proc init_cell() {

  // passive
  forall {
    insert pas
    Ra = ra 
    cm = c_m 
    g_pas = 1/rm
    e_pas = epas_sim
  }
    // exceptions along the axon
     forsec "myelin" cm = cm_myelin
     forsec "node" g_pas = g_pas_node
  forall {
  	insert iA 
	insert kslow 
	insert na
	insert iH
	insert cah
	insert car
	insert cad
	insert bk
	insert sk
  }

  forall if(ismembrane("k_ion")) ek = Ek
  forall if(ismembrane("na_ion")) ena = Ena    
  forall eh=-33
  forall if(ismembrane("ca_ion")) {
 	vshift_ca = 0
  }
  synlist = new List()
  preconlist = new List()
  preTrainList = new List()

  lengthA = 0
  lengthB = 0
  forsec "apic" {
          lengthA = lengthA + L
  }
  forsec "dend" {
          lengthB = lengthB + L
  }
  pA = lengthA/(lengthA + lengthB)

}

proc density() {

  forall {
    Ra = ra 
    cm = c_m 
    g_pas = 1/rm
    e_pas = epas_sim
    vshiftm_na= na_shift1
    vshifth_na= na_shift2
    taum_scale_na=na_taum_scale
    tauh_scale_na=na_tauh_scale
    q10_iH=ih_q10
    shift_cah=cah_shift
    shifth_cah=cah_shifth

    shift_car=car_shift
    shifth_car=car_shifth
    qm_car=car_qm
  }


  add_spines(2)
   // exceptions along the axon
   forsec "myelin" cm = cm_myelin
   forsec "node" g_pas = g_pas_node
   forall vshift_na = -5
   forsec "myelin" {
  	gbar_na    = gna_soma
	gbar_kslow =gkslow_beta 
	gbar_iA    = gka_beta
   }

  hill {
  	gbar_na = gna_node
	vshiftm_na=7
	vshifth_na=3
	gbar_iA = gka_node
	gbar_kslow = gkslow_node
  }
	
  iseg {
  	gbar_na = gna_node
	vshiftm_na=7
	vshifth_na=3
	gbar_iA = gka_node
	gbar_kslow = gkslow_node
  }
	
  forsec "node" {
  	gbar_na = gna_node
	vshiftm_na=7
	vshifth_na=3
	gbar_iA = gka_node
	gbar_kslow = gkslow_node
  }

   // we put in all of teh dendrites the same values as in the a_soma and then overwrite the appical values	
  forsec "dend" {
    gbar_na = gna_soma
    gbar_kslow = gkslow_start+gkslow_beta
    gbar_iA = gka_start+gka_beta
    gbar_iH=gih_start 
    pbar_car=pcar_soma
    pbar_cah=pcah_soma
    gbar_sk=gsk_dend
    gbar_bk=gbk_dend
  }
  
  a_soma {
    gbar_na = gna_soma
    gbar_kslow = gkslow_start+gkslow_beta
    gbar_iA = gka_start+gka_beta
    gbar_iH=gih_start
    pbar_car=pcar_soma
    pbar_cah=pcah_soma
    gbar_sk=gsk_soma
    gbar_bk=gbk_soma
  }

  
  a_soma distance(0,0.5)
 
  forsec  ApicalDendSectionName {
	dist1=distance(0)
 	dist2=distance(1)

	
	gbar_na = gna_api
	if (dist1<dist_na){
           gbar_na(0:1) = (gna_soma+dist1*(gna_api-gna_soma)/dist_na):(gna_soma+dist2*(gna_api-gna_soma)/dist_na)
        }


	pbar_cah = pcah_api
        if (dist1<dist_cah){
           pbar_cah(0:1) = (pcah_soma+dist1*(pcah_api-pcah_soma)/dist_cah):(pcah_soma+dist2*(pcah_api-pcah_soma)/dist_cah) 
        }

        pbar_car = pcar_api
        if (dist1<dist_car){
           pbar_car(0:1) = (pcar_soma+dist1*(pcar_api-pcar_soma)/dist_car):(pcar_soma+dist2*(pcar_api-pcar_soma)/dist_car) 
	   //(pcar_soma+dist1*(pcar_api-pcar_soma)/dist_car):(pcar_soma+dist2*(pcar_api-pcar_soma)/dist_car)
        } 


	gbar_iA(0:1) = (gka_start+gka_beta*exp(gka_alpha*dist1)):(gka_start+gka_beta*exp(gka_alpha*dist2))

	gbar_kslow(0:1) = (gkslow_start+gkslow_beta*exp(gkslow_alpha*dist1)):(gkslow_start+gkslow_beta*exp(gkslow_alpha*dist2))
	
	gbar_iH(0:1) = (gih_start+gih_end/(1+exp(gih_alpha*(dist1-gih_x2)))):(gih_start+gih_end/(1+exp(gih_alpha*(dist2-gih_x2))))
	 
	gbar_bk = gbk_dend
        if (dist1<dist_bk){
           gbar_bk(0:1) = (gbk_soma+dist1*(gbk_dend-gbk_soma)/dist_bk):(gbk_soma+dist2*(gbk_dend-gbk_soma)/dist_bk)
        }

	gbar_sk = gsk_dend
	if (dist1<dist_sk){
           gbar_sk(0:1) = (gsk_soma+dist1*(gsk_dend-gsk_soma)/dist_sk):(gsk_soma+dist2*(gsk_dend-gsk_soma)/dist_sk)
        }

	
    }

        forsec  ApicalDendSectionName {

                if (pcah_soma<pcah_api){
                        if (pbar_cah>pcah_api){
                                pbar_cah=pcah_api
                        }
                }
                if (pcah_soma>pcah_api){
                        if (pbar_cah<pcah_api){
                                pbar_cah=pcah_api
                        }
                }

                if (pcar_soma<pcar_api){
                        if (pbar_car>pcar_api){
                                pbar_car=pcar_api
                        }
                }
                if (pcar_soma>pcar_api){
                        if (pbar_car<pcar_api){
                                pbar_car=pcar_api
                        }
                }

                if (gbar_na<gna_api) gbar_na=gna_api
		if (gbar_bk<gbk_dend) gbar_bk=gbk_dend
		if (gbar_sk<gsk_dend) gbar_sk=gsk_dend	
        }


}

// Copied from Hay model
// $s1 section
func getLongestBranch(){local maxL,d localobj distallist,sref
  //Commented out: Always calculate the distance with respect to soma 0.5
  //sprint(tstr,"%s distance()",$s1)
  //execute(tstr,this)

  //if(0==strcmp($s1,"axon")){
  //  sprint(tstr,"%s[0] distance(1)",$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
}

//Copied from Hay model
// $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)

  //if(0==strcmp($s1,"axon")){
  //  sprint(tstr,"%s[0] distance(1)",$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
}


proc setparameters(){
  EsynConductance = $1
  IsynConductance = $2
  NsynE = $3
  NsynI = $4
}

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

        for(i=0;i<(NsynE+NsynI);i+=1){
                if (i==(NsynE+NsynI)/4-1) { print "25% of synapses done" }
                if (i==(NsynE+NsynI)/2-1) { print "50% of synapses done" }
                if (i==3*(NsynE+NsynI)/4-1) { print "75% of synapses done" }
                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)

		//print "distributesSyn(): ",cmd2
		//print "siteVec = ",siteVec[0],siteVec[1]
                sprint(cmd2,"%s[siteVec[0]] sref = new SectionRef()",treename)
		//print "distributesSyn(): ",cmd2
                execute(cmd2)
                if (i<NsynE){
                        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 = 800
                                synlist.o[syni].Fac = 0
                                synlist.o[syni].Use = 0.6
                                synlist.o[syni].u0 = 0
                                synlist.o[syni].gmax = EsynConductance
                                preconlist.append(new NetCon(nilstim, synlist.o[syni]))
                                preconi = preconlist.count()-1 //connection index
                                preconlist.o[preconi].weight = 1
                                preconlist.o[preconi].delay = 0
                        }
                } else {
                        sref {
                                synlist.append(new ProbUDFsyn2(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 = 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
                                preconlist.append(new NetCon(nilstim, synlist.o[syni]))
                                preconi = preconlist.count()-1 //connection index
                                preconlist.o[preconi].weight = 1
                                preconlist.o[preconi].delay = 0
                        }
                }
                if (i==(NsynE+NsynI)-1) { print "synapses done" }
        }
}

proc distributeSynBranch() {local syni,preconi,i,itip,NsynsBranch,x localobj nilstim
      strdef treename,cmd2
      NsynsBranch = $1
      treename = $s2
      itip = $3
      x = rd1.repick()

      for(i=0;i<(NsynsBranch);i+=1){
                sprint(cmd2,"access %s[%g]",treename,itip)
                execute(cmd2)

                sprint(cmd2,"%s[%g] sref = new SectionRef()",treename,itip)
                execute(cmd2)
                sref {
                        synlist.append(new ProbAMPANMDA2(x))
                        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

                        preconlist.append(new NetCon(nilstim, synlist.o[syni]))
                        preconi = preconlist.count()-1 //connection index
                        preconlist.o[preconi].weight = 1
                        preconlist.o[preconi].delay = 0

                }
        }
}


proc setsynapseconductances(){local syni, i
        for(i=0;i<(NsynE+NsynI);i+=1){
                if (i<NsynE){
                         synlist.o[i].gmax = $1
                } else {
                         synlist.o[i].gmax = $2
		}
        }
}

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

proc setpretrainsbranch(){local j,NsynsBranch
  NsynsBranch = $o1.count()
  for(j=0;j<(NsynsBranch);j+=1){
    preTrainList.append($o1.o[j])
  }
}

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

// 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])
                }
        }
}