/* ***************************************************************** *
 * Copyright 1998 International Business Machines Corporation. All   *
 * Rights Reserved.                                                  *
 *                                                                   *
 * Please read this carefully.  Your use of this reference           *
 * implementation of certain of the IETF public-key infrastructure   *
 * specifications ("Software") indicates your acceptance of the      *
 * following.  If you do not agree to the following, do not install  *
 * or use any of the Software.                                       *
 *                                                                   *
 * Permission to use, reproduce, distribute and create derivative    *
 * works from the Software ("Software Derivative Works"), and to     *
 * distribute such Software Derivative Works is hereby granted to    *
 * you by International Business Machines Corporation ("IBM").  This *
 * permission includes a license under the patents of IBM that are   *
 * necessarily infringed by your use of the Software as provided by  *
 * IBM.                                                              *
 *                                                                   *
 * IBM licenses the Software to you on an "AS IS" basis, without     *
 * warranty of any kind.  IBM HEREBY EXPRESSLY DISCLAIMS ALL         *
 * WARRANTIES OR CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING,   *
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR CONDITIONS OF       *
 * MERCHANTABILITY, NON INFRINGEMENT AND FITNESS FOR A PARTICULAR    *
 * PURPOSE.  You are solely responsible for determining the          *
 * appropriateness of using this Software and assume all risks       *
 * associated with the use of this Software, including but not       *
 * limited to the risks of program errors, damage to or loss of      *
 * data, programs or equipment, and unavailability or interruption   *
 * of operations.                                                    *
 *                                                                   *
 * IBM WILL NOT BE LIABLE FOR ANY DIRECT DAMAGES OR FOR ANY SPECIAL, *
 * INCIDENTAL, OR  INDIRECT DAMAGES OR FOR ANY ECONOMIC              *
 * CONSEQUENTIAL DAMAGES (INCLUDING LOST PROFITS OR SAVINGS), EVEN   *
 * IF IBM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  IBM  *
 * will not be liable for the loss of, or damage to, your records or *
 * data, or any damages claimed by you based on a third party claim. *
 *                                                                   *
 * IBM wishes to obtain your feedback to assist in improving the     *
 * Software.  You grant IBM a world-wide, royalty-free right to use, *
 * copy, distribute, sublicense and prepare derivative works based   *
 * upon any feedback, including materials, error corrections,        *
 * Software Derivatives, enhancements, suggestions and the like that *
 * you provide to IBM relating to the Software (this does not        *
 * include products for which you charge a royalty and distribute to *
 * IBM under other terms and conditions).                            *
 *                                                                   *
 * You agree to distribute the Software and any Software Derivatives *
 * under a license agreement that: 1) is sufficient to notify all    *
 * licensees of the Software and Software Derivatives that IBM       *
 * assumes no liability for any claim that may arise regarding the   *
 * Software or Software Derivatives, and 2) that disclaims all       *
 * warranties, both express and implied, from IBM regarding the      *
 * Software and Software Derivatives.  (If you include this          *
 * Agreement with any distribution of the Software or Software       *
 * Derivatives you will have met this requirement.)  You agree that  *
 * you will not delete any copyright notices in the Software.        *
 *                                                                   *
 * This Agreement is the exclusive statement of your rights in the   *
 * Software as provided by IBM.   Except for the rights granted to   *
 * you in the second paragraph above, You are not granted any other  *
 * patent rights, including but not limited to the right to make     *
 * combinations of the Software with products that infringe IBM      *
 * patents. You agree to comply with all applicable laws and         *
 * regulations, including all export and import laws and regulation. *
 * This Agreement is governed by the laws of the State of New York.  *
 * This Agreement supersedes all other communications,               *
 * understandings or agreements we may have had prior to this        *
 * Agreement.                                                        *
 * ***************************************************************** */

#include "jnhTimer.h"
#include "pkiTask.h"

static pki_mutex_t mutex;

class init_obj_t {
public:
	init_obj_t(void);
};

void wakeup_rtn(void * args);

init_obj_t::init_obj_t(void) {
	mutex = PKI_MUTEX_INIT;
	pkiCreateMutex(&mutex, 0);
	pkiStartTask(wakeup_rtn, NULL);
}
static init_obj_t init_obj;

class timer_event {

  friend uint32 JnhRegisterEventAbs(time_t,	void (* event_rtn)(void *), void *, bool, uint32 * );
  friend uint32 JnhRegisterEventDelta(unsigned long, void (* event_rtn)(void *), void *, bool, uint32 * );
  friend uint32 JnhCancelEvent(uint32);
  friend uint32 JnhCancelAll(void);
  friend void wakeup_rtn(void *);

