//Created by Paulo Aguiar [pauloaguiar@fc.up.pt]

// Steps:
// 1 - CREATE WDR neuron and interneuron from the respective template cells
// 2 - SET INPUT SIGNALS/STIMULI
// 3 - SET SYNAPSES/RECEPTORS
// 4 - CONNECT STIMULI TO SYNAPSES
// 5 - PROVIDE NOISE TO WDR
// 6 - FINAL COMMANDS





// 1 *****************************************************************************************************

// CREATE WDR neuron and interneuron from the respective template cells

load_file("WDR.hoc")
load_file("interneuron.hoc")

objref wdr, interneuron
wdr = new WDR()
interneuron = new Interneuron()





// 2 *****************************************************************************************************

// SET INPUT SIGNALS/STIMULI
// C-fibres and Ad-fibres signals are driven by spike-times stored in vectors
// noise signals are driven by NetStim objects

// signal periods; all times are in ms
n_burst_spikes = 1			// define the number of spikes created by each stim in the fibres
T0 = 10.0					// if each stim produces a short burst of spikes, this is their ISI [ms]
n_close_stims = 1			// define the number of stims in each set (ex: 1=singlet; 3=triplets)
T1 = 200.0					// in heterogeneous stimulation n_close_stims are separated by T1 [ms]
n_stim_sets = 15			// define the number of stim sets
T2 = 1000.0					// period between stimuli sets [ms]
start_time = 1000.0			// time for first stim [ms]

//tstop is defined after loading the session (close to the end of this file)

// NOTES:	average stimuli period = T2 / n_close_stims
//			total stims = n_close_stims * n_stim_sets

// contruct the stimulus times vector
objref stim_times

stim_times  = new Vector(n_stim_sets*n_close_stims*n_burst_spikes, 0.0)

index = 0

for i=0,n_stim_sets-1 {
    for j=0,n_close_stims-1 {
	for k=0,n_burst_spikes-1 {
	    
	    stim_times.x[index] = start_time + i * T2 + j * T1 + k * T0
	    index = index + 1
	    
	}
    }
}

printf("\nSTIMULATION TIMES")
stim_times.printf()




// 3 *****************************************************************************************************

// SET CONNECTIONS/SYNAPSES
//	Ad-fibres	--> NMDAR, AMPAR	--> wdr
//	C-fibres	--> NMDAR, AMPAR, NK1R	--> interneuron
//	interneuron	--> NMDAR, AMPAR, GABAR	--> wdr
//	interneuron	--> GABAAR		--> wdr
//	noise_inh	--> GABAAR		--> wdr
//	noise_exc	--> AMPAR		--> wdr

// Create post-synaptic receptors for all synapses



// SYNAPSES FROM Ad-FIBRES
// -----------------------

N_A  = 20		// number of A synapses impinging on WDR
Ad_delay = 30.0		// delay in the activation of Ad synapses [ms]
Ad_dispersion = 5.0	//dispersion in the arrival time of Ad signals [ms]

objref nil
objref ampar_Ad[N_A], nmdar_Ad[N_A]
objref ampa_Ad_conn[N_A], nmda_Ad_conn[N_A]

for i=0,N_A-1 {
    
    wdr.dendrite ampar_Ad[i]  = new AMPA_DynSyn(0.5)
    ampar_Ad[i].tau_fac   = 0.1 // NOT SUBJECT TO SHORT-TERM PLASTICITY
    ampar_Ad[i].tau_rec   = 0.1
    ampar_Ad[i].U1        = 1.0
    ampar_Ad[i].tau_rise  = 0.1
    ampar_Ad[i].tau_decay = 5.0
    
    //netcon = new NetCon(source, target, threshold [mV], delay [ms], weight [uS]) -> delay is overwritten by FIinitializeHandler
    ampa_Ad_conn[i] = new NetCon(nil, ampar_Ad[i], -30.0, 0.0 , 0.0008)//0.0008
    
    
    wdr.dendrite nmdar_Ad[i]  = new NMDA_DynSyn(0.5)
    nmdar_Ad[i].tau_fac   = 0.1 // NOT SUBJECT TO SHORT-TERM PLASTICITY
    nmdar_Ad[i].tau_rec   = 0.1
    nmdar_Ad[i].U1        = 1.0
    nmdar_Ad[i].tau_rise  = 2.0
    nmdar_Ad[i].tau_decay = 100.0
    
    //netcon = new NetCon(source, target, threshold [mV], delay [ms], weight [uS]) -> delay is overwritten by FIinitializeHandler
    nmda_Ad_conn[i] = new NetCon(nil, nmdar_Ad[i], -30.0, 0.0 , 0.0001)//0.0001
    
}

// the activation times of Ad-fibres are set together with C-fibres - below



// SYNAPSES FROM C-FIBRES
// ----------------------

