#include <../../nrnconf.h>
/* /local/src/master/nrn/src/nrnoc/synapse.c,v 1.2 1997/08/15 13:04:13 hines Exp */
/* modified from fstim.c */

/*
fsyn(maxnum)
 allocates space for maxnum synapses. Space for
 previously existing synapses is released. All synapses initialized to
 0 maximum conductance.

fsyn(i, loc, delay, tau, gmax, erev)
 The ith synapse is injected at parameter `loc'
 different synapses do not concatenate but can ride on top of
 each other. delay refers to onset of synapse relative to t=0
 delay and duration are in msec.
 stim in namps.

 a synaptic current defined by
 	i = g * (v - erev) 	i(nanoamps), g(microsiemens);
 	where
 	 g = 0 for t < delay and
 	 g = gmax * (t - delay)/tau * exp(-(t - delay - tau)/tau)
 	  for t > onset
 this has the property that the maximum value is gmax and occurs at
  t = delay + tau.
  
fsyni(i)
 returns synaptic current for ith synapse at the value of the
 global time t in units of nanoamps.

fsyng(i)
 returns synaptic conductance for ith synapse at the value of the
 global time t.

*/

#include <stdlib.h>
#include "neuron.h"
#include "section.h"
#include "nrniv_mf.h"

#define nt_t nrn_threads->_t

/* impress the stimulus code to do synapses */
typedef struct Stimulus {
	double loc;	/* parameter location (0--1) */
	double delay;	/* value of t in msec for onset */
	double duration;/* turns off at t = delay + duration */
	double mag;	/* conductance in microsiemens */
	double erev;
	double mag_seg;	/* value added to rhs, depends on area of seg*/
	double g;	/* holds conductance when current calculated */
	Node *pnd;	/* segment location */
	Section* sec;
} Stimulus;

static int maxstim = 0;		/* size of stimulus array */
static Stimulus *pstim;		/* pointer to stimulus array */
static void free_syn(void);
static void stim_record(int);

void print_syn(void) {
	int i;
	
	if (maxstim == 0) return;
	/*SUPPRESS 440*/
	Printf("fsyn(%d)\n/* section	fsyn( #, loc, delay(ms), tau(ms), conduct(uS), erev(mV)) */\n", maxstim);
	for (i=0; i<maxstim; i++) {
		Printf("%-15s fsyn(%2d,%4g,%10g,%8g,%14g,%9g)\n",
		secname(pstim[i].sec), i,
		pstim[i].loc, pstim[i].delay, pstim[i].duration, pstim[i].mag,
		pstim[i].erev);
	}
}

static double stimulus();

void fsyni(void) {
	int i;
	double cur;
	
	i = chkarg(1, 0., (double)(maxstim-1));
	if ((cur = stimulus(i)) != 0.) {
		cur *= pstim[i].mag / pstim[i].mag_seg;
	}
	hoc_retpushx(cur);
}

void fsyng(void) {
	int i;
	double g=0.0;
	
	i = chkarg(1, 0., (double)(maxstim-1));
	IGNORE(stimulus(i));
	g = pstim[i].g;
	if (g != 0.) {
		g *= pstim[i].mag / pstim[i].mag_seg;
	}
	hoc_retpushx(g);
}

void fsyn(void) {
	int i;

	if (nrn_nthread > 1) {
		hoc_execerror("fsyn does not allow threads", "");
	}
	i = chkarg(1, 0., 10000.);
	if (ifarg(2)) {
		if (i >= maxstim) {
			hoc_execerror("index out of range", (char *)0);
		}
		pstim[i].loc = chkarg(2, 0., 1.);
		pstim[i].delay = chkarg(3, 0., 1e21);
		pstim[i].duration = chkarg(4, 0., 1e21);
		pstim[i].mag = *getarg(5);
		pstim[i].erev = *getarg(6);
		pstim[i].sec = chk_access();
		section_ref(pstim[i].sec);
		stim_record(i);
	} else {
		free_syn();
		maxstim = i;
		if (maxstim) {
			pstim = (Stimulus *)emalloc((unsigned)(maxstim * sizeof(Stimulus)));
		}
		for (i = 0; i<maxstim; i++) {
			pstim[i].loc = 0;
			pstim[i].mag = 0.;
			pstim[i].delay = 1e20;
			pstim[i].duration = 0.;
			pstim[i].erev = 0.;
			pstim[i].sec = 0;
			stim_record(i);
		}
	}
	hoc_retpushx(0.);
}

static void free_syn(void) {
	int i;
	if (maxstim) {
		for (i=0; i < maxstim; ++i) {
			if (pstim[i].sec) {
				section_unref(pstim[i].sec);
			}
		}
		free((char *)pstim);
		maxstim = 0;
	}
}

static void stim_record(int i)	/*fill in the section info*/
{
	Node *node_ptr();
	double area;
	Section* sec;

	sec = pstim[i].sec;
	if (sec) {
		if (sec->prop) {
			pstim[i].pnd = node_ptr(sec, pstim[i].loc, &area);
			pstim[i].mag_seg = 1.e2*pstim[i].mag / area;
		}else{
			section_unref(sec);
			pstim[i].sec = 0;
		}
	}
}

void synapse_prepare(void) {
	int i;
	
	for (i=0; i<maxstim; i++) {
		stim_record(i);
	}
}

static double alpha();

static double stimulus(int i)
{
	double x, g;
	
	if ((g = pstim[i].mag_seg) == 0.0) {
		pstim[i].g = 0.0;
		return 0.0;
	}
#if CVODE
	at_time(nrn_threads, pstim[i].delay);
#endif
	x = (nt_t - pstim[i].delay)/pstim[i].duration;
	pstim[i].g = g * alpha(x);
	return pstim[i].g * (NODEV(pstim[i].pnd) - pstim[i].erev);
}

static double alpha(double x)
{
	double exp();
	
	if (x > 0.0 && x < 10.0) {
		return x * exp(-x + 1.0);
	}
	return 0.0;
}

void activsynapse_rhs(void) {
	
	int i;
	for (i=0; i<maxstim; i++) {
		if (pstim[i].sec) {
			NODERHS(pstim[i].pnd) -=  stimulus(i);
		}
	}
}

void activsynapse_lhs() {
	
	int i;

	for (i=0; i<maxstim; i++) {
		if (pstim[i].sec) {
			NODED(pstim[i].pnd) += pstim[i].g;
		}
	}
}

