/*---------------------------------------------------------------------
 * File:  TIM_bridge.c
 *
 * External Entity:  Time (TIM)
 *                            
 * Description:
 * This file provides an implementation of the standard Shlaer-Mellor
 * timer functionality.  Usage is conformed to the bridge interface
 * as described in the BridgePoint Action Language Users Guide.
 *
 * Modify this file to match project design requirements.  Simply add
 * or remove code.  Special comments draw attention to where
 * modifications can most easily be made.
 *
 * Only a subset of the TIM interfaces are provided in this simple
 * prototype implementation.  Recurring timers and timer expiration
 * modification are not supported.  Long integers are used to store
 * time values thus limiting the duration of timers and the system
 * ticker to about 71 minutes.  The sample implementation uses
 * the localtime, mktime, ftime and time library routines.
 *
 * For this example implementation to work, TIM_init() must be
 * invoked at start-up (perhaps from UserInitializationCallout).
 * Also, TIM_tick() must be invoked periodically and as often
 * as practical (perhaps from UserBackgroundProcessingCallout).
 * The resolution of the timers is driven largely by the frequency
 * of invocation of TIM_tick().
 *
 * Notice:
 * (C) Copyright 1999, 2000 ROX Software, Inc.
 *     All rights reserved.
 *-------------------------------------------------------------------*/

#include <sys/timeb.h>
#include <time.h>
#include "TIM_bridge.h"
#include "sys_init.h"

typedef unsigned long ETimer_time_t;

/*---------------------------------------------------------------------
 * Timer "Object" Structure Declaration
 *    [next] is the mechanism used to collect and sequence timers.
 *    Timer instances are strung together in an active (animate)
 *    list and an inactive (inanimate) list.  The next pointer
 *    provides the "hole for the beads".
 *    [expiration] is the system clock time at which this
 *    timer will pop.
 *    [t0] (not used) is the system time at installation and
 *    would be useful with recurring timers.
 *    [event] is the handle of the event that the timer will
 *    generate upon expiration.
 *    [flags] (not used) provides boolean variables useful to
 *    indicate whether the timer has popped or whether it is
 *    a recurring timer.
 *-------------------------------------------------------------------*/
typedef struct ETimer_s ETimer_t;
struct ETimer_s {
  ETimer_t * next;
  ETimer_time_t expiration;
  OoaEvent_t * event;
};

static ETimer_t swtimers[ SYS_MAX_OOA_TIMERS ];  /* system.clr color */
static ETimer_t * animate = (ETimer_t *) 0, * inanimate = (ETimer_t *) 0;
static ETimer_time_t tinit = 0;
static struct timeb systyme;

static void timer_insert_sorted( ETimer_t * );
static void timer_fire( ETimer_t * const );
static ETimer_time_t ETimer_msec_time( void );
static ETimer_t *timer_start( const ETimer_time_t, OoaEvent_t * const );
static bool timer_cancel( ETimer_t * );


/*=====================================================================
 * BridgePoint Primitive:
 * <timer_inst_ref_var> = TIM::timer_start(
 *   microseconds:<integer_var>,
 *   event_inst:<event_inst_var> )
 * This bridge starts up an instance of a one-shot S-M OOA timer.
 *===================================================================*/
Escher_Timer_t *
TIM_timer_start(
    Escher_OoaEvent_s * ee_event_inst,
    const Escher_uSec_t ee_microseconds )
{
  /* Insert implementation specific code here.  */
  return (Escher_Timer_t *) timer_start( ee_microseconds/1000, ee_event_inst );
}

/*=====================================================================
 * BridgePoint Primitive:
 * <timer_inst_ref_var> = TIM::timer_start_recurring(
 *   microseconds:<integer_var>,
 *   event_inst:<event_inst_var> )
 * This bridge starts up an instance of a recurring S-M OOA timer.
 *===================================================================*/
