/*************************************************
OpengateM - MAC address authentication system 
  module for multi-alarms 

Copyright (C) 2006 Opengate Project Team
Written by Yoshiaki Watanabe

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 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.

Email: watanaby@is.saga-u.ac.jp
**************************************************/

/*** alarms example ****
This case, the alarms are ringed after 5 and 8 seconds

int main(void)
{
  AddAlarm("alarm1", 8, FALSE, func1);
  AddAlarm("alarm2", 5, FALSE, func2);
  EnableAlarm();
 }
void func1(int signo){
  printf("func1 is called at %d\n",time(NULL));
}
void func2(int signo){
  printf("func2 is called at %d\n",time(NULL));
}
**********/

#include	"opengatemmng.h"

typedef struct alarm{ /* an item in the alarm list */
  char name[WORDMAXLN]; /* the alarm name */
  int timeout;          /* the duration(sec) to timeout */
  int unixtime;         /* the unix time to timeout */
  int preceding;         /* if true, precede to other alarms */
  Sigfunc *func;       /* function to call at timeout */
  struct alarm *next;  /* the next item in list */
} Alarm;

int addAlarm(char *name, int timeout, int preceding, Sigfunc *func);
int removeAlarm(char *name);
int delAlarmListItem(Alarm *pDel);
int enableAlarm(void);
int disableAlarm(void);
void alarmSigFunc(int signo);

static Sigfunc *defaultSigfunc;
static Alarm *pRunningAlarm=NULL;
static Alarm *pAlarmTop=NULL;


/***************************************************
Add a new alarm named <name>, which calls 
function <func> after <timeout> seconds
If <preceding> is TRUE, the alarm precedes to other alarms
(the alarm has first priority. other alarms are delayed after the alarm)
Multiple alarms can be registered in the alarm list
At timeout, the alarm is removed from the alarm list
after the calling addAlarm, alarms are disabled
***************************************************/
int addAlarm(char *name, int timeout, int preceding, Sigfunc *func)
{
  Alarm *pNew;
  Alarm *p;
  Alarm *pPrev;

  /* disable Alarm */
  disableAlarm();

  /* memory allocate for the alarm list item */
  if((pNew = (Alarm *) malloc(sizeof(Alarm))) == NULL){
    //   err_msg("ERR at %s#%d: malloc error",__FILE__,__LINE__);
    return -1;
  }

  /* set the item info */
  strlcpy(pNew->name, name, WORDMAXLN);
  pNew->timeout = timeout;
  pNew->unixtime = time(NULL) + timeout;
  pNew->preceding = preceding;
  pNew->func = func;
  
  /* if list is null, add as first item */
  if(pAlarmTop ==NULL){
    pAlarmTop = pNew;
    pNew->next =NULL;
  }

  /* if preceding alarm, insert it at the top */
  else if(preceding==TRUE){
    pNew->next =pAlarmTop;
    pAlarmTop = pNew;
  }

  /* if list is not null, search the item later than the new alarm 
     and not a preceding alarm */
  else{
    p = pPrev = pAlarmTop;
    while(p!=NULL){
      if(p->unixtime >= pNew->unixtime && (p->preceding)==FALSE) break;
      pPrev = p;
      p = p->next;
    }
    
    /* insert new item before the item */
    if(p==pAlarmTop) pAlarmTop = pNew;
    else pPrev->next = pNew;
    pNew->next = p;
  }

  return 0;
}

/***************************************************
Remove alarm named <name> from the alarm list 
after the calling removeAlarm, alarms are disabled
***************************************************/
int removeAlarm(char *name)
{
  Alarm *p, *pPrev, *pDel;


  /* disable alarm */
  disableAlarm();

  p=pAlarmTop;

  if(name==NULL){
    /* if name is NULL, all items are removed */
    while(p!=NULL){
      pDel = p;
      p = p->next;
      free(pDel);
    }
    pAlarmTop=NULL;
  }

  else{
    /* scan alarm list to search name and delete it */
    while(p!=NULL){
      
      /* if name is matched */
      if(strncmp(name, p->name, WORDMAXLN)==0){
	
	/* delete the item */
	delAlarmListItem(p);

	/* exit (only the first match item is deleted)  */
	break;
      }
      
      /* if not matched, move to next item */
      else{
	pPrev = p;
	p = p->next;
      }
    }
  }

  return 0;
}


