#include <../../nrnconf.h>
#include <string.h>
#include <stdlib.h>
#include <OS/list.h>
#include "nrnoc2iv.h"
#include "kschan.h"
#include "kssingle.h"

// needed for DiscreteEvent aspect
#include "netcvode.h"
#include "cvodeobj.h"

//----------------
// KSSingleTrans, KSSingleState is only apparently redundant with KSTrans
// and KSState. i.e. they are identical only if KSChan has only a single
// gating complex with a power of 1. If there are G gating complexes with
// powers pg and numbers of states sg (where g ranges from 0 to G-1) then
// the number of distinct states for the gate g is the binomial factor
// nsg = (sg + pg - 1, pg). i.e. (sg + pg - 1)! / pg! / (sg - 1)!
// and the total number of distinct states is the product of the nsg.
// consider m^3 * h, and suppose m is open and M is closed then there
// are three ways to go from mmmh to Mmmh and one way to go to mmmH. i.e
// MMMH <-> mMMH (3am, bm)   MMMH <-> MMMh (ah, bh)   MMMh <-> mMMh (3am, bm)
// mMMH <-> mmMH (2am, 2bm)  mMMH <-> mMMh (ah, bh)   mMMh <-> mmMh (2am, 2bm)
// mmMH <-> mmmH (am, 3bm)   mmMH <-> mmMh (ah, bh)   mmMh <-> mmmh (am, 3bm)
// there are 8 states and 18 transitions.
//
// Following a single particle as it moves from state to state is
// the simplest case. It suffices to keep track of the state index
// and, following ...
// use two random numbers. One random number is transformed into
// a negative exponential distribution
// and defines the time interval to the next transition. The rate is the
// sum of all the possible transition rates. The other is uniformly
// distibuted between 0 and 1 and is used to determine which transition
// branch was taken according to the
//
// For the case of N particles distributed among the states we use
// an integer array in which an element is the integer population of the
// corresponding state. Too bad we lose the efficiency of the implicit handling
// of 0 population states but I don't see how it can be helped.
// Again we use two random numbers. The transition rate of some event, i.e.
// some particle moves to another state, is the sum over all states of the
// state population times the sum of all the possible transition rates for
// that state. And, again, the second random number defines the branch taken.
// Note that this is still an exact stochastic simulation in that only
// one thing happens at a time.
//
// It remains to discuss how things are affected by changing rates.
// We make the approximation that rates are constant within a dt. In
// consequence we only need to compute the rate information once per dt
// (at least for the N particle case) no matter how many transitions have
// to be followed til the last one is beyond the t0+dt point. The question
// is what to do with that last interval which remains unused, ie. time
// has not reached the "transition time"
// Also we'd like to be efficient when the average interval is much greater
// than dt.
// The simplest implementation is to just discard the random numbers and
// assert that the time is t0 <- t0+dt and compute from that point
// at the next solve step. The alternative is to hold the last random
// pair and recalculate the interval (assuming the rate has changed)
// to obtain (from the last actual transition time) a new transition time.
// and see if it is now less than the new t0 + dt. Note that there is
// some extra error here because of the assumption of constant rate over the
// last entire random interval and not just the last dt.
// Note that we do not have to worry about this (or at least worry less)
// in voltage clamp context as well as in contexts in which intervals are
// seldom > dt. However no analysis has been done and I can't demonstrate
// that the statistical error of following a single channel
// during an action potential is not substantial. We compromise and
// test the voltage. If it has changed then we reset the transition time
// to t0 and repick random numbers. If it has not changed significantly,
// then we keep the last random interval and use that when t0+dt passes the
// up to then unexecuted transition.
//----------------
// The above broad design envisioned a parallel structure involving
// a single kinetic scheme isomorphic to the user level spec which
// may have multiple gates raised to powers. The initial implementation
// for the KSSingle constructor, however, for simplicity assumed that the
// user level spec was only one ks gate complex with power 1. In any case
// the question of state read out and conductance calculation arises
// and in either case it seems to make most sense that the standard
// state array in the prop->param array be used. With the current
// limited implementation, KSSingleNodeData.statelist_ is conceptually
// identical to the prop->param states. In the envisioned full implementation
// the least violent possibility is to keep the user level spec as much
// as possible but to expand the prop->param states to correspond
// to single channel requirements. Another possibility which does a
// lot more violence to the user spec but makes the current implementation
// almost complete is to replace the user spec with a single channel
// adequate ks scheme. Merely stating it is sufficient to reject it.
// A third possiblity is to not use the prop->param states at all since
// their user level spec does not correspond to the ks single channel
// states. But this makes GUI interface not re-usable (e.g. plotting
// a state) and the question of determining states values at the user
// level remains.  The last possibility is to not allow single channel
// mode unless there is only one ks gate complex raised to a single power
// and coelesce the redundant structures.
// So it seems that the best extension so far is to
// keep the user level spec in KSChan but when in single channel mode,
// define the prop->param state array to correspond to single channel
// states. Since we are going that far we might as well go the whole
// way and also introduce a new Nsingle PARAMETER and make the
// dparam[2]._pvoid == KSSingleNodeData existent only in single channel
// mode. Note that since KSChan.nstate_ <= KSSingle.nstate_, initialization
// can fill the KSChan interpreted prop->param states with the fractions,
// and then, per normal, the prop->param states interpreted at KSSingle
// states get filled with the right integers. We'll also need a naming
// convention for KSSingle states since that is how they get accessed at
// the hoc level. We need names for the hh open and closed state
// if the hh state name is "a". We need names generated for powers.
// Products are easy, just concatenate the names. For powers of hh states we
// use the hh state name followed by the number that are open. eg an m^3
// gate gets the names m0, m1, m2, m3. Thus m^3*h has the names
// m0h0 m1h0 m2h0 m3h0 m0h1 m1h1 m2h1 m3h1 (m3h1 is the only open state)
// Kind of makes mh[4][2] attractive.
// The combinatorics for ks gates raised to a power
// is more complex and it is not clear that that is so useful. I think
// it makes sense to ignore that and use array indices only for hh gates
// raised to a power. Thus a 3 state gate multiplied by an h hh gate would
// have the names c0h[2], c1h[2], oh[2]
// The nice aspect of this is that the current implementation can be
// easily transformed since the transformation of prop->param states can
// be deferred to later. i.e there seems to be a nice incremental path
// to a full implementation without removing at one step what was added
// at the previous step. The first step is to replace the not always used
// int* KSSingleNodeData.statelist_ with an always used
// double* KSSingleNodeData.statepop_ which points into the prop->param.
//----------------