Escher_Timer_t *
TIM_timer_start_recurring(
    Escher_OoaEvent_s * ee_event_inst,
    const Escher_uSec_t ee_microseconds )
{
  /* Insert implementation specific code here.  */
  Escher_Timer_t * result = (Escher_Timer_t *) 0;
  return result;
}

/*=====================================================================
 * BridgePoint Primitive:
 * <integer_var> = TIM::timer_remaining_time(
 *   timer_inst_ref:<timer_inst_ref_var> )
 * Return the remaining time of the specified timer.
 *===================================================================*/
Escher_uSec_t
TIM_timer_remaining_time(
    const Escher_Timer_t * const ee_timer_inst_ref )
{
  /* Insert implementation specific code here.  */
  return ( ee_timer_inst_ref == (Escher_Timer_t *) 0 ) ? 0 :
	  ( 1000 * ((ETimer_t *) ee_timer_inst_ref)->expiration );
}

/*=====================================================================
 * BridgePoint Primitive:
 * <was_running_flag> = TIM_timer_reset_time(
 *   microseconds:<integer_var>,
 *   timer_inst_ref:<timer_inst_ref_var> )
 * Try to change expiration of an existing timer.
 * If successful, return true.
 *===================================================================*/
bool
TIM_timer_reset_time(
    const Escher_uSec_t ee_microseconds,
    Escher_Timer_t * const ee_timer_inst_ref )
{
  /* Insert implementation specific code here.  */
  return ( false );             /* not implemented */
}

/*=====================================================================
 * BridgePoint Primitive:
 * <was_running_flag> = TIM_timer_add_time(
 *   microseconds:<integer_var>,
 *   timer_inst_ref:<timer_inst_ref_var> )
 * Try to add time to an existing timer.
 * If successful, return true.
 *===================================================================*/
bool
TIM_timer_add_time(
    const Escher_uSec_t ee_microseconds,
    Escher_Timer_t * const ee_timer_inst_ref )
{
  /* Insert implementation specific code here.  */
  return ( false );             /* not implemented */
}

/*=====================================================================
 * BridgePoint Primitive:
 * <was_running_flag> = TIM::timer_cancel(
 *   timer_inst_ref:<timer_inst_ref_var> )
 * This attempts to cancel the specified timer.
 * Return true if we actually cancelled the timer.
 *===================================================================*/
bool
TIM_timer_cancel(
    Escher_Timer_t * const ee_timer_inst_ref )
{
  /* Insert implementation specific code here.  */
  return timer_cancel( (ETimer_t *) ee_timer_inst_ref );
}


/*=====================================================================
 * BridgePoint Primitive:
 * <date_var> = TIM::current_date()
 * Return a variable representing the current calendar date.
 *===================================================================*/
Escher_Date_t
TIM_current_date()
{
  /* Insert implementation specific code here.  */
  return ( (Escher_Date_t) time( 0 ) );
}

/*=====================================================================
 * BridgePoint Primitive:
 * <date_var> = TIM::create_date(
 *   day:<integer_var>,
 *   hour:<integer_var>,
 *   minute:<integer_var>,
 *   month:<integer_var>,
 *   second:<integer_var>,
 *   year:<integer_var> )
 * Return a variable representing the calendar date as specified
 * by the input components.
 *===================================================================*/
Escher_Date_t
TIM_create_date(
    const int ee_day,
    const int ee_hour,
    const int ee_minute,
    const int ee_month,
    const int ee_second,
    const int ee_year )
{
  /* Insert implementation specific code here.  */
  struct tm t;
  t.tm_mday = ee_day;
  t.tm_hour = ee_hour;
  t.tm_min = ee_minute;
  t.tm_mon = ee_month;
  t.tm_sec = ee_second;
  t.tm_year = ee_year;
  t.tm_year = ee_year;
  return ( (Escher_Date_t) mktime( &t ) );
}

/*=====================================================================
 * BridgePoint Primitive:
 * <integer_var> = TIM::get_second(
 *   date:<integer_var> )
 * Return the year field of the date variable.
 *===================================================================*/