/***************************************************
Enable alarm interupt
Delayed alarms by preceding alarm or by disable-mode 
are executed in this timing
***************************************************/
int enableAlarm(void)
{
  Alarm *p, *pDel;
  int alarmemain=0;
  int alarmSet=FALSE;

  p=pAlarmTop;

  /* scan all list */
  while(p!=NULL){
    
    /* time remain to the timeout */
    alarmemain = p->unixtime - time(NULL);
    
    /* if the time is the past */
    if(alarmemain<=0){
      
      /* execute the function and save item for deletion */
      p->func(0);
      pDel = p;
      
      /* move to next item */
      p = p->next;
      
      /* delete the list item */
      delAlarmListItem(pDel);
    }
    
    /* if the time is the future */
    else{
      /* save the alarm info */
      pRunningAlarm = p;
      
      /* set signal alarm */
      if(defaultSigfunc==NULL)defaultSigfunc=signalx(SIGALRM, alarmSigFunc);
      else signalx(SIGALRM, alarmSigFunc);
      alarm(alarmemain);
      alarmSet=TRUE;

      /* exit */
      break;
    }
  }
  return 0;
}


/***************************************************
Disable alarm interupt
Time count is not stoped in disable mode
***************************************************/
int disableAlarm(void)
{
  /* reset alarm */
  signalx(SIGALRM, defaultSigfunc);
  alarm(0);

  return 0;
}

/***************************************************
Signal function for alarm signal
***************************************************/
void alarmSigFunc(int signo)
{
  /* some alarm must run at this point, but check it */
  if(pRunningAlarm != NULL){

    /* execute the function */
    (pRunningAlarm->func)(signo);

    /* remove the alarm */
    delAlarmListItem(pRunningAlarm);
  }

  /* restart alarm */
  enableAlarm();
}

/***************************************************
delete an item pointed by <p> in linked list
***************************************************/
int delAlarmListItem(Alarm *pDel){
  Alarm *p;
  Alarm *pPrev;

  /* if the item is none, no proc */
  if(pDel==NULL){
    /* no proc */
  }

  /* if the item is top, change top to the next */
  else if(pDel==pAlarmTop){
    pAlarmTop = pDel->next;
    free(pDel);
  }

  /* if the item is not top, search previous and link it to next */
  else{
    pPrev=pAlarmTop;
    p=pAlarmTop->next;

    while(p!=NULL){
      if(p == pDel){
	pPrev->next = p->next;
	free(p);
	break;
      }
      pPrev=p;
      p=p->next;
    }
  }
  return 0;
}

/***************************************************
list up registered alarms
upper alarm in the list has higher priority 
this is prepared for debug use
***************************************************/
void listAlarm(void)
{
  Alarm *p;

  printf("TimeNow=%d\n",(int)time(NULL));

  p=pAlarmTop;

  while(p!=NULL){
    printf("name=%s timeout=%d unixtime=%d preceding=%d\n", 
	   p->name, p->timeout, p->unixtime, p->preceding);
    p=p->next;
  }
}
/*****************************************************/
/*****************************************************/
int AddAlarm(char *name, int timeout, int preceding, Sigfunc *func){
  int ret;

  if(debug>1) err_msg("DEBUG:=>addAlarm(%s,%d, %d, %x)", 
		    name, timeout, preceding, func);
  ret=addAlarm(name, timeout, preceding, func);
  if(debug>1) err_msg("DEBUG:(%d)<=addAlarm( )",ret);

  return ret;
}

int RemoveAlarm(char *name){
  int ret;

  if(debug>1){
    if(name==NULL) err_msg("DEBUG:=>removeAlarm(NULL)");
    else           err_msg("DEBUG:=>removeAlarm(%s)", name);
  }
  ret=removeAlarm(name);
  if(debug>1) err_msg("DEBUG:(%d)<=removeAlarm( )",ret);

  return ret;
}

int EnableAlarm(void){
  int ret;

  if(debug>1) err_msg("DEBUG:=>enableAlarm()");
  ret=enableAlarm();
  if(debug>1) err_msg("DEBUG:(%d)<=enableAlarm( )",ret);

  return ret;
}

int DisableAlarm(void){
  int ret;

  //  if(debug>1) err_msg("DEBUG:=>disableAlarm()");
  ret=disableAlarm();
  //  if(debug>1) err_msg("DEBUG:(%d)<=disableAlarm( )",ret);

  return ret;
}
