/*
   File: rtstime.c
   Implements timers used in parser.

   Copyright 2005 Radboud University of Nijmegen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Library General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   CVS ID: "$Id: rtstime.c,v 1.19 2006/06/16 20:18:53 marcs Exp $"
*/

/* standard includes */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#ifndef WIN32
#include <unistd.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/times.h>
#endif
#include <sys/time.h>

/* libabase includes */
#include <abase_error.h>

/* local includes */
#include "rtstime.h"

#ifdef DEBUG_RTS
#  define DEBUG_TIME_OUT
#endif

#undef PROCESSORTIME
#undef RTIME

#if !defined(MSDOS) && !defined(WIN32)
#define GETTIMEOFDAY
#undef CLOCK
#undef PROCESSORTIME
#undef ALARM_SIGNAL
#else
#undef GETTIMEOFDAY
#define CLOCK
#endif

/*------------------------------------------------------------------------------
// Time
//
// On Solaris, times() is quite expensive, gettimeofday() is fast.
// On NT, only clock() is available. Unsolved problem: clock wraps
// around every 2147 seconds.
//----------------------------------------------------------------------------*/
#ifdef PROCESSORTIME
static Time convert_tms_to_time(struct tms* tp)
{
  return (0.0001 * (double)((tp->tms_utime * 10000) / CLK_TCK));
}

Time get_current_time(void)
{
  struct tms t;
  times (&t);
  return (convert_tms_to_time(&t));
}
#endif /* PROCESSORTIME */

#ifdef RTIME
static Time convert_timespec_to_time(struct timespec* tp)
{ return ((double)(tp->tv_sec) + 0.000000001 * (double)(tp->tv_nsec));
}

Time get_current_time(void)
{ struct timespec t;
  clock_gettime(CLOCK_REALTIME, &t);
  return (convert_timespec_to_time (&t));
}
#endif /* RTIME */

#ifdef GETTIMEOFDAY
static Time convert_timeval_to_time(struct timeval *tp)
{ return ((double)(tp->tv_sec) + 0.000001 * (double)(tp->tv_usec));
}

Time get_current_time(void)
{
  struct timeval t;
  gettimeofday(&t, NULL);
  return (convert_timeval_to_time (&t));
}
#endif /* GETTIMEOFDAY */

#ifdef CLOCK
static Time convert_clock_to_time (clock_t t)
{
  return ((double)t / CLOCKS_PER_SEC);
}

Time get_current_time (void)
{ clock_t t = clock();
  return (convert_clock_to_time (t));
}
#endif /* CLOCK */

/*------------------------------------------------------------------------------
// Timers
//----------------------------------------------------------------------------*/
typedef struct
{
  Time start_time;
  Time interval_time;
  Time total_time;
} Timer;

static void reset_timer (Timer* timer)
{
  timer -> start_time = 0;
  timer -> interval_time = 0;
  timer -> total_time = 0;
}

static void start_timer (Timer* timer)
{
  timer -> start_time = get_current_time();
}

static void stop_timer(Timer* timer)
{
  Time t = get_current_time();
  Time i = t - timer->start_time;
  timer->interval_time = i;
  timer->total_time += i;
}

static Time get_interval_time(Timer* timer)
{
  return timer->interval_time;
}

static Time get_total_time(Timer* timer)
{ return timer->total_time;
}

/*------------------------------------------------------------------------------
// AGFL timers
//----------------------------------------------------------------------------*/
static Timer scan_timer;
static Timer parse_timer;
static Timer print_timer;

/*
   Scan time
*/
void start_scan_time(void)
{ start_timer(&scan_timer);
}

void stop_scan_time(void)
{ stop_timer(&scan_timer);
}

Time get_scan_time(void)
{ return get_interval_time(&scan_timer);
}

Time get_total_scan_time(void)
{ return get_total_time(&scan_timer);
}

/*
   Parse time
*/
void reset_parse_time()
{ reset_timer(&parse_timer);
}

void restart_parse_time()
{ reset_timer(&parse_timer);
  start_timer(&parse_timer);
}

void start_parse_time(void)
{ start_timer(&parse_timer);
}

