//-----------------------------------------------------------------------------
// Copyright (c) 1994,1995 Southeastern Universities Research Association,
//                         Continuous Electron Beam Accelerator Facility
//
// This software was developed under a United States Government license
// described in the NOTICE file included as part of this distribution.
//
//-----------------------------------------------------------------------------
//
// Description:
//      Implementation of cdevTimerQueue Class
//
// Author:  Jie Chen
//
// Revision History:
//   cdevTimerQueue.cc,v
// Revision 1.1  1998/02/10  18:04:58  chen
// add cdevSystem timer handler
//
//
//
#include "cdevTimerQueue.h"

//=====================================================================
//          Implementation of cdevTimerQNode Class
//=====================================================================
cdevTimerQNode::cdevTimerQNode (cdevTimerHandler* h,
				const void* arg,
				const cdevTimeValue& t,
				const cdevTimeValue& i,
				int id)
:handler_ (h), arg_ (arg), timer_ (t), interval_ (i), tid_ (id)
{
#ifdef _TRACE_OBJECTS
  printf ("Create cdevTimerQNode Class Object\n");
#endif
  // empty
}

//=====================================================================
//          Implementation of cdevTimerQueue Class
//=====================================================================
cdevTimerQueue::cdevTimerQueue (void)
:queue_ (), timerId_ (0)
{
#ifdef _TRACE_OBJECTS
  printf ("Create cdevTimerQueue Class Object\n");
#endif
  // empty
}

cdevTimerQueue::~cdevTimerQueue (void)
{
#ifdef _TRACE_OBJECTS
  printf ("Delete cdevTimerQueue Class Object\n");
#endif
  cdevSlistIterator ite (queue_);
  cdevTimerQNode* tnode = 0;

  for (ite.init(); !ite; ++ite) {
    tnode = (cdevTimerQNode *) ite ();
    delete tnode;
  }
  queue_.deleteAllValues ();
}

int
cdevTimerQueue::isEmpty (void) const
{
  return queue_.isEmpty ();
}

const cdevTimeValue&
cdevTimerQueue::earliestTime (void) const
{
  cdevTimerQNode* tnode = (cdevTimerQNode *)queue_.firstElement ();
  return tnode->timer_;
}

void
cdevTimerQueue::reschedule (cdevTimerQNode* node)
{ 
  cdevTimerQNode* tnode = 0;
  if (queue_.isEmpty () || node->timer_ < earliestTime ()) { 
    // put in the beginning of queue
    queue_.add (node);
  }
  else {
    cdevSlistIterator ite (queue_);
    int               found = 0;
    
    for (ite.init (); !ite; ++ite) {
      tnode = (cdevTimerQNode *) ite ();
      if (node->timer_ > tnode->timer_) {
	ite.addAfter (node);
	found = 1;
	break;
      }
    }

    // add to the end of the queue
    if (!found)
      ite.addAfter (node);
  }
}

int
cdevTimerQueue::scheduleTimer (cdevTimerHandler* h,
			       const void* arg,
			       const cdevTimeValue& future_time,
			       const cdevTimeValue& interval)
{
  timerId_ ++;

  cdevTimerQNode* tnode = 0;
  cdevTimerQNode* qnode = new cdevTimerQNode (h, arg, 
					      future_time,
					      interval, timerId_);
  
  if (queue_.isEmpty () || future_time < earliestTime ()) 
    queue_.add (qnode);
  else {
    // find right place to put this node
    cdevSlistIterator ite (queue_);
    int               found = 0;
    
    for (ite.init (); !ite; ++ite) {
      tnode = (cdevTimerQNode *) ite ();
      if (future_time > tnode->timer_) {
	ite.addAfter (qnode);
	found = 1;
	break;
      }
    }

    // add to the end of the queue
    if (!found)
      ite.addAfter (qnode);
  }
  return timerId_;
}

int
cdevTimerQueue::cancel (int id)
{
  cdevSlistIterator ite (queue_);
  cdevTimerQNode* tnode = 0;

  for (ite.init (); !ite; ++ite) {
    tnode = (cdevTimerQNode *) ite ();
    if (tnode->tid_ == id) {
      delete tnode;
      ite.removeCurrent ();
      return 0;
    }
  }
  return -1;
}

int
cdevTimerQueue::cancel (cdevTimerHandler* handler)
{
  cdevSlistIterator ite (queue_);
  cdevTimerQNode* tnode = 0;

  for (ite.init (); !ite; ++ite) {
    tnode = (cdevTimerQNode *) ite ();
    if (tnode->handler_ == handler) {
      delete tnode;
      ite.removeCurrent ();
      return 0;
    }
  }
  return -1;  
}

int
cdevTimerQueue::expire (const cdevTimeValue& curTime)
{
  while (1) {
    // all timers have been called
    if (queue_.isEmpty () || earliestTime () > curTime )
      break;

    cdevTimerQNode* fnode = (cdevTimerQNode *)queue_.firstElement ();
    cdevTimerHandler* handler = fnode->handler_;
    const void* arg = fnode->arg_;
    int   freemem = 1;
    int   res;

    // dequeue
    queue_.removeFirst ();
    
    if (fnode->interval_ > cdevTimeValue::zero) {
      do
	fnode->timer_ += fnode->interval_;
      while (fnode->timer_ <= curTime);

      reschedule (fnode);
      freemem = 0;
    }
    
    res = handler->timerCallback ((double)curTime, arg);

    if (res == -1)
      cancel (handler);

    if (freemem)
      delete fnode;
  }
  return 0;
}

    
