#include <../../nrnconf.h>

#if HAVE_IV
#include <Dispatch/iohandler.h>
#include <Dispatch/dispatcher.h>

#include <stdio.h>
#include "oc2iv.h"
#include "objcmd.h"
#endif  /* HAVE_IV */
#include "classreg.h"

#ifdef MINGW
#include <windows.h>
#endif

#if HAVE_IV
#if carbon || defined(MINGW)
class OcTimer {
#else
class OcTimer : public IOHandler {
#endif
public:
	OcTimer(const char*);
	OcTimer(Object*);
	virtual ~OcTimer();

	virtual void timerExpired(long, long);
	void start();
	void stop();
	double seconds();
	void seconds(double);
private:
	double seconds_;
	HocCommand* hc_;
#if carbon
	EventLoopTimerRef timer_;
#else
#ifdef MINGW
	HANDLE wtimer_;
	bool stopped_;
#else
	bool stopped_;
#endif /* not MINGW */
#endif /*not carbon*/
};

#if carbon
static void timer_proc(EventLoopTimerRef, void* v) {
	((OcTimer*)v)->timerExpired(0,0);
}
#endif

#ifdef MINGW
static void CALLBACK callback(PVOID lpParameter, BOOLEAN TimerOrWaitFired) {
	((OcTimer*)lpParameter)->timerExpired(0,0);
}
#endif

#endif  /* HAVE_IV */

static double t_seconds(void* v) {
#if HAVE_IV
	OcTimer* t = (OcTimer*)v;
	if (ifarg(1)) {
		t->seconds(chkarg(1, 1e-6, 1e6));
	}
	return double(t->seconds());
#else 
	return 0.;
#endif /* HAVE_IV  */
}

static double t_start(void* v) {
#if HAVE_IV
	OcTimer* t = (OcTimer*)v;
	t->start();
	return 0.;
#else 
	return 0.;
#endif /* HAVE_IV  */
}
static double t_stop(void* v) {
#if HAVE_IV
	OcTimer* t = (OcTimer*)v;
	t->stop();
	return 0.;
#else 
	return 0.;
#endif /* HAVE_IV  */
}
static void* t_cons(Object*) {
#if HAVE_IV
    if (hoc_is_object_arg(1)) {
    	return new OcTimer(*hoc_objgetarg(1));
    } else {
        return new OcTimer(gargstr(1));
    }
#else 
	return (void*)0;
#endif /* HAVE_IV */
}
static void t_destruct(void* v) {
#if HAVE_IV
	OcTimer* t = (OcTimer*)v;
	delete t;
#endif  /* HAVE_IV */
}

Member_func t_members[] = {
	"seconds", t_seconds,
	"start", t_start,
	"end", t_stop,
	0, 0
};

void OcTimer_reg() {
	class2oc("Timer", t_cons, t_destruct, t_members, NULL, NULL, NULL);
}
#if HAVE_IV
OcTimer::OcTimer(const char* cmd) {
	hc_ = new HocCommand(cmd);
	seconds_ = .5;
#if carbon
	timer_ = 0;
#else
#ifdef MINGW
	wtimer_ = NULL;
	stopped_ = true;
#else
	stopped_ = true;
#endif
#endif
}
OcTimer::OcTimer(Object* cmd) {
	hc_ = new HocCommand(cmd);
	seconds_ = .5;
#if carbon
	timer_ = 0;
#else
#ifdef MINGW
	wtimer_ = NULL;
	stopped_ = true;
#else
	stopped_ = true;
#endif
#endif
}
OcTimer::~OcTimer() {
	stop();
	delete hc_;
}
void OcTimer::start() {
#if carbon
	if (timer_) { return; }
	InstallEventLoopTimer(GetMainEventLoop(), seconds_, seconds_, timer_proc,
		(void*)this, &timer_);
#else
#ifdef MINGW
	stopped_ = false;
	LARGE_INTEGER nsec100;
	nsec100.QuadPart = (long long)(-seconds_*10000000.);
	wtimer_ = CreateWaitableTimer(NULL, TRUE, NULL);
	while (stopped_ == false) {
		SetWaitableTimer(wtimer_, &nsec100, 0, NULL, NULL, 0);
		hc_->execute();
		WaitForSingleObject(wtimer_, INFINITE);
	}
	CloseHandle(wtimer_);
	wtimer_ = NULL;
#else
	long s = long(seconds_);
	long us = long((seconds_ - double(s))*1000000.);
	stopped_ = false;
	Dispatcher::instance().startTimer(s, us, this);
#endif
#endif
}
void OcTimer::stop() {
#if carbon
	if (timer_) {
		RemoveEventLoopTimer(timer_);
		timer_ = 0;
	}
#else
#ifdef MINGW
	stopped_ = true;
#else
	stopped_ = true;
	Dispatcher::instance().stopTimer(this);
#endif
#endif
}
void OcTimer::timerExpired(long, long) {
#if carbon
#else
#ifdef MINGW
#else
	if (!stopped_) {
		this->start();
	}
#endif
#endif
	// want it to be part of interval just like on mac
	hc_->execute();
}
double OcTimer::seconds() {
	return seconds_;
}
void OcTimer::seconds(double sec) {
	seconds_ = sec;
}
#endif /* HAVE_IV */
