/*
 * test su_timer functionality:
 *
 * Create a timer, executing print_stamp() in every 20 ms
 */
int main(int argc, char *argv[])
{
  su_root_t *root;
  su_timer_t *t, *t1, *t_end;
  su_timer_t **timers;
  su_duration_t interval = 60;
  char *argv0 = argv[0];
  char *s;
  int use_t1 = 0;
  su_time_t now, started;
  intptr_t i, N = 500;
  GSource *source;

  struct timing timing[1] = {{ 0 }};
  struct tester tester[1] = {{ 0 }};

  while (argv[1] && argv[1][0] == '-') {
    char *o = argv[1] + 1;
    while (*o) {
      if (*o == '1')
	o++, use_t1 = 1;
      else if (*o == 'r')
	o++, timing->t_run = 1;
      else if (*o == 'N') {
	if (o[1])
	  N = strtoul(o + 1, &o, 0);
	else if (argv[2])
	  N = strtoul(argv++[2], &o, 0);
	break;
      }
      else
	break;

    }
    if (*o)
      usage(argv0);
    argv++;
  }

  if (argv[1]) {
    interval = strtoul(argv[1], &s, 10);

    if (interval == 0 || s == argv[1])
      usage(argv0);
  }

  su_init(); atexit(su_deinit);

  tester->root = root = su_glib_root_create(tester);

  source = su_root_gsource(tester->root);
  g_source_attach(source, NULL /*g_main_context_default ()*/);

  su_msg_create(intr_msg,
		su_root_task(root),
		su_root_task(root),
		test_break, 0);

  signal(SIGINT, intr_handler);
#if HAVE_SIGPIPE
  signal(SIGPIPE, intr_handler);
  signal(SIGQUIT, intr_handler);
  signal(SIGHUP, intr_handler);
#endif

  t = su_timer_create(su_root_task(root), interval);
  t1 = su_timer_create(su_root_task(root), 1);
  t_end = su_timer_create(su_root_task(root), 20 * interval);

  if (t == NULL || t1 == NULL || t_end == NULL)
    su_perror("su_timer_create"), exit(1);

  tester->t = t, tester->t1 = t1;

  timing->t_prev = su_now();

  if (timing->t_run)
    su_timer_run(t, print_stamp, timing);
  else
    su_timer_set(t, print_stamp, timing);

  if (use_t1)
    su_timer_set(t1, print_X, NULL);

  su_timer_set(t_end, end_test, NULL);

  su_root_run(root);

  su_msg_destroy(intr_msg);

  su_timer_destroy(t);
  su_timer_destroy(t1);

  if (timing->t_times != 10) {
    fprintf(stderr, "%s: t expired %d times (expecting 10)\n",
	    argv0, timing->t_times);
    return 1;
  }

  /* Insert timers in order */
  timers = calloc(N, sizeof *timers);
  if (!timers) { perror("calloc"); exit(1); }

  now = started = su_now();

  for (i = 0; i < N; i++) {
    t = su_timer_create(su_root_task(root), 1000);
    if (!t) { perror("su_timer_create"); exit(1); }
    if (++now.tv_usec == 0) ++now.tv_sec;
    su_timer_set_at(t, increment, (void *)i, now);
    timers[i] = t;
  }

  tester->sentinel = (void*)(i - 1);

  su_root_run(root);

  printf("Processing %u timers took %f millisec (%f expected)\n",
	 (unsigned)i, su_time_diff(su_now(), started) * 1000, (double)i / 1000);

  for (i = 0; i < N; i++) {
    su_timer_destroy(timers[i]);
  }

  su_root_destroy(root);

  su_deinit();

  return 0;
}
  void TimerQueue::doTimer(su_timer_t* timer) {
    //self check
    assert( m_length == numberOfElements()) ;
    assert( 0 != m_length || (NULL == m_head && NULL == m_tail) ) ;
    assert( 1 != m_length || (m_head == m_tail)) ;
    assert( m_length < 2 || (m_head != m_tail)) ;
    assert( !(NULL == m_head && NULL != m_tail)) ;
    assert( !(NULL == m_tail && NULL != m_head)) ;

#ifndef TEST
    DR_LOG(log_debug) << m_name << ": running timer function" ;
#endif
    //std::cout << "doTimer: running timer function with " << m_length << " timers queued " << std::endl;

    if( m_in_timer ) return ;
    m_in_timer = 1 ;

    queueEntry_t* expired = NULL ;
    queueEntry_t* tailExpired = NULL ;

    su_time_t now = su_now() ;
    assert( NULL != m_head ) ;

    queueEntry_t* ptr = m_head ;
    while( ptr && su_time_cmp( ptr->m_when, now ) < 0 ) {
      //std::cout << "expiring a timer" << std::endl ;
      m_length-- ;
      m_head = ptr->m_next ;
      if( m_head ) m_head->m_prev = NULL ;
      else m_tail = NULL ;

      //detach and assemble them into a new queue temporarily
      if( !expired ) {
        expired = tailExpired = ptr ;
        ptr->m_prev = ptr->m_next = NULL ;
      }
      else {
        tailExpired->m_next = ptr ;
        ptr->m_prev = tailExpired ;
        tailExpired = ptr ;
      }
      ptr = ptr->m_next ;
    }

    if( NULL == m_head ) {
#ifndef TEST
      DR_LOG(log_debug) << m_name << ": timer not set (queue is empty after processing expired timers), length: " << dec << m_length ;
#endif
      //std::cout << "doTimer: timer not set (queue is empty after processing expired timers)" << std::endl;
      assert( 0 == m_length ) ;
    }
    else {
      //std::cout << "doTimer: Setting timer for " << su_duration( m_head->m_when, su_now() )  << "ms after processing expired timers" << std::endl;
#ifndef TEST
      DR_LOG(log_debug) << m_name << ": Setting timer for " << su_duration( m_head->m_when, su_now() )  << 
        "ms after processing expired timers, length: "  << dec << m_length ;
#endif
      int rc = su_timer_set_at(m_timer, timer_function, this, m_head->m_when);      
    }
    m_in_timer = 0 ;

    while( NULL != expired ) {
      expired->m_function( expired->m_functionArgs ) ;
      queueEntry_t* p = expired ;
      expired = expired->m_next ;
      delete p ;
    }    

    //self check
    assert( m_length == numberOfElements()) ;
    assert( 0 != m_length || (NULL == m_head && NULL == m_tail) ) ;
    assert( 1 != m_length || (m_head == m_tail)) ;
    assert( m_length < 2 || (m_head != m_tail)) ;
    assert( !(NULL == m_head && NULL != m_tail)) ;
    assert( !(NULL == m_tail && NULL != m_head)) ;
  }    
  TimerEventHandle TimerQueue::add( TimerFunc f, void* functionArgs, uint32_t milliseconds, su_time_t now ) {
    //self check
    assert( m_length == numberOfElements()) ;
    assert( 0 != m_length || (NULL == m_head && NULL == m_tail) ) ;
    assert( 1 != m_length || (m_head == m_tail)) ;
    assert( m_length < 2 || (m_head != m_tail)) ;
    assert( !(NULL == m_head && NULL != m_tail)) ;
    assert( !(NULL == m_tail && NULL != m_head)) ;

    su_time_t when = su_time_add(now, milliseconds) ;
    queueEntry_t* entry = new queueEntry_t(this, f, functionArgs, when) ;
    TimerEventHandle handle = entry ;
    assert(handle) ;
    int queueLength ;

    if( entry ) {
#ifndef TEST
      DR_LOG(log_debug) << m_name << ": Adding entry to go off in " << std::dec << milliseconds << "ms" ;
#endif
      //std::cout << "Adding entry to go off in " << milliseconds << "ms" << std::endl;

      if( NULL == m_head ) {
        assert( NULL == m_tail ) ;
        m_head = m_tail = entry; 
#ifndef TEST
        DR_LOG(log_debug) << m_name << ": Adding entry to the head (queue was empty), length: " << dec << m_length+1 ;
#endif
        //std::cout << "Adding entry to the head of the queue (it was empty)" << std::endl ;
      }
      else if( NULL != m_tail && su_time_cmp( when, m_tail->m_when ) > 0) {
        //one class of timer queues will always be appending entries, so check the tail
        //before starting to iterate through
#ifndef TEST
          DR_LOG(log_debug) << m_name << ": Adding entry to the tail of the queue: length " << dec << m_length+1;
#endif
          //std::cout << "Adding entry to the tail of the queue" << std::endl ;
          entry->m_prev = m_tail ;
          m_tail->m_next = entry ;
          m_tail = entry ;
      }
      else { 
        //iterate
        queueEntry_t* ptr = m_head ;
        int idx = 0 ;
        do {
          if( su_time_cmp( when, ptr->m_when ) < 0) {
#ifndef TEST
            DR_LOG(log_debug) << m_name << ": Adding entry at position " << std::dec << idx << " of the queue, length: " << dec << m_length+1 ;
#endif
            //std::cout << "Adding entry at position " << idx << " of the queue" << std::endl ;
            entry->m_next = ptr ;
            if( 0 == idx ) {
              m_head = entry ;
            }   
            else {
              entry->m_prev = ptr->m_prev ; 
              ptr->m_prev->m_next = entry ;

            }         
            ptr->m_prev = entry ;
            break ;
          }
          idx++ ;
        } while( NULL != (ptr = ptr->m_next) ) ;
        assert( NULL != ptr ) ;
/*
        if( NULL == ptr ) {
#ifndef TEST
          DR_LOG(log_debug) << m_name << ": Adding entry to the tail of the queue: length " << dec << m_length+1;
#endif
          //std::cout << "Adding entry to the tail of the queue" << std::endl ;
          entry->m_prev = m_tail ;
          m_tail->m_next = entry ;
          m_tail = entry ;
        }
*/
      }
      queueLength = ++m_length ;
    }
    else {
      //DR_LOG(log_error) << "Error allocating queue entry" ;
      //std::cerr << "Error allocating queue entry" << std::endl ;
      return NULL ;
    }

    //only need to set the timer if we added to the front
    if( m_head == entry ) {
      //DR_LOG(log_debug) << "timer add: Setting timer for " << milliseconds  << "ms" ;
      //std::cout << "timer add: Setting timer for " << milliseconds  << "ms" << std::endl;
      int rc = su_timer_set_at(m_timer, timer_function, this, when);
      assert( 0 == rc ) ;
    }

    //DR_LOG(log_debug) << "timer add: queue length is now " << queueLength ;
    //std::cout << "timer add: queue length is now " << queueLength << std::endl;

     //self check
    assert( m_length == numberOfElements()) ;
    assert( 0 != m_length || (NULL == m_head && NULL == m_tail) ) ;
    assert( 1 != m_length || (m_head == m_tail)) ;
    assert( m_length < 2 || (m_head != m_tail)) ;
    assert( !(NULL == m_head && NULL != m_tail)) ;
    assert( !(NULL == m_tail && NULL != m_head)) ;

    return handle ;

  }
  void TimerQueue::remove( TimerEventHandle entry) {
#ifndef TEST
        DR_LOG(log_debug) << m_name << ": removing entry, prior to removal length: " << dec << m_length;
#endif
    //self check
    assert( m_length == numberOfElements()) ;
    assert( 0 != m_length || (NULL == m_head && NULL == m_tail) ) ;
    assert( 1 != m_length || (m_head == m_tail)) ;
    assert( m_length < 2 || (m_head != m_tail)) ;
    assert( !(NULL == m_head && NULL != m_tail)) ;
    assert( !(NULL == m_tail && NULL != m_head)) ;
    assert( m_head && m_length >= 1 ) ;

    int queueLength ;
    {
      if( m_head == entry ) {
        m_head = entry->m_next ;
        if( m_head ) m_head->m_prev = NULL ;
        else {
          assert( 1 == m_length ) ;
          m_tail = NULL ;
        }
      }
      else if( m_tail == entry ) {
        assert( m_head && entry->m_prev ) ;
        m_tail = entry->m_prev ;
        m_tail->m_next = NULL ;
      }
      else {
        assert( entry->m_prev ) ;
        assert( entry->m_next ) ;
        entry->m_prev->m_next = entry->m_next ;
        entry->m_next->m_prev = entry->m_prev ;
      }
      m_length-- ;
      assert( m_length >= 0 ) ;

      if( NULL == m_head ) {
#ifndef TEST
        DR_LOG(log_debug) << m_name << ": removed entry, timer not set (queue is empty after removal), length: " << dec << m_length;
#endif
        //std::cout << "timer not set (queue is empty after removal)"  << std::endl;
        su_timer_reset( m_timer ) ;
      }
      else if( m_head == entry->m_next ) {
#ifndef TEST
        DR_LOG(log_debug) << m_name << ": removed entry, setting timer for " << std::dec << su_duration( m_head->m_when, su_now() )  << 
          "ms after removal, length: " << dec << m_length;
#endif
        //std::cout << "Setting timer for " << su_duration( m_head->m_when, su_now() )  << "ms after removal of head entry"  << std::endl;
        int rc = su_timer_set_at(m_timer, timer_function, this, m_head->m_when);
      }      
    }

    //DR_LOG(log_debug) << "timer remove: queue length is now " << queueLength ;
    //std::cout << "timer remove: queue length is now " << queueLength << std::endl;

    delete entry ;

    //self check
    assert( m_length == numberOfElements()) ;
    assert( 0 != m_length || (NULL == m_head && NULL == m_tail) ) ;
    assert( 1 != m_length || (m_head == m_tail)) ;
    assert( m_length < 2 || (m_head != m_tail)) ;
    assert( !(NULL == m_head && NULL != m_tail)) ;
    assert( !(NULL == m_tail && NULL != m_head)) ;
  }