/*********************************************************************
 *
 *         EZWGL, the EZ Widget and Graphics Library
 *
 *             Copyright (C) 1996, 1997  Maorong Zou
 *  
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **********************************************************************/
/*
 *  June 1996.  Beta Release.
 *  Sept 1996.  Release Version 1.0
 *  Dec 1996.  Release Version 1.1 Beta
 *  April 1997.  Release Version 1.2
 *  November 1997.  Release Version 1.3
 */
#define _EZ_INPUT_C_
/***********************************************************/
#include "EZ_Widget.h"
/***********************************************************/
void         EZ_InitializeInputIdList  MY_ANSIARGS((void));
void         EZ_DestroyInputIdList  MY_ANSIARGS((void));
void         *EZ_AddInput MY_ANSIARGS((int fd, int mask, 
				       EZ_InputCallBack callback,
				       void *data));
void         EZ_RemoveInput MY_ANSIARGS((void *id));
int          EZ_CheckAppInputs MY_ANSIARGS((unsigned long length));
/***********************************************************/
#ifdef EZ_ADD_INPUT
static EZ_InputId *findInputIdFromFd  MY_ANSIARGS((int fd));
/***********************************************************/
static EZ_InputId    AppInputIdListHead;
/***********************************************************/
void EZ_InitializeInputIdList()
{
  EZ_InputId *tmp = &AppInputIdListHead;
  tmp->prev = tmp;
  tmp->next = tmp;
}
void EZ_DestroyInputIdList()
{
  EZ_InputId *head = &AppInputIdListHead;
  EZ_InputId *tmp = head->next;
  while(tmp != head)
    {
      EZ_InputId *t = tmp->next;
      (void)my_free((char *)tmp);
      tmp = t;
    }
  head->next = head;
  head->prev = head;
}
/***********************************************************/
static EZ_InputId *findInputIdFromFd(fd)
     int fd;
{
  EZ_InputId *head = &AppInputIdListHead;
  EZ_InputId *tmp = head->next;

  while(tmp != head)
    {
      if(tmp->fd == fd) return(tmp);
      tmp = tmp->next;
    }
  return(NULL);
}

static EZ_InputId *findInputId(id)
     EZ_InputId *id;
{
  EZ_InputId *head = &AppInputIdListHead;
  EZ_InputId *tmp = head->next;

  while(tmp != head)
    {
      if(tmp == id) return(tmp);
      tmp = tmp->next;
    }
  return(NULL);
}

/***********************************************************/
void  *EZ_AddInput(fd, mask, callback, data)
     int fd;
     int mask;
     EZ_InputCallBack callback;
     void  *data;
{
  if(fd >= 0 && fd < FD_SETSIZE)
    {
      EZ_InputId *iid = findInputIdFromFd(fd);

      if(iid == NULL)
	{
	  EZ_InputId  *next, *head = &AppInputIdListHead;
	  
	  iid = my_malloc(sizeof(EZ_InputId), _INPUT_ID_);
	  if(iid == NULL)  EZ_OutOfMemory("EZ_AddInput");
	  next = head->next;
	  head->next = iid;
	  next->prev = iid;
	  iid->next = next;
	  iid->prev = head;
	  iid->inAction = 0;
	  iid->deleted = 0;
	}
      iid->fd = fd;
      iid->setMask = mask;
      iid->mask =  0;
      iid->callback = callback;
      iid->data = data;

      return((void *)iid);
    }
  return(NULL);
}
/***********************************************************/
void EZ_RemoveInput(ptr)
     void *ptr;
{
  EZ_InputId *id = (EZ_InputId *)ptr;
  if(id && id == findInputId(id))  id->deleted = 1;
}
/***********************************************************
 *
 *  Check whether or not any fd is ready. If there are
 *  any, invoke their callbacks. 
 *  Return value:
 *     -1 if there are no files to watch. otherwise,
 *        return the number of fds which are ready.   
 */

