///////////////////////
/* net_dd_imodel.hoc */
///////////////////////

objref interneuron[nIN]			// the "network" of interneurons (n = nIN)

begintemplate Interneuron			// template of an interneuron

//names of variables 
public  soma_interneuron, old_v, drv_interneuron, syn_II, syn_EI, \
			switch_syn_II, switch_syn_EI, set_syn, change_Imu_interneuron, \
			pre_list_II, pre_list_EI, connect_pre_II, is_connected_II, disconnect_cell_II, \
			connect_pre_EI, disconnect_cell_EI, index_IN, indicing_IN, connect_gap, \
      disconnect_gaps, switch_gaps, currents, bgdrive, inj_input, injvec,\
      signinput 
      
external n_d_interneuron,n_L_interneuron,n_seg_interneuron,\
			 ARes_interneuron,MCap_interneuron,Rm_interneuron, Vrest_interneuron,\
			 Syn_II_rise, SynE_II, max_ddcon_II, Syn_II_N, nIN,\
			 Syn_EI_rise, Syn_EI_decay, SynE_EI, Ek_interneuron, shift_interneuron,\
			 SynDel, SynADel, SynG_II, GapR, tauvec_II, temiinj_IN, iinj_time, inj_step,\
			 Imu_interneuron, tstop, BGSyn_rise_IN, BGSyn_decay_IN, tINinj_on,\
       IN_SIGNSyn_rise, IN_SIGNSyn_decay                                

objref syn_II[50], syn_EI, pre_list_II, pre_list_EI,\
	     drv_interneuron, net_c_interneuron, net_c_principalneuron, gaps[8],\
	     bgsyn, bgdrive_list, inj_IN, injvec, signsyn, signinput_list
	
create soma_interneuron

proc init() {
   pre_list_II = new List()
   pre_list_EI = new List()
   bgdrive_list = new List()
   signinput_list = new List()
   
   soma_interneuron { 
		    //geometry
		    diam = n_d_interneuron  L=n_L_interneuron nseg=n_seg_interneuron 
		    f_surf = area(0.5)/100000      // area(0.5) = 100 um^2 (PI*diam^2)
		
		    //cable parameters
		    Ra = ARes_interneuron cm=MCap_interneuron v=Vrest_interneuron 
		    old_v = Vrest_interneuron
		
        //modified HH conductances
        insert hh_net      // this determines the membrane mechanisms
	 	           gl_hh_net = 1/Rm_interneuron		
		           el_hh_net = Vrest_interneuron
		           egk_hh_net = Ek_interneuron
		           sh_hh_net = shift_interneuron
		
        // postsynaptic currents: IPSC 
		    for i = 0,Syn_II_N-1 {
            syn_II[i] = new Exp2Syn(0.5)
            syn_II[i].tau1 = Syn_II_rise
            syn_II[i].tau2 = tauvec_II.x[i*(max_ddcon_II/Syn_II_N)]
            syn_II[i].e = SynE_II
        }

        // postsynaptic currents: EPSC
		    syn_EI = new Exp2Syn(0.5)		
		
		    syn_EI.tau1 = Syn_EI_rise
		    syn_EI.tau2 = Syn_EI_decay
		    syn_EI.e = SynE_EI
		    
		    // excitatory synapses for background drive
        bgsyn = new Exp2Syn(0.5)
        bgsyn.tau1 = BGSyn_rise_IN
        bgsyn.tau2 = BGSyn_decay_IN
        bgsyn.e = 0
        
        // excitatory synapses for synaptic input signal
        signsyn = new Exp2Syn(0.5)
        signsyn.tau1 = IN_SIGNSyn_rise
        signsyn.tau2 = IN_SIGNSyn_decay
        signsyn.e = 0

		    // IClamps - a) for constant drive
		    drv_interneuron = new IClamp(0.5)

		    drv_interneuron.del = 0
		    drv_interneuron.dur = tstop
		    drv_interneuron.amp = Imu_interneuron*f_surf	// nA
		    // excitatory curent injection -  the density param. 'Imu' [uA/cm^2]  should be converted 
		    // to the injected current 'amp' [nA] using the factor 'fsurf' derived from the 
		    // surface area [um^2] and the conversion of the units
		    
		    // IClamps - b) for shaped input
		    inj_IN = new IClamp(0.5)

		    inj_IN.del = 0
		    inj_IN.dur = tstop
		    
		    // Gaps
		    for i=0,7 { 		// connect to 8 neighbours L and R 4
		        gaps[i] = new gap(0.5) 
		        gaps[i].r = GapR 
		        setpointer gaps[i].vgap,v(0.5)
		    }
		    n_gaps=0 
   }
}   

proc indicing_IN() {
  index_IN = $1
}

// ****GAPs****
proc connect_gap() {
// $o1 arg is the other Cell
    setpointer gaps[n_gaps].vgap, $o1.soma_interneuron.v(0.5)
    n_gaps +=1
}

proc disconnect_gaps() {
    for i=0,7 { 
	setpointer gaps[i].vgap, soma_interneuron.v(0.5)
    } 	
    n_gaps=0
}

