#ifndef netcvode_h
#define netcvode_h

#define PRINT_EVENT 1

#include "mymath.h"
#include "tqueue.h"

struct NrnThread;
class PreSyn;
class HocDataPaths;
class PreSynTable;
class NetCon;
class DiscreteEvent;
class TQItemPool;
class SelfEventPool;
class SelfEvent;
class hoc_Item;
class PlayRecord;
class PlayRecList;
class IvocVect;
class BAMechList;
class MaxStateTable;
class HTList;
class HTListList;
class NetCvode;
class MaxStateItem;
class CvodeThreadData;
class HocEvent;
class HocEventList;
struct BAMech;
struct Section;
struct InterThreadEvent;

class NetCvodeThreadData {
public:
	NetCvodeThreadData();
	virtual ~NetCvodeThreadData();
	void interthread_send(double, DiscreteEvent*, NrnThread*);
	void enqueue(NetCvode*, NrnThread*);
	TQueue* tq_; // for lvardt
	Cvode* lcv_; // for lvardt
	TQueue* tqe_;
	hoc_Item* psl_thr_; //for presyns with fixed step threshold checking
	SelfEventPool* sepool_;
	TQItemPool* tpool_;
	InterThreadEvent* inter_thread_events_;
	SelfQueue* selfqueue_;
	MUTDEC
	int nlcv_;
	int ite_cnt_;
	int ite_size_;
	int unreffed_event_cnt_;
	double immediate_deliver_;
};

class NetCvode {
public:
	NetCvode(bool single=true);
	virtual ~NetCvode();
	int solve(double t);
	void statistics(int);
	void spike_stat();
	void re_init(double t0 = 0.);
	int cellindex();
	void states();
	void dstates();
	int fun(double t, double* y, double* ydot);
	void error_weights();
	void acor();
	const char* statename(int, int style=1);
	void localstep(bool); bool localstep();
	bool is_local();
	void use_daspk(bool); bool use_daspk();
	void move_event(TQItem*, double, NrnThread*);
	void remove_event(TQItem*, int threadid);
	TQItem* event(double tdeliver, DiscreteEvent*, NrnThread*);
#if BBTQ == 3 || BBTQ == 4
	TQItem* fifo_event(double tdeliver, DiscreteEvent*, NrnThread*);
#endif
#if BBTQ == 5
	TQItem* bin_event(double tdeliver, DiscreteEvent*, NrnThread*);
#endif
	void send2thread(double, DiscreteEvent*, NrnThread*);
	void null_event(double);
	void tstop_event(double);
	void handle_tstop_event(double, NrnThread* nt);
	void hoc_event(double, const char* hoc_stmt, Object* ppobj = nil, int reinit = 0, Object* pyact=nil);
	NetCon* install_deliver(double* psrc, Section* ssrc, Object* osrc,
		Object* target,	double threshold, double delay,
		double weight
	);
	void presyn_disconnect(PreSyn*);
	void check_thresh(NrnThread*);
	void deliver_net_events(NrnThread*); // for default staggered time step method
	void deliver_events(double til, NrnThread*); // for initialization events
	void solver_prepare();
	void clear_events();
	void init_events();
	void print_event_queue();
	void event_queue_info();
	void vec_event_store();
	void local_retreat(double, Cvode*);
	void retreat(double, Cvode*);
	Object** netconlist();
	int owned_by_thread(double*);
	PlayRecord* playrec_uses(void*);
	void playrec_add(PlayRecord*);
	void playrec_remove(PlayRecord*);
	int playrec_item(PlayRecord*);
	PlayRecord* playrec_item(int);
	PlayRecList*  playrec_list() { return prl_;}
	void simgraph_remove();
	// fixed step continuous play and record
	PlayRecList* fixed_play_;
	PlayRecList* fixed_record_;
	void vecrecord_add(); // hoc interface functions
	void vec_remove();
	void record_init();
	void play_init();
	void fixed_record_continuous(NrnThread*);
	void fixed_play_continuous(NrnThread*);
	static double eps(double x) { return eps_*Math::abs(x); }
	int condition_order() { return condition_order_; }
	void condition_order(int i) { condition_order_ = i; }
	TQueue* event_queue(NrnThread* nt);
	void psl_append(PreSyn*);
	void recalc_ptrs();
public:
	void rtol(double); double rtol(){return rtol_;}
	void atol(double); double atol(){return atol_;}
	double rtol_, atol_;
	void stiff(int); int stiff(){return stiff_;} // 0 nothing stiff, 1 voltage, 2 mechanisms
	void maxorder(int); int maxorder(){return maxorder_;}
	int order(int);
	void minstep(double); double minstep(){return minstep_;}
	void maxstep(double); double maxstep(){return maxstep_;}
	void jacobian(int); int jacobian(){return jacobian_;}
	void structure_change();
	int print_event_;
//	int nlist() { return nlist_; }
//	Cvode* list() { return list_; }
	bool initialized_; // for global step solve.
	void consist_sec_pd(const char*, Section*, double*);
	double state_magnitudes();
	Symbol* name2sym(const char*);
	const char* sym2name(Symbol*);
	int pgvts(double tstop);
	void update_ps2nt();
	void point_receive(int, Point_process*, double*, double);
	bool deliver_event(double til, NrnThread*); //uses TQueue atomically
	bool empty_;
	void delete_list();
	void delete_list(Cvode*);
//private:
public:
	static double eps_;
	int local_microstep(NrnThread*);
	int global_microstep();
	void deliver_least_event(NrnThread*);
	void evaluate_conditions();
	int condition_order_;
	
