/***************************************************************
 * PrTimer.cpp
 *
 * This class provides an implementation of the timer class.
 *
 ****************************************************************/

/*
 * modification history
 *----------------------
 *
 * 10/26/97: K.C. Chang: created.
 */

#include "PrTimer.h"
#include "PrUtility.h"

#include <time.h>
#include <signal.h>
#include <iostream.h>
#include <stdio.h>
#include <sys/neutrino.h>
#include <sys/netmgr.h>

// set the frequency of the timer
PrTimer::PrTimer(double frequency)
{
    if (frequency > PR_TIMER_FREQUENCY_MAX)
      {
	  frequency = PR_TIMER_FREQUENCY_MAX;
	  cout<<"PrTimer::PrTimer():frequency = max ("<<frequency<<")"<<endl;
      }
    else if (frequency < PR_TIMER_FREQUENCY_MIN)
      {
	  frequency = PR_TIMER_FREQUENCY_MIN;
	  cout<<"PrTimer::PrTimer():frequency = min ("<<frequency<<")"<<endl;
      }

    period_ = 1.0/frequency;
    utility_ = new PrUtility;
    flag_ = Initialize();
}

PrTimer::~PrTimer()
{
    delete utility_;
    Quit();
}

// set the local time_ = 0;
int PrTimer::zero()
{
    time_ = timeGlobal();
    return PR_TIMER_OK;
}

int PrTimer::start()
{
    struct itimerspec timer;

    long sec = (long) period_;
    long nsec = (long)((period_ - (double)sec)*PR_BILLION);

    timer.it_value.tv_sec = sec;
    timer.it_value.tv_nsec = nsec; // offset;
    timer.it_interval.tv_sec = sec;
    timer.it_interval.tv_nsec = nsec;
   
    if (timer_settime(id_,0,&timer,NULL) == -1)
      return Error("start(): Unable to set timer");

    return PR_TIMER_OK;
}

// blocks until the next tick: returns the number of ticks missed.
int PrTimer::wait()
{
    /* Wait for the proxy */
/* MIG4NTO          Receive Use MsgReceive() instead.  Note that this takes a */
/* MIG4NTO                  channel id returned from ChannelCreate() instead */
/* MIG4NTO                  of a process id. See the migration guide as a */
/* MIG4NTO                  starting place for more information on this. */
/*    if (Receive(proxy_, 0, 0) == -1)
      return Error("wait()::Unable to receive a message");
*/ 
    struct _pulse pulse;
    if ( MsgReceive( pulseId_, &pulse, sizeof(pulse), NULL ) == -1 )
        return Error("wait()::Unable to receive a message");
 
    int counter = 0;
/* MIG4NTO         Creceive Use MsgReceive() preceeded immediately by: */
/* MIG4NTO                  event.sigev_notify = SIGEV_UNBLOCK; */
/* MIG4NTO                  TimerTimeout(CLOCK_REALTIME, _NTO_TIMEOUT_RECEIVE, */
/* MIG4NTO                  &event, NULL, NULL ); */
    struct sigevent event;
    event.sigev_notify = SIGEV_UNBLOCK;
    TimerTimeout( CLOCK_REALTIME, _NTO_TIMEOUT_RECEIVE, &event, NULL, NULL );
    while( MsgReceive( pulseId_, &pulse, sizeof(pulse), NULL ) != -1 ) {
        counter++;
        TimerTimeout( CLOCK_REALTIME, _NTO_TIMEOUT_RECEIVE, 
            &event, NULL, NULL );
    }

    return counter;
}

// returns the time in sec since the last time zero() was called
double PrTimer::time()
{
    return timeGlobal() - time_;
}

// returns the time in sec since 1/1/1970
double PrTimer::timeGlobal()
{
    return utility_->time();
}

// self test routine
int PrTimer::selfTest(int loop)
{
    cout<<"PrTimer:: selfTest(): starts"<<endl;

    if (flag() != PR_TIMER_OK)
      return Error("constructor error");

    if (start() != PR_TIMER_OK)
      return Error("start error");

    for(int i=0;i<loop;i++)
      {
	  printf("%d: time=%.9f\n",i,time());
	  for(int j=0;j<loop*1000;j++)
	    {
		Float f=1.0;
		f *= j;
		f = 1.0/f;
	    }
	  int ticks = wait();
	  if (ticks > 0)
	    cout<<"ticks skipped="<<ticks<<endl;
      }
    
    cout<<"PrTimer:: selfTest(): done"<<endl;
    
    return PR_TIMER_OK;
}

// private methods

// initialize the timer
inline int PrTimer::Initialize()
{
    zero();

    period_ = utility_->resolution(period_);

    if (period_ <= 0.0)
      return PR_TIMER_ERROR;
    
    /* Get a proxy for the interrupt handler to kick */
/* MIG4NTO qnx_proxy_attach Consider replacing proxies with QNX Neutrino */
/* MIG4NTO                  pulses. */
/*    proxy_ = qnx_proxy_attach(0,0,0,-1);
    if (proxy_ == -1) 
      return Error("start()::Unable to attach proxy");
*/
    pulseId_ = ChannelCreate( 0 );
    
    /* Attach to the timer */
    struct sigevent event;
    event.sigev_notify = SIGEV_PULSE;
    event.sigev_coid = ConnectAttach( ND_LOCAL_NODE, 0, pulseId_,
        _NTO_SIDE_CHANNEL, 0 );
    event.sigev_priority = getprio( 0 );
    event.sigev_code = _PULSE_CODE_MINAVAIL;

//    event.sigev_signo = -proxy_;
/* MIG4NTO     timer_create The QNX 4 version was based on a draft standard.  */
/* MIG4NTO                  In Neutrino the timer id is returned through the */
/* MIG4NTO                  third parameter and the sigevent structure is */
/* MIG4NTO                  filled in differently. */
    timer_create(CLOCK_REALTIME, &event, &id_);
    if (id_ == -1)
      return Error("start()::Unable to attach timer");
    
    return PR_TIMER_OK;
}

// quit
inline int PrTimer::Quit()
{
    if ( timer_delete(id_) == -1 )
      return PR_TIMER_ERROR;
    
    return PR_TIMER_OK;
}

// error message
inline int PrTimer::Error(char *mesg)
{
    cout<<"PrTimer:: Error(): "<<mesg<<endl;
    return PR_TIMER_ERROR;
}