extern "C" {
extern int cvode_active_;
}
extern NetCvode* net_cvode_instance;

double KSSingle::vres_;
unsigned int KSSingle::idum_;

KSSingle::KSSingle(KSChan* c) {
	//implemenation assumes one ks gate complex with power 1
	int i;
	sndindex_ = 2;
	nstate_ = c->nstate_;
	states_ = new KSSingleState[nstate_];
	ntrans_ = 2 * c->ntrans_;
	transitions_ = new KSSingleTrans[ntrans_];
	rval_ = new double[Math::max(ntrans_, nstate_)];
	uses_ligands_ = false;
	for (i=0; i < c->ntrans_; ++i) {
	    {
		KSSingleTrans& tf = transitions_[2*i];
		tf.kst_ = c->trans_ + i;
		if (tf.kst_->type_ >= 2) { uses_ligands_ = true; }
		tf.f_ = true;
		tf.fac_ = 1.;
		tf.src_ = tf.kst_->src_;
		tf.target_ = tf.kst_->target_;
	    }{
		KSSingleTrans& tf = transitions_[2*i + 1];
		tf.kst_ = c->trans_ + i;
		tf.f_ = false;
		tf.fac_ = 1.;
		tf.src_ = tf.kst_->target_;
		tf.target_ = tf.kst_->src_;
	    }
	}
	// The transition list for each source state is required for
	// N=1 single channels.
	// count first
	for (i=0; i < ntrans_; ++i) {
		states_[transitions_[i].src_].ntrans_++;
	}
	// allocate and reset count
	for (i=0; i < nstate_; ++i) {
		KSSingleState& ss = states_[i];
		ss.transitions_ = new int[ss.ntrans_];
		ss.ntrans_ = 0;
	}
	// link
	for (i=0; i < ntrans_; ++i) {
		KSSingleState& ss = states_[transitions_[i].src_];
		ss.transitions_[ss.ntrans_] = i;
		++ss.ntrans_;
	}
}

KSSingle::~KSSingle() {
	delete [] states_;
	delete [] transitions_;
	delete [] rval_;
}