int
TIM_get_second(
    const Escher_Date_t ee_date )
{
  /* Insert implementation specific code here.  */
  struct tm * tp;
  tp = localtime( &ee_date );
  return ( tp->tm_sec );
}

/*=====================================================================
 * BridgePoint Primitive:
 * <integer_var> = TIM::get_minute(
 *   date:<integer_var> )
 * Return the year field of the date variable.
 *===================================================================*/
int
TIM_get_minute(
    const Escher_Date_t ee_date )
{
  /* Insert implementation specific code here.  */
  struct tm * tp;
  tp = localtime( &ee_date );
  return ( tp->tm_min );
}

/*=====================================================================
 * BridgePoint Primitive:
 * <integer_var> = TIM::get_hour(
 *   date:<integer_var> )
 * Return the year field of the date variable.
 *===================================================================*/
int
TIM_get_hour(
    const Escher_Date_t ee_date )
{
  /* Insert implementation specific code here.  */
  struct tm * tp;
  tp = localtime( &ee_date );
  return ( tp->tm_hour );
}

/*=====================================================================
 * BridgePoint Primitive:
 * <integer_var> = TIM::get_day(
 *   date:<integer_var> )
 * Return the year field of the date variable.
 *===================================================================*/
int
TIM_get_day(
    const Escher_Date_t ee_date )
{
  /* Insert implementation specific code here.  */
  struct tm * tp;
  tp = localtime( &ee_date );
  return ( tp->tm_mday );
}

/*=====================================================================
 * BridgePoint Primitive:
 * <integer_var> = TIM::get_month(
 *   date:<integer_var> )
 * Return the year field of the date variable.
 *===================================================================*/
int
TIM_get_month(
    const Escher_Date_t ee_date )
{
  /* Insert implementation specific code here.  */
  struct tm * tp;
  tp = localtime( &ee_date );
  return ( tp->tm_mon );
}

/*=====================================================================
 * BridgePoint Primitive:
 * <integer_var> = TIM::get_year(
 *   date:<integer_var> )
 * Return the year field of the date variable.
 *===================================================================*/
int
TIM_get_year(
    const Escher_Date_t ee_date )
{
  /* Insert implementation specific code here.  */
  struct tm * tp;
  tp = localtime( &ee_date );
  return ( tp->tm_year );
}

/*=====================================================================
 * BridgePoint Primitive:
 * <timestamp_var> = TIM::current_clock()
 * This bridge returns a system dependent time value.
 *===================================================================*/
Escher_TimeStamp_t
TIM_current_clock()
{
  /* Insert implementation specific code here.  */
  return ( ETimer_msec_time() );
}


/* Routines below are implementation specific.  Modify to taste.     */


/*---------------------------------------------------------------------
 * Get a timer instance from the inanimate list, provide the
 * expiration time and insert it into its proper location among
 * the currently ticking timers.
 *-------------------------------------------------------------------*/
static ETimer_t *timer_start(
  const ETimer_time_t duration,
  OoaEvent_t * const event
)
{
  ETimer_t * t = inanimate;
	if ( t == (ETimer_t *) 0 ) return (ETimer_t *) 0;
  inanimate = inanimate->next;
  t->event = event;
  /*-----------------------------------------------------------------*/
  /* Calculate the timer expiration time.                            */
  /* Note:  Add one to the duration to make sure that delay is       */
  /* at least as long as duration.                                   */
  /*-----------------------------------------------------------------*/
  t->expiration = ETimer_msec_time() + duration + 1;
  timer_insert_sorted( t );
  return ( t );
}

/*---------------------------------------------------------------------
 * Insert a timer into the list of ticking timers maintaining
 * an order that fires timers chronologically.
 *-------------------------------------------------------------------*/
