text: #define NO_PYTHON_H 1
  #include <../neuronmusic/nrnmusic.h>
  
  extern "C" {
  extern int nrnmusic;
  extern MPI_Comm nrnmusic_comm;
  
  void nrnmusic_init(int*, char***);
  void nrnmusic_terminate();
  
  void nrnmusic_injectlist(void* vp, double tt);
  void nrnmusic_inject(void* port, int gindex, double tt);
  void nrnmusic_spikehandle(void* vport, double tt, int gindex);
  
  extern Object* nrnpy_po2ho(PyObject*);
  extern PyObject* nrnpy_ho2po(Object*);
  extern Object* hoc_new_object(Symbol*, void*);
  }
  
  MUSIC::Setup* nrnmusic_setup;
  MUSIC::Runtime* nrnmusic_runtime;
  
  class NrnMusicEventHandler : public MUSIC::EventHandlerLocalIndex {
   public:
    void operator() (double t, MUSIC::LocalIndex id);
    void filltable(NRNMUSIC::EventInputPort*, int);
    PreSyn** table;
  };
  
  class MusicPortPair {
  public:
  	MusicPortPair(void* port, int gindex, void* p);
  	virtual ~MusicPortPair();
  	void clear(); // avoid recursion on delete by first calling clear
  	void* port_;
  	int gindex_;
  	MusicPortPair* next_;
  };
  MusicPortPair::MusicPortPair(void* port, int gindex, void* vp) {
  	port_ = port;
  	gindex_ = gindex;
  	next_ = (MusicPortPair*)vp;
  }
  MusicPortPair::~MusicPortPair() {
  	if (next_) { delete next_; }
  }
  void MusicPortPair::clear() {
  	MusicPortPair* i, *j;
  	for (i=next_; i; i = j) {
  		j = i->next_;
  		i->next_ = 0;
  		delete i;
  	}
  }
  
  class NetParMusicEvent: public DiscreteEvent {
  public:
  	NetParMusicEvent();
  	virtual ~NetParMusicEvent();
  	virtual void send(double, NetCvode*, NrnThread*);
  	virtual void deliver(double, NetCvode*, NrnThread*);
  	virtual int type() { return 100; }
  };
  static NetParMusicEvent* npme;
  
  #include <OS/table.h>
  declareTable(PortTable, void*, int) // used as set
  implementTable(PortTable, void*, int)
  static PortTable* music_input_ports;
  static PortTable* music_output_ports;
  
  declareTable(Gi2PreSynTable, int, PreSyn*)
  implementTable(Gi2PreSynTable, int, PreSyn*)
  
  NetParMusicEvent::NetParMusicEvent() {
  }
  NetParMusicEvent::~NetParMusicEvent() {
  }
  void NetParMusicEvent::send(double t, NetCvode* nc, NrnThread* nt) {
  	nc->event(t + usable_mindelay_, this, nt);
  }
  void NetParMusicEvent::deliver(double t, NetCvode* nc, NrnThread* nt) {
  	nrnmusic_runtime->tick();
  	send(t, nc, nt);
  }
  
  void alloc_music_space() {
  	if (music_input_ports) { return; }
  	music_input_ports = new PortTable(64);
  	music_output_ports = new PortTable(64);
  }
  
  void nrnmusic_injectlist(void* vp, double tt) {
  	MusicPortPair* mpp;
  	for (mpp = (MusicPortPair*)vp; mpp; mpp = mpp->next_) {
  		nrnmusic_inject(mpp->port_, mpp->gindex_, tt);
  	}
  }
  
  void nrnmusic_inject(void* vport, int gi, double tt) {
  	//printf("nrnmusic_inject %p %d %g\n", vport, gi, tt);
  	((MUSIC::EventOutputPort*)vport)->
  	  insertEvent(tt / 1000.0, // unit conversion ms -> s
  		      (MUSIC::GlobalIndex)gi);
  }
  
  void NrnMusicEventHandler::filltable(NRNMUSIC::EventInputPort* port, int cnt) {
  	table = new PreSyn*[cnt];
  	int j = 0;
  	for (TableIterator(Gi2PreSynTable) i(*port->gi_table); i.more(); i.next()) {
  		PreSyn* ps = i.cur_value();
  		table[j] = ps;
  		++j;
  	}
  }
  
  void NrnMusicEventHandler::operator () (double t, MUSIC::LocalIndex id) {
  	PreSyn* ps = table[id];
  	ps->send(1000.0 * t, net_cvode_instance, nrn_threads);
  	//printf("event handler t=%g id=%d ps=%p\n", t, int(id), ps);
  }
  
  NRNMUSIC::EventOutputPort* NRNMUSIC::publishEventOutput (std::string id) {
    return new NRNMUSIC::EventOutputPort (nrnmusic_setup, id);
  }
  
  void NRNMUSIC::EventOutputPort::gid2index(int gid, int gi) {
  	// analogous to pc.cell
  	// except pc.cell(gid, nc) has already been called and this
  	// will add this to the PreSyn.music_out_ list.
  	alloc_music_space();
  	PreSyn* ps;
  	if (!gid2out_->find(gid, ps)) {
  		return;
  	}	
  	ps->music_port_ = new MusicPortPair((void*)this, gi, ps->music_port_);
  
  	// to create an IndexList for a later map, need to
  	// save the indices used for each port
  	int i = 0;
  	if (!music_output_ports->find(i, (void*)this)) {
  		music_output_ports->insert((void*)this, i);
  		gi_table = new Gi2PreSynTable(1024);
  	}
  	PreSyn* ps2;
  	assert(!gi_table->find(ps2, gi));
  //printf("gid2index insert %p %d\n", this, gi);
  	gi_table->insert(gi, ps);
  }
  
  NRNMUSIC::EventInputPort::EventInputPort(MUSIC::Setup* s, std::string id)
    : MUSIC::Port (s, id), MUSIC::EventInputPort(s, id) {
  	gi_table = new Gi2PreSynTable(1024);	
  }
  
  NRNMUSIC::EventInputPort* NRNMUSIC::publishEventInput (std::string id) {
    return new NRNMUSIC::EventInputPort (nrnmusic_setup, id);
  }
  
  PyObject* NRNMUSIC::EventInputPort::index2target(int gi, PyObject* ptarget) {
  	// analogous to pc.gid_connect
  	Object* target = nrnpy_po2ho(ptarget);
  	if (!is_point_process(target)) {
  		hoc_execerror("target arg must be a Point_process", 0);
  	}
  	alloc_music_space();
  	PreSyn* ps;
  	int i = 0;
  	if (!music_input_ports->find(i, (void*)this)) {
  		music_input_ports->insert((void*)this, i);
  	}
  	//assert (!gi_table->find(ps, gi));
  	if (!gi_table->find(ps, gi)) {
  	  ps = new PreSyn(NULL, NULL, NULL);
  	  net_cvode_instance->psl_append(ps);
  	  gi_table->insert(gi, ps);
  	}
  	ps->gid_ = -2;
  	ps->output_index_ = -2;
  
  	NetCon* nc = new NetCon(ps, target);
  	Object* o = hoc_new_object(netcon_sym_, nc);
  	nc->obj_ = o;
  	PyObject* po = nrnpy_ho2po(o);
  //printf("index2target %d %s\n", gi, hoc_object_name(target));
  	return po;
  }
  
  void nrnmusic_init(int* pargc, char*** pargv) {
  	int i;
  	int& argc = *pargc;
  	char**& argv = *pargv;
  	if (strlen(argv[0]) >= 5 && 
  	  strcmp(argv[0] + strlen(argv[0]) - 5, "music") == 0) {
  		nrnmusic = 1;
  	}
  	for (i=0; i < argc; ++i) {
  		if (strcmp(argv[i], "-music") == 0) {
  			nrnmusic = 1;
  		}
  	}
  	if (getenv ("_MUSIC_CONFIG_"))
  	  nrnmusic = 1; // temporary kludge
  	if (nrnmusic) {
  		nrnmusic_setup = new MUSIC::Setup(argc, argv);
  		nrnmusic_comm = nrnmusic_setup->communicator();
  	}
  }
  
  void nrnmusic_terminate() {
  	if (!nrnmusic_runtime) {
  		nrnmusic_runtime = new MUSIC::Runtime(nrnmusic_setup, 1);
  	}
  	nrnmusic_runtime->finalize ();
  	delete nrnmusic_runtime;
  }
  
  // Called from nrn_spike_exchange_init so usable_mindelay is ready to use
  // For now, can only be called once.
  static void nrnmusic_runtime_phase() {
  	static int called = 0;
  	assert(!called);
  	called = 1;
  
  	// call map on all the input ports
  	for (TableIterator(PortTable) i(*music_input_ports); i.more(); i.next()) {
  		NRNMUSIC::EventInputPort* eip = (NRNMUSIC::EventInputPort*)i.cur_key();
  		NrnMusicEventHandler* eh = new NrnMusicEventHandler();
  		Gi2PreSynTable* pst = eip->gi_table;
  		std::vector<MUSIC::GlobalIndex> gindices;
  		//iterate over pst and create indices
  		int cnt = 0;
  		for (TableIterator(Gi2PreSynTable) j(*pst); j.more(); j.next()) {
  			int gi = j.cur_key();
  //printf("input port eip=%p gi=%d\n", eip, gi);
  			gindices.push_back (gi);
  			++cnt;
  		}
  		eh->filltable(eip, cnt);
  		MUSIC::PermutationIndex indices (&gindices.front (),
  						 gindices.size ());
  		eip->map(&indices, eh, usable_mindelay_ / 1000.0);
  		delete eip->gi_table;
  	}
  	delete music_input_ports;
  
  	// call map on all the output ports
  	for (TableIterator(PortTable) i(*music_output_ports); i.more(); i.next()) {
  		NRNMUSIC::EventOutputPort* eop = (NRNMUSIC::EventOutputPort*)i.cur_key();
  		Gi2PreSynTable* pst = eop->gi_table;
  		std::vector<MUSIC::GlobalIndex> gindices;
  		//iterate over pst and create indices
  		for (TableIterator(Gi2PreSynTable) j(*pst); j.more(); j.next()) {
  			int gi = j.cur_key();
  //printf("output port eop=%p gi = %d\n", eop, gi);
  			gindices.push_back (gi);
  		}
  		MUSIC::PermutationIndex indices (&gindices.front (),
  						 gindices.size ());
  		eop->map(&indices, MUSIC::Index::GLOBAL);
  		delete eop->gi_table;
  	}
  	delete music_output_ports;
  
  	//switch to the runtime phase
  	//printf("usable_mindelay = %g\n", usable_mindelay_);
  	nrnmusic_runtime = new MUSIC::Runtime(nrnmusic_setup,
  					      usable_mindelay_ / 1000.0);
  	npme = new NetParMusicEvent();
  	npme->send(0, net_cvode_instance, nrn_threads);
  }
  