KSSingleState::KSSingleState() {
	ntrans_ = 0;
	transitions_ = NULL;
}
KSSingleState::~KSSingleState() {
	if (transitions_) {
		delete [] transitions_;
	}
}

KSSingleTrans::KSSingleTrans() {
}
KSSingleTrans::~KSSingleTrans() {
}
double KSSingleTrans::rate(Point_process* pnt) {
	if (kst_->type_ < 2) {
		return rate(NODEV(pnt->node));
	}else{
		return rate(pnt->prop->dparam);
	}
}

KSSingleNodeData::KSSingleNodeData() {
	statepop_ = NULL;
	nsingle_ = 1;
}

KSSingleNodeData::~KSSingleNodeData() {
}

void KSSingleNodeData::deliver(double tt, NetCvode* nc, NrnThread* nt) {
	++KSSingle::singleevent_deliver_;
	Cvode* cv = (Cvode*)((*ppnt_)->nvi_);
	if (cv) {
		nc->retreat(tt, cv);
		cv->set_init_flag();
	}
	assert(nt->_t == tt);
	vlast_ = NODEV((*ppnt_)->node);
	if (nsingle_ == 1) {
		kss_->do1trans(this);
	}else{
		kss_->doNtrans(this);
	}
	qi_ = nc->event(t1_, this, nt);
}

void KSSingleNodeData::pr(const char* s, double tt, NetCvode* nc) {
	printf("%s %s %.15g\n", s, hoc_object_name((*ppnt_)->ob), tt);
}

void KSSingle::state(Node* nd, double* p, Datum* pd, NrnThread* nt) {
	// integrate from t-dt to t
	int i;
	double v = NODEV(nd);
	KSSingleNodeData* snd = (KSSingleNodeData*)pd[sndindex_]._pvoid;
	// if truly single channel, as opposed to N single channels
	// then follow the one populated state. Otherwise do the
	// general case
	if (snd->nsingle_ == 1) {
		one(v, snd, nt);
	}else{
		multi(v, snd, nt);
	}
}

void KSSingle::cv_update(Node* nd, double* p, Datum* pd, NrnThread* nt) {
	// if v changed then need to move the outstanding
	// single channel event time to a recalculated time
	int i;
	double v = NODEV(nd);
	KSSingleNodeData* snd = (KSSingleNodeData*)pd[sndindex_]._pvoid;
	if (uses_ligands_ || !vsame(v, snd->vlast_)) {
		assert(nt->_t < snd->t1_);
		snd->vlast_ = v;
		snd->t0_ = nt->_t;
		if (snd->nsingle_ == 1) {
			next1trans(snd);
		}else{
			nextNtrans(snd);
		}
		net_cvode_instance->move_event(snd->qi_, snd->t1_, nt);
		++KSSingle::singleevent_move_;
	}
}

int KSSingle::rvalrand(int n) {
	int i;
	--n;
	double x = unifrand(rval_[n]);
	for (i=0; i < n; ++i) {
		if (rval_[i] >= x) {
			return i;
		}
	}
	return n; 
}

void KSSingle::one(double v, KSSingleNodeData* snd, NrnThread* nt) {
	if (uses_ligands_ || !vsame(v, snd->vlast_)) {
		snd->vlast_ = v;
		snd->t0_ = nt->_t - nt->_dt;
		next1trans(snd);
	}
	while (snd->t1_ <= nt->_t) {
		snd->vlast_ = v;
		do1trans(snd);
	}
}

void KSSingle::do1trans(KSSingleNodeData* snd) {
	snd->t0_ = snd->t1_;
//printf("KSSingle::do1trans t1=%g old=%d ", snd->t1_, snd->filledstate_);
	snd->statepop_[snd->filledstate_] = 0.;
	snd->filledstate_ = transitions_[snd->next_trans_].target_;
	snd->statepop_[snd->filledstate_] = 1.;
//printf("new=%d \n", snd->filledstate_);
	next1trans(snd);
}