	int pgvts_event(double& tt);
	DiscreteEvent* pgvts_least(double& tt, int& op, int& init);
	int pgvts_cvode(double tt, int op);

	bool init_global();
	void alloc_list();
	void del_cv_memb_list();
	void del_cv_memb_list(Cvode*);
	void distribute_dinfo(int*, int);
	void playrec_setup();
	void fill_global_ba(NrnThread*, int, BAMechList**);
	void fill_local_ba(int*, NetCvodeThreadData&);
	void fill_local_ba_cnt(int, int*, NetCvodeThreadData&);
	void fill_local_ba_alloc(NetCvodeThreadData&);
	void fill_local_ba_indices(int, int*, NetCvodeThreadData&);
	BAMechList* cvbml(int, BAMech*, Cvode*);
	void maxstate_analyse();
	void maxstate_analyze_1(int, Cvode&, MaxStateItem*, CvodeThreadData&);
	void fornetcon_prepare();
	int fornetcon_change_cnt_;
	double maxstate_analyse(Symbol*, double*);
	void p_construct(int);
	void ps_thread_link(PreSyn*);
	MaxStateTable* mst_;
private:
	int maxorder_, jacobian_, stiff_;
	double maxstep_, minstep_;
	
	int structure_change_cnt_;
	int matrix_change_cnt_;
	bool single_;
	PreSynTable* pst_;
	int pst_cnt_;
	int playrec_change_cnt_;
	PlayRecList* prl_;
	IvocVect* vec_event_store_;
	HocDataPaths* hdp_;
public:
	Cvode* gcv_;
	void set_CVRhsFn();
	bool use_partrans();
	hoc_Item* psl_; //actually a hoc_List
	HTListList* wl_list_; // for faster deliver_net_events when many cvode
	int pcnt_;
	NetCvodeThreadData* p;
	int enqueueing_;
	int use_long_double_;
public:
	MUTDEC // only for enqueueing_ so far.
	void set_enqueueing();
	double allthread_least_t(int& tid);
	int solve_when_threads(double);
	void deliver_events_when_threads(double);
	int global_microstep_when_threads();
	void allthread_handle(double, HocEvent*, NrnThread*);
	void allthread_handle();
	HocEventList* allthread_hocevents_;
};	
	
#endif