void stop_parse_time(void)
{ stop_timer(&parse_timer);
}

Time get_parse_time(void)
{ return get_interval_time(&parse_timer);
}

Time get_total_parse_time(void)
{ return get_total_time(&parse_timer);
}

/*
   Print time
*/
void start_print_time(void)
{ start_timer(&print_timer);
}

void stop_print_time(void)
{ stop_timer(&print_timer);
}

Time get_print_time(void)
{ return get_interval_time(&print_timer);
}

Time get_total_print_time(void)
{ return get_total_time(&print_timer);
}

void reset_timers(void)
{ reset_timer(&scan_timer);
  reset_timer(&print_timer);
  reset_parse_time();
}

/*------------------------------------------------------------------------------
// Time-out timer.
//
// On NT, use time() instead of clock(), due to wrap-arounds of clock.
//----------------------------------------------------------------------------*/
#ifdef GETTIMEOFDAY
static struct timeval time_out;
static int ticks;
#define MAX_TICKS 1500

void set_time_out (int max)
{ gettimeofday (&time_out, NULL);
  time_out.tv_sec += (time_t) max;
#ifdef DEBUG_TIME_OUT
  abs_message ("tod: time-out time = %.3f", convert_timeval_to_time(&time_out));
#endif
  ticks = 0;
}

int have_time_out()
{
    struct timeval cur_time;

    if (ticks == -1) return 1;
    if (ticks != 0) { ticks--; return 0; }

    gettimeofday(&cur_time, NULL);

#ifdef DEBUG_TIME_OUT
    abs_message ("tod: check time-out time = %.3f", convert_timeval_to_time(&cur_time));
#endif

    /* abs-message ("have_time_out: cur=%ld, timeout=%ld", cur_time.tv_sec, time_out.tv_sec); */

    if (cur_time.tv_sec < time_out.tv_sec) {
        ticks = MAX_TICKS;
        return 0;
    } else if (cur_time.tv_sec > time_out.tv_sec) {
        ticks = -1;
        return 1;
    } else if (cur_time.tv_usec > time_out.tv_usec) {
        ticks = -1;
        return 1;
    } else {
        ticks = MAX_TICKS;
        return 0;
    }
}

#else /* !GETTIMEOFDAY */
#ifdef ALARM_SIGNAL

static int timeout_alarm_occured;
static void alarm_handler(int signum)
{
#ifdef DEBUG_TIME_OUT
    abs_message ("alarm_handler: incoming signal #%d...", signum);
#endif /* DEBUG_TIME_OUT */
    if (signum == SIGALRM) {
        timeout_alarm_occured = 1;
    }
}

void set_time_out (int max)
{
    timeout_alarm_occured = 0;
    signal(SIGALRM, alarm_handler);
    alarm((unsigned) max);
}

int have_time_out()
{
    if (timeout_alarm_occured) {
        signal(SIGALRM, SIG_IGN);
        return (1);
    }
    return (0);
}

#else /* !ALARM_SIGNAL */

static time_t time_out;
void set_time_out (int max)
{
    time_out = time(NULL) + (time_t) max;
#ifdef DEBUG_TIME_OUT
    abs_message ("utime: time-out time = %s", ctime(&time_out));
#endif
}

int have_time_out()
{
    time_t cur_time = time(NULL);
#ifdef DEBUG_TIME_OUT
    abs_message ("utime: check time-out time = %s", ctime(&cur_time));
#endif
    return (cur_time > time_out);
}

#endif /* !ALARM_SIGNAL */
#endif /* !GETTIMEOFDAY */

/*------------------------------------------------------------------------------
// Stand alone version
//----------------------------------------------------------------------------*/
#ifdef STAND_ALONE
#define NTIMES	1000000

int main()
{
  Timer timer;
  unsigned i;

  start_timer (&timer);
  for (i = 0; i < NTIMES; i++)
  {
#if 0
    struct timeval t;
    gettimeofday(&t, NULL);
#else
    clock();
#endif
  }
  stop_timer (&timer);
  abs_message ("time: %.3f secs\n", get_interval_time(&timer));
  return (0);
}

#endif /* STAND_ALONE */