static void timer_insert_sorted(
  ETimer_t * t
)
{
  ETimer_time_t poptime = t->expiration;
  if ( animate == (ETimer_t *) 0 ) {                  /* empty list   */
    t->next = (ETimer_t *) 0;
    animate = t;
  } else if ( poptime <= animate->expiration ) {     /* before head  */
    t->next = animate;
    animate = t;         
  } else {                                           /* find bigger  */
    ETimer_t * prev = animate;
    ETimer_t * cursor;
    while ( ( cursor = prev->next ) != (ETimer_t *) 0 ) {
      if ( poptime <= cursor->expiration ) {
        break;
      }
      prev = cursor;
    }
    prev->next = t;                                  /* link in      */
    t->next = cursor;
  }
}


/*---------------------------------------------------------------------
 * Insert a timer into the list of ticking timers maintaining
 * an order that fires timers chronologically.
 *-------------------------------------------------------------------*/
static bool timer_cancel(
  ETimer_t * t
)
{
  if ( t == (ETimer_t *) 0 ) {
    return ( false );
  }
  if ( animate == (ETimer_t *) 0 ) {
    return ( false );
  }
  /*-----------------------------------------------------------------*/
  /* Check to see if the timer has already been reset.  This check   */
  /* is probabilistic.  It could have a hole if multitasked.         */
  /* We need to try to unlink and see if we actually unlinked.       */
  /* Attempt to remove the timer from the list of running timers.    */
  /* If we succeed, then we can cancel/delete the timer.  If the     */
  /* timer is not in the list, then there is no point in attempting  */
  /* to do any more.                                                 */
  /*-----------------------------------------------------------------*/
  if ( t == animate ) {
    animate = animate->next;
  } else {
    ETimer_t * prev = animate;
    ETimer_t * cursor;
    while ( ( cursor = prev->next ) != t ) {           /* find */
      if ( cursor == (ETimer_t *) 0 ) {
        return ( false );
      }
      prev = cursor;
    }
    prev->next = t->next;                     /* unlink */
  }
  t->next = inanimate;
  inanimate = t;
  return ( true );
}


/*---------------------------------------------------------------------
 * Generate delayed event to the application.
 * Deactivate fired timer.
 *-------------------------------------------------------------------*/
static void timer_fire(
  ETimer_t * const t
)
{
  Escher_SendEvent( t->event );
  animate = animate->next;      /* Remove from active list.          */
  t->next = inanimate;          /* Connect to inactive list.         */
  inanimate = t;
}

/*---------------------------------------------------------------------
 * This is the low level mechanism for monotonically increasing
 * at a constant rate.  Substitute code here to read some
 * sort of hardware timer.
 *-------------------------------------------------------------------*/
static ETimer_time_t ETimer_msec_time()
{
  ETimer_time_t t;
  ftime( &systyme );
  t = ( systyme.time * 1000 ) + systyme.millitm;
  return ( t - tinit );
}
 

/*---------------------------------------------------------------------
 * Initialize the tick mechanism and the timer instances.
 *-------------------------------------------------------------------*/
void TIM_init()
{
  int i;
  ftime( &systyme );            /* Initialize the hardware ticker.   */
  tinit = ( systyme.time * 1000 ) + systyme.millitm;
  /*-----------------------------------------------------------------*/
  /* Build the collection (linked list) of timers.                   */
  /*-----------------------------------------------------------------*/
  for ( i = 0; i < SYS_MAX_OOA_TIMERS; i++ ) {
    swtimers[ i ].expiration = 0;
    swtimers[ i ].event = (OoaEvent_t *) 0;
    swtimers[ i ].next = inanimate;
    inanimate = &swtimers[ i ];
  }
}

/*---------------------------------------------------------------------
 * This is the repetitively invoked timer poller.
 * This routine needs to be run periodically.       
 *-------------------------------------------------------------------*/
void TIM_tick()
{
  /*-----------------------------------------------------------------*/
  /* Check to see if there are timers in the ticking timers list.    */
  /*-----------------------------------------------------------------*/
  if ( animate != (ETimer_t *) 0 ) {
    if ( animate->expiration <= ETimer_msec_time() ) {
      timer_fire( animate );
    }
  }
}