objref nil
objref ampar_C, nmdar_C, nk1r_C, nk1r_C_WDR
objref ampa_C_conn, nmda_C_conn, nk1_C_conn, nk1_C_WDR_conn

tau_fac = 0.1		//0.1 or 3000.0 (Facilitation Exp)
tau_rec = 0.1		//0.1 or 4000.0 (Depression Exp)
U1 = 1.0			//1.0 or 0.5 (Facilitation Exp) or 0.07 (Depression Exp)

interneuron.dendrite ampar_C = new AMPA_DynSyn(0.5)
ampar_C.tau_fac   = tau_fac
ampar_C.tau_rec   = tau_rec
ampar_C.U1        = U1
ampar_C.tau_rise  = 0.1
ampar_C.tau_decay = 5.0

interneuron.dendrite nmdar_C = new NMDA_DynSyn(0.5)
nmdar_C.tau_fac   = tau_fac
nmdar_C.tau_rec   = tau_rec
nmdar_C.U1        = U1
nmdar_C.tau_rise  = 2.0
nmdar_C.tau_decay = 100.0

interneuron.dendrite nk1r_C = new NK1_DynSyn(0.5)
nk1r_C.tau_fac    = tau_fac
nk1r_C.tau_rec    = tau_rec
nk1r_C.U1         = U1 
nk1r_C.tau_rise   = 100.0
nk1r_C.tau_decay  = 3000.0

wdr.dendrite nk1r_C_WDR = new NK1_DynSyn(0.5) // substance P diffusion to deeper laminae reaches WDR which have some NK1R
nk1r_C_WDR.tau_fac    = tau_fac
nk1r_C_WDR.tau_rec    = tau_rec
nk1r_C_WDR.U1         = U1 
nk1r_C_WDR.tau_rise   = 200.0
nk1r_C_WDR.tau_decay  = 3000.0


//netcon = new NetCon(source, target, threshold [mV], delay [ms], weight [uS]) -> delay is overwritten by FIinitializeHandler
ampa_C_conn     = new NetCon(nil, ampar_C   , -30.0, 0.0, 0.008000/U1) //0.008000  IMPORTANT: normalize all weights by U1 (for identical initial responses)
nmda_C_conn     = new NetCon(nil, nmdar_C   , -30.0, 0.0, 0.004000/U1) //0.004000
nk1_C_conn      = new NetCon(nil, nk1r_C    , -30.0, 0.0, 0.000020/U1) //0.000020; if bigger interneuron will show strong windup (but constrained by iKCa)
nk1_C_WDR_conn  = new NetCon(nil, nk1r_C_WDR, -30.0, 0.0, 0.000015/U1) //0.000015; smaller than above to reflect diffusion and less expression


//set activation times
objref fih
fih = new FInitializeHandler("loadqueue()")

proc loadqueue() { local ii, jj, event_time  localobj r
        
    r = new Random(123456789) // use a different seed if you need different random streams; "123456789" is the seed used in the paper figure
    
    //load Ad-fiber spike times
    for ii=0,stim_times.size()-1 {
	
	//distribute through all synapses
	for jj=0,N_A-1 {
	    
	    event_time = stim_times.x[ii] + Ad_delay + Ad_dispersion * r.repick()

	    ampa_Ad_conn[jj].event( event_time )
	    nmda_Ad_conn[jj].event( event_time )
	    
	}
    }        
    
    //load C-fiber spike times
    for ii=0,stim_times.size()-1 {
	
	event_time = stim_times.x[ii] // THE C-FIBRES DELAY IS PLACED AFTER THE INTERNEURON TO ALLOW CONTROLED DISPERSION

	ampa_C_conn.event( event_time )
	nmda_C_conn.event( event_time )
	nk1_C_conn.event( event_time )
	nk1_C_WDR_conn.event( event_time )	    
	
    }
    
}




// SYNAPSES FROM INTERNEURON
// -------------------------

N_IC = 20		// number of interneuron (from C) synapses impinging on WDR
C_delay = 200.0		// conduction in C-fibres is slower; this is the delay
C_dispersion = 20.0	// dispersion in the arrival time of C signals [ms]

objref ampar_interneuron[N_IC], nmdar_interneuron[N_IC], gabaar_interneuron[N_IC]
objref ampa_interneuron_conn[N_IC], nmda_interneuron_conn[N_IC], gabaa_interneuron_conn[N_IC]

objref r
r = new Random(123456789) // use a different seed if you need different random streams; the seed used for the figures in the paper was 123456789