void KSSingle::next1trans(KSSingleNodeData* snd) {
	int i;
	KSSingleState* ss = states_ + snd->filledstate_;
	double x = 0;
	for (i = 0; i < ss->ntrans_; ++i) {
		KSSingleTrans* st = transitions_ + ss->transitions_[i];
		x += st->rate(*snd->ppnt_);
		rval_[i] = x;
	}
	if (x > 1e-9) {
		snd->t1_ = exprand()/x + snd->t0_;
		snd->next_trans_ = ss->transitions_[rvalrand(ss->ntrans_)];
	}else{
		snd->t1_ = 1e9 + snd->t0_;
		snd->next_trans_ = ss->transitions_[0];
	}
//printf("KSSingle::next1trans v=%g t1_=%g %d (%d, %d)\n", snd->vlast_, snd->t1_, snd->next_trans_,
//transitions_[snd->next_trans_].src_, transitions_[snd->next_trans_].target_);
}
void KSSingle::multi(double v, KSSingleNodeData* snd, NrnThread* nt) {
	if (uses_ligands_ || !vsame(v, snd->vlast_)) {
		snd->vlast_ = v;
		snd->t0_ = nt->_t - nt->_dt;
		nextNtrans(snd);
	}
	while (snd->t1_ <= nt->_t) {
		snd->vlast_ = v;
		doNtrans(snd);
	}
}

void KSSingle::doNtrans(KSSingleNodeData* snd) {
	snd->t0_ = snd->t1_;
	KSSingleTrans* st = transitions_ + snd->next_trans_;
	assert(snd->statepop_[st->src_] >= 1.);
	--snd->statepop_[st->src_];
	++snd->statepop_[st->target_];
//printf("KSSingle::doNtrans t1=%g %d with %g -> %d with %g\n", snd->t1_,
//st->src_, snd->statepop_[st->src_], st->target_, snd->statepop_[st->target_]);
	nextNtrans(snd);
}

void KSSingle::nextNtrans(KSSingleNodeData* snd) {
	int i;
	double x = 0;
	for (i = 0; i < ntrans_; ++i) {
		KSSingleTrans* st = transitions_ + i;
		x += snd->statepop_[st->src_] * st->rate(*snd->ppnt_);
		rval_[i] = x;
	}
	if (x > 1e-9) {
		snd->t1_ = exprand()/x + snd->t0_;
		snd->next_trans_ = rvalrand(ntrans_);
	}else{
		snd->t1_ = 1e9 + snd->t0_;
		snd->next_trans_ = 0;
	}
}

void KSSingle::alloc(Prop* p, int sindex) { // and discard old if not NULL
	KSSingleNodeData* snd = (KSSingleNodeData*)(p->dparam[2]._pvoid);
	if (snd) {
		delete snd;
	}
	snd = new KSSingleNodeData();
	snd->kss_ = this;
	snd->ppnt_ = (Point_process**)(&p->dparam[1]._pvoid);
	p->dparam[2]._pvoid = snd;
	snd->statepop_ = p->param + sindex;
}

void KSSingle::init(double v, double* s, KSSingleNodeData* snd, NrnThread* nt) {
	// assuming 1-1 correspondence between KSChan and KSSingle states
	// place steady state population intervals end to end
	int i;
	double x = 0;
	snd->qi_ = NULL;
	snd->t0_ = nt->_t;
	snd->vlast_ = v;
	for (i=0; i < nstate_; ++i) {
		x += s[i];	
		rval_[i] = x;
	}
// initialization of complex kinetic schemes often not accurate to 9 decimal places
//	assert(Math::equal(rval_[nstate_ - 1], 1., 1e-9));
	for (i=0; i < nstate_; ++i) {
		snd->statepop_[i] = 0;
	}
	if (snd->nsingle_ == 1) {
		snd->filledstate_ = rvalrand(nstate_);
		++snd->statepop_[snd->filledstate_];
		next1trans(snd);
	}else{
		for (i=0; i < snd->nsingle_; ++i) {
			++snd->statepop_[rvalrand(nstate_)];
		}
		nextNtrans(snd);
//for (i=0; i < nstate_; ++i) {
//	printf("  state %d pop %g\n", i, snd->statepop_[i]);
//}
	}
	if (cvode_active_) {
		snd->qi_ = net_cvode_instance->event(snd->t1_, snd, nrn_threads);
	}
}

void KSChan::nsingle(Point_process* pp, int n) {
	KSSingleNodeData* snd = (KSSingleNodeData*)pp->prop->dparam[2]._pvoid;
	if (snd) {
		snd->nsingle_ = n;
	}
}
int KSChan::nsingle(Point_process* pp) {
	KSSingleNodeData* snd = (KSSingleNodeData*)pp->prop->dparam[2]._pvoid;
	if (snd) {
		return snd->nsingle_;
	}else{
		return 1000000000;
	}
}