int  EZ_CheckAppInputs(length)
     unsigned long length;  /* time to wait */
{
  fd_mask       fd_masks[3 * EZ_FDMSIZE];
  EZ_InputId    *next, *tmpInputId, *head = &AppInputIdListHead;  
  int maxfd_1 = 0; 
  
  next = head->next;
  if(next != head)
    {
      memset(fd_masks, 0, (3* EZ_FDMSIZE)*sizeof(fd_mask));

      while(next != head)
	{
	  tmpInputId = next->next;

	  if(next->inAction == 0)
	    {
	      int fd = next->fd;
	      int setmask = next->setMask;
	      if(next->deleted == 1)  /* marked removed. Remove it now */
		{
		  EZ_InputId *front = next->prev;      
		  EZ_InputId *back = next->next;      
      
		  front->next = back;
		  back->prev = front;
		  (void) my_free( (char *)next);
		}
	      else
		{
		  int elt = EZ_FDELT(fd);    /* which elt in fd_masks */
		  int bit = EZ_FDMASK(fd);   /* which bit in **[elt]  */
	      
		  if(setmask & EZ_READABLE_MASK)
		    fd_masks[elt] |= bit;
		  if(setmask & EZ_WRITABLE_MASK)
		    fd_masks[elt+EZ_FDMSIZE] |= bit;
		  if(setmask & EZ_EXCEPTION_MASK)
		    fd_masks[elt + 2*EZ_FDMSIZE] |= bit;
		  
		  next->mask = 0;
		  if(maxfd_1 <= fd) maxfd_1 = fd + 1;
		}
	    }
	  next = tmpInputId;
	}
    }
  if(maxfd_1 > 0)  /* we have some fds, go wait */
    {
      int count;
      struct timeval tmout;
      tmout.tv_usec = length % 1000000;
      tmout.tv_sec  = length / 1000000;
      count = select(maxfd_1, (fd_set *) (&fd_masks[0]),
		     (fd_set *)(&fd_masks[EZ_FDMSIZE]),
		     (fd_set *)(&fd_masks[2*EZ_FDMSIZE]),
		     &tmout);
      if(count > 0)
	{
	  next = head->next;
	  while(next != head)
	    {
	      if(next->inAction == 0)
		{
		  int fd = next->fd;
		  int mask = 0;
		  int elt = EZ_FDELT(fd);    /* which elt in fd_masks */
		  int bit = EZ_FDMASK(fd);   /* which bit in **[elt]  */
		  
		  if(fd_masks[elt] & bit)
		    mask |= EZ_READABLE_MASK;
		  if(fd_masks[elt+EZ_FDMSIZE] & bit)
		    mask |= EZ_WRITABLE_MASK;
		  if(fd_masks[elt + 2*EZ_FDMSIZE] & bit)
		    mask |= EZ_EXCEPTION_MASK;		    

		  next->mask = (next->setMask) & mask;
		}
	      next = next->next;
	    }

	  /* invoke callbacks */
	  next = head->next;	  
	  while(next != head)
	    {
	      EZ_InputId *tmp = next->next;
	      if(next->inAction == 0)
		{
		  int mask = next->mask;
		  if(mask && next->callback)
		    {
		      next->inAction = 1;
		      next->mask = 0;
		      (next->callback)(next, next->data, next->fd, mask);
		      next->inAction = 0;
		    }
		}
	      next = tmp;
	    }
	}
      return(count);
    }
  return(-1);
}
#else
void  EZ_InitializeInputIdList() { }
void  EZ_DestroyInputIdList() { }
void  *EZ_AddInput(fd, mask, callback, data) 
     int fd, mask; EZ_InputCallBack callback; void *data;
{ return(NULL); }
void EZ_RemoveInput(id) void *id; { };
int EZ_CheckAppInputs(length) 
     unsigned long length;
{
  return(-1);
}
#endif
#undef _EZ_INPUT_C_  
/****************************************************************************************/	  
	  
	  
	    