for i=0,N_IC-1 {
    
    wdr.dendrite ampar_interneuron[i]  = new AMPA_DynSyn(0.5)
    ampar_interneuron[i].tau_fac   = 0.1
    ampar_interneuron[i].tau_rec   = 0.1
    ampar_interneuron[i].U1        = 1.0
    ampar_interneuron[i].tau_rise  = 0.1
    ampar_interneuron[i].tau_decay = 5.0
    
    wdr.dendrite nmdar_interneuron[i]  = new NMDA_DynSyn(0.5)
    nmdar_interneuron[i].tau_fac   = 0.1
    nmdar_interneuron[i].tau_rec   = 0.1
    nmdar_interneuron[i].U1        = 1.0
    nmdar_interneuron[i].tau_rise  = 2.0
    nmdar_interneuron[i].tau_decay = 100.0
    
    wdr.dendrite gabaar_interneuron[i]  = new GABAa_DynSyn(0.5)
    gabaar_interneuron[i].tau_fac   = 0.1 // NOT SUBJECT TO SHORT-TERM PLASTICITY
    gabaar_interneuron[i].tau_rec   = 0.1
    gabaar_interneuron[i].U1        = 1.0
    gabaar_interneuron[i].tau_rise  = 0.1
    gabaar_interneuron[i].tau_decay = 10.0
    
    
    event_time = C_delay + C_dispersion * r.repick()  
    printf("\n C-fibres activations: fibre [%d] - time = %f ms", i, event_time)
    
    //netcon = new NetCon(source, target, threshold [mV], delay [ms], weight [uS])
    interneuron.hillock ampa_interneuron_conn[i]  = new NetCon(&v(0.5), ampar_interneuron[i],  -30.0, event_time,     0.00020) //0.00020
    interneuron.hillock nmda_interneuron_conn[i]  = new NetCon(&v(0.5), nmdar_interneuron[i],  -30.0, event_time,     0.00020) //0.00020
    interneuron.hillock gabaa_interneuron_conn[i] = new NetCon(&v(0.5), gabaar_interneuron[i], -30.0, event_time+1.0, 0.00020) //+1.0, 0.00020
    
}




	
// 5 ****************************************************************************************************

// PROVIDE NOISE TO THE WDR MEMBRANE POTENTIAL

// create source of stimulation for inhibitory and excitatory noise synapses
objref stim_inh, stim_exc

wdr.soma stim_inh = new NetStim(0.5)
stim_inh.interval	= 10.0
stim_inh.start		= 0.0
stim_inh.number		= 1000000
stim_inh.noise		= 1.0

wdr.soma stim_exc = new NetStim(0.5)
stim_exc.interval	= 10.0
stim_exc.start		= 0.0
stim_exc.number		= 1000000
stim_exc.noise		= 1.0


objref gabaar_noise, ampar_noise

wdr.soma gabaar_noise = new GABAa_DynSyn(0.5) // inhibitory
gabaar_noise.tau_fac    = 0.1
gabaar_noise.tau_rec    = 0.1
gabaar_noise.U1         = 1.0 
gabaar_noise.tau_rise   = 0.1
gabaar_noise.tau_decay  = 5.0

wdr.soma ampar_noise = new AMPA_DynSyn(0.5)   // excitatory
ampar_noise.tau_fac    = 0.1
ampar_noise.tau_rec    = 0.1
ampar_noise.U1         = 1.0 
ampar_noise.tau_rise   = 0.1
ampar_noise.tau_decay  = 5.0


//netcon = new NetCon(source, target, threshold [mV], delay [ms], weight [uS])
objref ampa_noise_conn, gabaa_noise_conn
ampa_noise_conn  = new NetCon(stim_exc, ampar_noise,  -30.0, 0.0, 0.0001) //0.0001
gabaa_noise_conn = new NetCon(stim_exc, gabaar_noise, -30.0, 0.0, 0.0001) //0.0001
//the mean of the noise current is roughly 0.001 mA/cm²

//ampa_noise_conn  = new NetCon(stim_exc, ampar_noise,  -30.0, 0.0, 0.000) //0.000100
//gabaa_noise_conn = new NetCon(stim_inh, gabaar_noise, -30.0, 0.0, 0.000) //0.000429
//the mean of this synaptic noise current (0.000429 GABAA + 0.000100 AMPA) is roughly 0.0 mA/cm²;
//the stdev of fluctuations produced in the membrane potential is 0.5226 mV
//this assumes a resting potential of -64.8806 mV (E_GABAA=-80mV; E_AMPA=0mV)




// 6 ****************************************************************************************************

// FINAL COMMANDS

// store spike-times
objref nc, nil, vec
interneuron.soma nc = new NetCon(&v(.5), nil, -30.0, 0.0, 1.0)
vec = new Vector()
nc.record(vec)




