/*                                              -*- mode:C++; fill-column:100 -*-
  SFBAlarm.h - Support for time-based events
  Copyright (C) 2009 The Regents of the University of New Mexico.  All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
  USA
*/

/**
  \file SFBAlarm.h Support for time-based events.
  \author David H. Ackley.  
  \date (C) 2009 All rights reserved.
  \lgpl
 */
#ifndef SFBALARM_H_
#define SFBALARM_H_

#include "SFBTypes.h"
#include "SFBWiring.h"  /* for millis() */

/**
   A function pointer type that can be called when an alarm is triggered.  This is the simpler of
   the such funtion pointer types; see SFBAlarmArgHandler for the more complicated type.
 */
typedef void (*SFBAlarmHandler)(u32 when);

/**
   A function pointer type that can be called when an alarm is triggered.  This is the more powerful
   of the such funtion pointer types; see SFBAlarmArgHandler for the more limited type.
 */
typedef void (*SFBAlarmArgHandler)(u32 when, void *arg);

#ifndef SFBALARM_QUEUE_SIZE
#define SFBALARM_QUEUE_SIZE 51  ///< The maximum number of alarms.  The RAM footprint of an SFBAlarm
                                ///  grows (at least) as (14 bytes * SFBALARM_QUEUE_SIZE).
#endif

#if SFBALARM_QUEUE_SIZE < 256
typedef u8 SFBAlarmIndexType;   ///< The type of an alarm index.  u8 suffices unless SFBALARM_QUEUE_SIZE exceeds 255
#else
#if SFBALARM_QUEUE_SIZE < 65536
typedef u16 SFBAlarmIndexType;
#else
typedef u32 SFBAlarmIndexType;
#endif
#endif

/**
 * Manage "alarm clock"-style settable timers.
 */
extern class SFBAlarm {
  enum { MAX_EVENTS = SFBALARM_QUEUE_SIZE };
 public: 

  SFBAlarm() ;

  u32 create(SFBAlarmHandler handler) ;  /* Create a new alarm object, return alarm number > 0 or 0 if full */ 

  u32 create(SFBAlarmArgHandler handler, void * arg) ; /* Create a new alarm object, return alarm number > 0 or 0 if full */ 

  void update(u32 alarmNumber, SFBAlarmHandler handler) { /* Alter the handler of an existing alarm */ 
    update(alarmNumber,(SFBAlarmArgHandler) handler, (void*) 0);
  }

  void update(u32 alarmNumber, SFBAlarmArgHandler handler, void * arg); /* Alter the handler and/or arg of an existing alarm */ 

  bool set(u32 alarmNumber, u32 when) ;                   /* Set (or reset) alarmNumber to go off at when; true if was set */

  bool cancel(u32 alarmNumber) ;                          /* Cancel any pending alarm for alarmNumber, true if was set */

  u32 currentAlarmNumber() { return currentAlarmIndex; }      /* what alarm is active (only valid during callback) */ 

  int runDueEvents(int maxEvents, u32 deadline) ;

  int runPriorityEvents(int maxEvents, u32 priorityAtLeast) ;

  bool canCreate() ;

  u32 created() { 
    return alarms; 
  }

  bool canAdd() ;

  bool canRemove() ;

  u32 count() { return used; }

  bool inUse(u32 alarmIndex) {          /* true iff alarmIndex is a legal, currently scheduled alarm number */
    return alarmIndex <= alarms
      && idxs[alarmIndex] > 0;
  }

  /**
   * Get the earliest time in the queue.  Only valid
   * when canRemoveEvent() returns true.
   */
  u32 getTopTime() {
    return times[slots[1]];
  }

  int getUsed() {
    return used;
  }
 private:

  /**
   * Remove the topmost element and run it.  Only valid
   * when canRemoveEvent() returns true.
   */
  void removeAndRunTop() ;

  void deleteElt(int eltIdx);              /* eltIdx must currently be in pq! */

  u32 times[MAX_EVENTS];                   /* when this event goes off */
  SFBAlarmArgHandler handlers[MAX_EVENTS]; /* who to call when it does */
  void * args[MAX_EVENTS];                 /* argument to pass back to them */
  SFBAlarmIndexType slots[MAX_EVENTS];     /* priority queue map slot idx->elt idx */
  SFBAlarmIndexType idxs[MAX_EVENTS];      /* reverse map elt idx->slot idx */
  SFBAlarmIndexType used;
  SFBAlarmIndexType alarms;
  SFBAlarmIndexType currentAlarmIndex;

  void bubbleUp(int fromIndex);
  void bubbleDown(int fromIndex);

#ifdef TEST_SFBALARM
  friend void heapcheck(int) ;
  friend void runRandoms() ;
#endif

} Alarms;

#endif /*SFBALARM_H_*/