  static timer_event * root;
	static uint32 next_event;
	timer_event * prev;
	timer_event * next;
public:
	time_t event_time;
	uint32 event_id;
	bool active;
  bool shareThread;
	void (* event_rtn)(void * arg);
  void * event_arg;

	timer_event(time_t evt,
					void (* ertn)(void * arg),
					void * arg,
          bool shareThrd);
	~timer_event();
};

timer_event * timer_event::root = NULL;

uint32 timer_event::next_event = 1;

timer_event::timer_event(time_t evt,
								 void (* ertn)(void * arg),
								 void * arg,
                 bool shareThrd) {
	prev = NULL;
	next = NULL;
	timer_event * ptr = root;
	timer_event * last_ptr = NULL;
	active = false;
	event_time = evt;
	event_arg = arg;
	event_rtn = ertn;
  shareThread = shareThrd;
	event_id = next_event++;
	if (root == NULL) {
		root = this;
	} else {
		while ((ptr != NULL) && (ptr->event_time < evt)) {
			last_ptr=ptr;
			ptr=ptr->next;
		};
		prev = last_ptr;
		next = ptr;
		if (last_ptr != NULL) last_ptr->next = this;
		if (ptr != NULL) ptr->prev = this;
	};
}

timer_event::~timer_event() {
	if (root == this) root = next;
	if (prev) prev->next = next;
	if (next) next->prev = prev;
}

uint32 JnhRegisterEventAbs(
	time_t event_time,
	void (* event_rtn)(void * arg),
	void * arg,
  bool shareThread,
	uint32 * event_id) {

	timer_event * ptr;

	pkiLockMutex(&mutex);
	ptr = new timer_event(event_time,
								 event_rtn,
								 arg,
                 shareThread);
	if (event_id) *event_id = ptr->event_id;
	pkiUnlockMutex(&mutex);
	return 0;
}

uint32 JnhRegisterEventDelta(
	unsigned long delta_seconds,
	void (* event_rtn)(void * arg),
	void * arg,
  bool shareThread,
	uint32 * event_id) {

	time_t wakeup_time;
	timer_event * ptr;
  struct tm wakeup_time_tm;

	pkiLockMutex(&mutex);

// This next bit is horrible.  Why doesn't the C Standard Library
// provide an addtime() function to add X seconds to a time_t?
  wakeup_time = time(NULL);
  memcpy(&wakeup_time_tm, localtime(&wakeup_time), sizeof(struct tm));
  wakeup_time_tm.tm_sec += delta_seconds;
  wakeup_time = mktime(&wakeup_time_tm);
  
  ptr = new timer_event(wakeup_time,
								 event_rtn,
								 arg,
                 shareThread);
	if (event_id) *event_id = ptr->event_id;
	pkiUnlockMutex(&mutex);
	return 0;
}

uint32 JnhCancelEvent(uint32 event_id) {
	timer_event * ptr;
	timer_event * next_ptr;
	pkiLockMutex(&mutex);
	while (ptr != NULL) {
		next_ptr = ptr->next;
		if (ptr->event_id == event_id) {
			if (!ptr->active) delete ptr;
			pkiUnlockMutex(&mutex);
			return 0;
		};
		ptr = next_ptr;
	};
	pkiUnlockMutex(&mutex);
	return 1;
}

uint32 JnhCancelAll(void) {
	timer_event * ptr;
	timer_event * next_ptr;
	pkiLockMutex(&mutex);
  ptr = timer_event::root;
	while (ptr != NULL) {
		next_ptr = ptr->next;
		if (!ptr->active) delete ptr;
		ptr = next_ptr;
	};
	pkiUnlockMutex(&mutex);
	return 0;
}


void wakeup_rtn(void * args) {
	time_t now;
	timer_event * ptr;
	double secs;

	while (1) {
		pkiLockMutex(&mutex);
    if (timer_event::root == NULL) {
			pkiUnlockMutex(&mutex);
			pkiSleep(10);  // Wake every ten seconds just to check...
		} else {
			now = time(NULL);
      if (now >= timer_event::root->event_time) {
				// Make sure this event doesn't get cancelled
				timer_event::root->active = true;
				ptr = timer_event::root;
				pkiUnlockMutex(&mutex);
        if (ptr->shareThread) ptr->event_rtn(ptr->event_arg);
        else pkiStartTask(ptr->event_rtn, ptr->event_arg);
				pkiLockMutex(&mutex);
				delete ptr;
				now = time(NULL);
			};
      
      if (timer_event::root != NULL) secs = difftime(timer_event::root->event_time, now);
      else secs = 10;
      
      pkiUnlockMutex(&mutex);
			
      if (secs <= 0) continue;  // More events to process - don't sleep
      if (secs > 10) secs = 10; // Sleep for a max of 10 secs
			
      pkiSleep ((unsigned long)secs);
		};
	};
}