// ---------- STORE TOTAL SYNAPTIC CURRENTS ----------
// targeting WDR
objref vec_ampar_Ad[N_A], vec_nmdar_Ad[N_A]
for s=0,N_A-1 {
    vec_ampar_Ad[s] = new Vector()
    vec_ampar_Ad[s].record(&ampar_Ad[s].g)
    
    vec_nmdar_Ad[s] = new Vector()
    vec_nmdar_Ad[s].record(&nmdar_Ad[s].g)
}

objref vec_nk1r_C_WDR
vec_nk1r_C_WDR = new Vector()
vec_nk1r_C_WDR.record(&nk1r_C_WDR.g)

objref vec_ampar_interneuron[N_IC], vec_nmdar_interneuron[N_IC], vec_gabaar_interneuron[N_IC]
for s=0,N_IC-1 {
    vec_ampar_interneuron[s] = new Vector()  
    vec_ampar_interneuron[s].record(&ampar_interneuron[s].g)
    
    vec_nmdar_interneuron[s] = new Vector()
    vec_nmdar_interneuron[s].record(&nmdar_interneuron[s].g)
    
    vec_gabaar_interneuron[s] = new Vector()
    vec_gabaar_interneuron[s].record(&gabaar_interneuron[s].g)
}

//targeting Interneuron
objref vec_ampar_C
vec_ampar_C = new Vector()
vec_ampar_C.record(&ampar_C.g)

objref vec_nmdar_C
vec_nmdar_C = new Vector()
vec_nmdar_C.record(&nmdar_C.g)

objref vec_nk1r_C
vec_nk1r_C = new Vector()
vec_nk1r_C.record(&nk1r_C.g)
// -----------------------------------------------------------




access wdr.soma

load_file("wdr-complete-model.ses")

tstop = (n_stim_sets + 1) * T2 + start_time

celsius = 36

dt = 0.0125 //has finite representation in binary


init()

//forall cvode.print_event_queue()

run()


// write spike times to file "wdr_spike_times.dat"
objref fileobj
fileobj = new File()
fileobj.wopen("wdr_spike_times.dat")
vec.printf(fileobj)
fileobj.close()





// -------- Write total synaptic currents to files ------
// ampar_Ad, nmdar_Ad
for s=1,N_A-1 {
    vec_ampar_Ad[0].add(vec_ampar_Ad[s])
    vec_nmdar_Ad[0].add(vec_nmdar_Ad[s])
}

objref fileobj
fileobj = new File()
fileobj.wopen("vec_ampar_Ad.dat")
vec_ampar_Ad[0].printf(fileobj)
fileobj.close()

objref fileobj
fileobj = new File()
fileobj.wopen("vec_nmdar_Ad.dat")
vec_nmdar_Ad[0].printf(fileobj)
fileobj.close()

// nk1r_C_WDR
objref fileobj
fileobj = new File()
fileobj.wopen("vec_nk1r_C_WDR.dat")
vec_nk1r_C_WDR.printf(fileobj)
fileobj.close()

// ampar_interneuron, nmdar_interneuron, gabaar_interneuron
for s=1,N_IC-1 {   
    vec_ampar_interneuron[0].add(vec_ampar_interneuron[s])    
    vec_nmdar_interneuron[0].add(vec_nmdar_interneuron[s])    
    vec_gabaar_interneuron[0].add(vec_gabaar_interneuron[s])
}

objref fileobj
fileobj = new File()
fileobj.wopen("vec_ampar_interneuron.dat")
vec_ampar_interneuron[0].printf(fileobj)
fileobj.close()

objref fileobj
fileobj = new File()
fileobj.wopen("vec_nmdar_interneuron.dat")
vec_nmdar_interneuron[0].printf(fileobj)
fileobj.close()

objref fileobj
fileobj = new File()
fileobj.wopen("vec_gabaar_interneuron.dat")
vec_gabaar_interneuron[0].printf(fileobj)
fileobj.close()



// nk1r_C
objref fileobj
fileobj = new File()
fileobj.wopen("vec_nk1r_C.dat")
vec_nk1r_C.printf(fileobj)
fileobj.close()

// ampar_C
objref fileobj
fileobj = new File()
fileobj.wopen("vec_ampar_C.dat")
vec_ampar_C.printf(fileobj)
fileobj.close()

// nmdar_C
objref fileobj
fileobj = new File()
fileobj.wopen("vec_nmdar_C.dat")
vec_nmdar_C.printf(fileobj)
fileobj.close()





//************************************************************************************
//UNITS	    
//Category						Variable		Units
//Time							t				[ms]
//Voltage						v				[mV]
//Current						i				[mA/cm2] (distributed)	[nA] (point process)
//Concentration					ko, ki, etc.	[mM]
//Specific capacitance			cm				[uf/cm2]
//Length						diam, L			[um]
//Conductance					g				[S/cm2] (distributed)	[uS] (point process)
//Cytoplasmic resistivity		Ra				[ohm cm]
//Resistance					Ri				[10E6 ohm]