proc switch_gaps() {local i,r
  if ($1==0) {r=1000000000} else {r=$1}
	for i=0,7 {gaps[i].r = r }
}	

// ****IPSCs****
proc connect_pre_II() {local f, axdel, strength
// $o1 arg is the **PRESynaptic** Cell, $2 is the distance between the cells, $3 gsyn, $4 distance for the tau-Vector
   axdel=$2*SynADel       // in ms distance between cells 50 um
                       	  // AP propagation  .25 m/s => 0.2 ms for one interval
   strength = $3*f_surf	  // synaptic strength calculate using the surface factor
   synno = int(($4*Syn_II_N)/(max_ddcon_II+1))   			   

   $o1.soma_interneuron pre_list_II.append( new NetCon(&v(1),syn_II[synno],-20,SynDel+axdel,strength))

	// list.append() adds new items to the list
	// NetCon: (presynaptic variable, target, thresh, delay, weight)
	// the last argument is the 'weight' with a range of [0-1] where 1 corresponds to 1 uS peak 
	// conversion: 'SynG' [mS/cm^2] to 'weight' [uS] using the factor 'fsurf' derived from the 
	// surface area [um^2] and the conversion of the units
	
	// connect_pre_II() is executed on top of the postsynaptic cell-object (object.connect_pre_II()).                      
	// Every cell in the network has an own pre_list for every type of connections that 
  // is made on it. These lists contain all the NetCon-objects of this synapse type 
  // which have the respective cell as a target cell. In the brackets the first argument 
  // gives the presynaptic cell of which the soma is used as the source of the 
	// NetCon-object 
}

proc disconnect_cell_II() {
   pre_list_II.remove_all()
}

func is_connected_II() {local i,c			// check if connected
   c = 0
   for i = 0,pre_list_II.count()-1 {
		net_c_interneuron = pre_list_II.object(i)		// get netCon object from list
		if ($o1 == net_c_interneuron.precell()) {c=1}
   }
   return c
}   

proc switch_syn_II() {local i			// check if connected
   for i = 0,Syn_II_N-1 {
      syn_II[i].tau1 = Syn_II_rise			// to make sure changes in syn params 
      syn_II[i].tau2 = tauvec_II.x[i*(max_ddcon_II/Syn_II_N)]
      syn_II[i].e = SynE_II
   }
   
   for i = 0,pre_list_II.count()-1 {
	    net_c_interneuron = pre_list_II.object(i)		// get netCon object from list
	    net_c_interneuron.active($1)
	 }
}

// ****EPSCs****
proc connect_pre_EI() {local f, axdel, strength
// $o1 arg is the **PRSynEaptic** Cell, $2 is the distance between the cells, $3 gsyn

   axdel=$2*SynADel        // in ms distance between cells 50 um
                       	  // AP propagation  .25 m/s => 0.2 ms for one interval
   strength = $3*f_surf	  // synaptic strength calculate using the surface factor			   

   $o1.soma_principalneuron pre_list_EI.append( new NetCon(&v(1),syn_EI,0,SynDel+axdel,strength))

   	
	// list.append() adds new items to the list
	// NetCon: (presynaptic variable, target, thresh, delay, weight)
	// the last argument is the 'weight' with a range of [0-1] where 1 corresponds to 1 uS peak 
	// conversion: 'SynG' [mS/cm^2] to 'weight' [uS] using the factor 'fsurf' derived from the 
	// surface area [um^2] and the conversion of the units
}

proc disconnect_cell_EI() {
   pre_list_EI.remove_all()   
}

proc switch_syn_EI() {local i			// check if connected
   syn_EI.tau1 = Syn_EI_rise			// to make sure changes in syn params 
                            			// take place in the network
   syn_EI.tau2 = Syn_EI_decay
    
   syn_EI.e = SynE_EI
   
    for i = 0,pre_list_EI.count()-1 {
		net_c_principalneuron = pre_list_EI.object(i)		// get netCon object from list
		net_c_principalneuron.active($1)
		}
}

// ****Constant excitatory drive****
proc change_Imu_interneuron() {
  if (numarg()<1) {
	 drv_interneuron.amp = Imu_interneuron*f_surf
	} else {
	 drv_interneuron.amp = $1*f_surf
	}
}

// ****Background synaptic drive****
proc bgdrive() {
    strength = $2*f_surf
    bgdrive_list.append( new NetCon($o1,bgsyn,10,0,strength) )    
}

// ****Shaped input****
proc inj_input() {
  injvec = new Vector(tstop/inj_step)
  // $1 is the multiplicative factor that depends on the spatial position of the cell in the net!
  injvec = temiinj_IN.c.mul(f_surf*$1)
  // $2 is a variability factor determined by spainput_on_sd
  for i = tINinj_on/inj_step,$2/inj_step {
      injvec.x[i-1] = 0
  }
  injvec.play(&inj_IN.amp,iinj_time,1)
}

proc signinput() {
    strength = $2*f_surf
    signinput_list.append( new NetCon($o1,signsyn,10,0,strength) )    
}

endtemplate Interneuron