# ----------------------------------------------------------------------------
# Contributors: Renan O. Shimoura
#               Nilton L. Kamiji
#               Rodrigo F. O. Pena
#               Vinicius L. Cordeiro
#               Cesar C. Ceballos
#               Cecilia Romaro
#               Antonio C. Roque
# ----------------------------------------------------------------------------
# References:
#
# *The Cell-Type Specific Cortical Microcircuit: Relating Structure and Activity
# in a Full-Scale Spiking Network Model*,
# Tobias C. Potjans and Markus Diesmann,
# Cerebral Cortex, 24(3):785-806, 2014.
# ----------------------------------------------------------------------------
# File description:
#
# Function responsible to make connections between different populations of
# neurons and to connect stimulation (background or DC currents).
# ----------------------------------------------------------------------------

from netParams import *

def PDnet(NeuronGroup, stim, bg_type, w_ex, g, bg_freq, nsyn_type, thal):

	w_ex = w_ex*pA		   	# excitatory synaptic weight
	std_w_ex = 0.1*w_ex     # standard deviation weigth

	# Background number per layer
	if bg_type == 0:
		# layer-specific:
		bg_layer = bg_layer_specific
	elif bg_type == 1:
		# layer-independent:
		bg_layer = bg_layer_independent
	elif bg_type == 2:
		bg_layer = zeros(8)
		#layer-independent-random:
		for i in range(0,8,2):
		    # range of the number of inputs given to an excitatory population:
		    exc_bound_A = bg_layer_specific[i]
		    exc_bound_B = bg_layer_independent[i]
		    diff_exc = abs(exc_bound_A-exc_bound_B)

		    # randomly choosing a number for the external input to an excitatory population:
		    if exc_bound_A<=exc_bound_B: exc_input = exc_bound_A + rand()*diff_exc
		    elif exc_bound_A>exc_bound_B: exc_input = exc_bound_B + rand()*diff_exc

		    # range of the number of inputs given to an inhibitory population:
		    if i!=6: inh_bound_A = ((1-0.1)/(1+0.1))*exc_input		# eq. 4 from the article
		    else: inh_bound_A = ((1-0.2)/(1+0.2))*exc_input		# eq. 4 from the article
		    inh_bound_B = exc_input
		    diff_inh = abs(inh_bound_A-inh_bound_B)

		    # randomly choosing a number for the external input to an inhibitory population:
		    if inh_bound_A<=inh_bound_B: inh_input = inh_bound_A + rand()*diff_inh
		    else: inh_input = inh_bound_B + rand()*diff_inh

		    # array created to save the values:
		    bg_layer[i] = int(exc_input)
		    bg_layer[i+1] = int(inh_input)

	pop = [] # Stores NeuronGroups, one for each population
	for r in range(0, 8):
		pop.append(NeuronGroup[nn_cum[r]:nn_cum[r+1]])

		# DC-current normalized by population
		if (stim == 1):
			NeuronGroup.Iext[pop[r]] = 0.3512*pA*bg_layer[r]

	###########################################################################
	# Creating synapse connections
	###########################################################################

	syn_model = '''
				w:amp			# synaptic weight
				'''

	# equations executed only when presynaptic spike occurs:
	# for excitatory connections
	pre_eq = '''
			I_post += w
			'''

	con = [] # Stores connections

	###########################################################################
	# Connecting neurons
	###########################################################################
	pre_index = []
	post_index = []

	for c in range(0, 8):
		for r in range(0, 8):

			if (nsyn_type==0):
				# number of synapses calculated with equation 3 from the article
				nsyn = int(log(1.0-table[r][c])/log(1.0 - (1.0/float(n_layer[c]*n_layer[r]))))
			elif (nsyn_type==1):
				# number of synapses calculated with equation 5 from the article
				nsyn = int(n_layer[c]*n_layer[r]*table[r][c])

			pre_index = randint(n_layer[c], size=nsyn)
			post_index = randint(n_layer[r], size=nsyn)

			if nsyn<1:
				pass
			else:
				# Excitatory connections
				if (c % 2) == 0:
					# Synaptic weight from L4e to L2/3e is doubled
					if c == 2 and r == 0:
						con.append(Synapses(pop[c], pop[r], model=syn_model, on_pre=pre_eq))
						con[-1].connect(i = pre_index, j = post_index)
						con[-1].w = '2.0*clip((w_ex + std_w_ex*randn()),w_ex*0.0, w_ex*inf)'
					else:
						con.append(Synapses(pop[c], pop[r], model=syn_model, on_pre=pre_eq))
						con[-1].connect(i = pre_index, j = post_index)
						con[-1].w = 'clip((w_ex + std_w_ex*randn()),w_ex*0.0, w_ex*inf)'
					con[-1].delay = 'clip(d_ex + std_d_ex*randn(), 0.1*ms, d_ex*inf)'

				# Inhibitory connections
				else:
					con.append(Synapses(pop[c], pop[r], model=syn_model, on_pre=pre_eq))
					con[-1].connect(i = pre_index, j = post_index)
					con[-1].w = '-g*clip((w_ex + std_w_ex*randn()),w_ex*0.0, w_ex*inf)'
					con[-1].delay = 'clip(d_in + std_d_in*randn(), 0.1*ms, d_in*inf)'

	###########################################################################
	# Creating poissonian background inputs
	###########################################################################
	bg_in  = []
	if (stim==0):
		for r in range(0, 8):
			bg_in.append(PoissonInput(pop[r], 'I', bg_layer[r], bg_freq*Hz, weight=w_ex))

	###############################################################################
	# Creating thalamic neurons as poissonian inputs
	###############################################################################
	thal_con = []
	thal_input = []
	if thal=="ON":
		thal_input = PoissonGroup(n_layer[8], rates=120.0*Hz)	#from PD paper: rates=15Hz
		for r in range(0,8):
			thal_con.append(Synapses(thal_input, pop[r], model=syn_model, on_pre=pre_eq))
			thal_con[-1].connect(p=table[r][8])
			thal_con[-1].w = 0.0

	###########################################################################
	# Creating spike monitors
	###########################################################################
	smon_net = SpikeMonitor(NeuronGroup)

	return pop, con, bg_in, smon_net, thal_input ,thal_con