// 
// <file> 
// 
// Name:        dispatch2.cpp
// 
// Purpose:     
// 
// Created:     
// 
// Modified:    5. Sept. 96 Gerbert Orasche
// 
// Description: 
// 
// The Win32 version of the dispatcher
//
// 
// </file> 

#include "dispatcher.h"

#include "fdmask.h"
#include "iohandler.h"
#include "timer.h"

#include <hyperg/utils/hgunistd.h>
#include <hyperg/utils/verbose.h>

#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <fstream.h>

const char* Dispatcher :: version1 = "Dispatcher: $Id: dispatch2.C,v 1.2 1997/02/21 16:30:12 gorasche Exp $" ;

// gorasche 050996
// need double linked list, because NT uses filehandles up to 2^32
#include "..\utils\list.h"

// we need an IOHandler* dynamic list, the key is the file descriptor
// fds are assumed to be int
class SocketNode : public DLListNode
{
// methods
public:
  SocketNode(int fd,IOHandler* handler);
  SocketNode();
  ~SocketNode();
  void operator =(SocketNode& Data);
  void setHandler(IOHandler* handler);

// data
public:
  int fd_;
  IOHandler* handler_;
  boolean valid_;
};

DLListdeclare(TmpSockList, SocketNode)

// we derive a class for the find function
class SockList: public TmpSockList
{
public:
  SocketNode* find(const int fd);
};


SocketNode::SocketNode(int fd,IOHandler* handler)
{
  fd_=fd;
  handler_=handler;
  valid_=true;
}

SocketNode::SocketNode()
{
  fd_=0;
  handler_=nil;
  valid_=false;
}

SocketNode::~SocketNode()
{
  // only static data, no destruction needed
}

void SocketNode::operator =(SocketNode& Data)
{
  fd_=Data.fd_;
  handler_=Data.handler_;
}

void SocketNode::setHandler(IOHandler* handler)
{
  handler_=handler;
  valid_=true;
}


// searches the whole list of sockets for the fd
SocketNode* SockList::find(const int fd)
{
  SocketNode* pTemp;
  for(pTemp=getFirst();pTemp&&fd!=pTemp->fd_;pTemp=getNext(pTemp));

  return pTemp;
}

  void setHandler(SocketNode* SN);


Dispatcher* Dispatcher::_instance;

Dispatcher::Dispatcher() {

   _nfds = 0;

   _rmask = new FdMask(); 
   _wmask = new FdMask();
   _emask = new FdMask();
   _rmaskready = new FdMask();
   _wmaskready = new FdMask();
   _emaskready = new FdMask();

   saved_ = false ;
   rsaved_=new FdMask();
   wsaved_=new FdMask();
   esaved_=new FdMask();
   rrsaved_=new FdMask();
   wrsaved_=new FdMask();
   ersaved_=new FdMask();

   _rtable = new SockList;
   _wtable = new SockList;
   _etable = new SockList;
   
   _queue = new TimerQueue;

   dropout_ = false ;
   child_signal_ = false ;
   // there are no deleted entries in the socket lists
   bItemsChanged_=false;

   // we need a socket for falling out at signals (non-evil ones)
   // let us assume, we do not get any errors...
   // we do not use INETSocket here to stay independent from the rest of the HW source
   m_iDropoutSock=::socket(PF_INET,SOCK_STREAM,0);
   int optval=1;
   int optlen=sizeof(int);
   int ret=::setsockopt(m_iDropoutSock,SOL_SOCKET,SO_REUSEADDR,(char*)&optval,optlen);
   struct sockaddr_in name;
   name.sin_family=AF_INET;
   name.sin_port=0;
   name.sin_addr.s_addr=htonl(INADDR_ANY) ;
   ret=::bind(m_iDropoutSock,(struct sockaddr*)&name,sizeof(name));
   // get the port number assigned
   int namelen;
   ret=::getsockname(m_iDropoutSock,(struct sockaddr*)&name,&namelen);
   m_iDropoutPort=name.sin_port;
   // now listen
   ret=::listen(m_iDropoutSock,SOMAXCONN);
   // and tell it our dispatcher instance
   _rmask->setBit(m_iDropoutSock);
}

void Dispatcher::waitForChild()
{
  ;
}

Dispatcher::~Dispatcher()
{
  delete _rmask;
  delete _wmask;
  delete _emask;
  delete _rmaskready;
  delete _wmaskready;
  delete _emaskready;

  delete rsaved_;
  delete wsaved_;
  delete esaved_;
  delete rrsaved_;
  delete wrsaved_;
  delete ersaved_;

  // delete all socket nodes
  // done by the destructor - to be verified!
/*  _rtable->free();
  _wtable->free();
  _etable->free();*/

  // delete the lists themselves
  delete _rtable;
  delete _wtable;
  delete _etable;
  delete _queue;

  ::closesocket(m_iDropoutSock);
}

Dispatcher& Dispatcher::instance()
{
  if(_instance==nil)
  {
    _instance=new Dispatcher;
  }
  return *_instance;
}

void Dispatcher::instance(Dispatcher* d)
{
  _instance=d;
}

// returns the pointer to the I/O handler class associated with fd
IOHandler* Dispatcher::handler(int fd,DispatcherMask mask) const
{
   IOHandler* cur=nil;
   const SocketNode* SDTemp;

   // let's search in our array with binary search
   if(mask==ReadMask)
     if(SDTemp=_rtable->find(fd))
       cur=SDTemp->handler_;
   else if(mask==WriteMask)
     if(SDTemp=_wtable->find(fd))
       cur=SDTemp->handler_;
   else if(mask==ExceptMask)
     if(SDTemp=_etable->find(fd))
       cur=SDTemp->handler_;
   else
     abort();
   return cur;
}

void Dispatcher::link(int fd, DispatcherMask mask, IOHandler* handler)
{
  DEBUGNL ("Dispatcher::link(): fd: "<<fd<<", mask: "<<(int)mask<<", handler: "<<handler) ;
  // somebody wants to remove this one with an invalid call!
  // do it, otherwise notify will crash!!!
  if(handler==nil)
  {
    DEBUGNL("Dispatcher::link invalid link to nil handler!!");
    unlink(fd,mask);
    return;
  }

  attach(fd,mask,handler);

  if(saved_)
  {
    switch (mask)
    {
      case Dispatcher::ReadMask: 
        rsaved_->setBit(fd);
        _rmask->clrBit(fd);
      break ;
      case Dispatcher::WriteMask: 
        wsaved_->setBit(fd);
        _wmask->clrBit(fd);
      break ;
      case Dispatcher::ExceptMask: 
        esaved_->setBit(fd);
        _emask->clrBit(fd);
        break ;
    }
  }
}

void Dispatcher::unlink(int fd)
{
  DEBUGNL ("Dispatcher::unlink(): fd: "<<fd) ;
  detach(fd);

  if(saved_)
  {
    rsaved_->clrBit(fd);
    wsaved_->clrBit(fd);
    esaved_->clrBit(fd);
  }
}

void Dispatcher::unlink(int fd,DispatcherMask m)
{
  DEBUGNL ("Dispatcher::unlink(): fd: "<<fd<<", mask: "<<(int)m) ;
  detach(fd,m);

  if(saved_)
  {
    switch(m)
    {
      case ReadMask: 
        rsaved_->clrBit(fd);
        break ;
      case WriteMask: 
        wsaved_->clrBit(fd); 
      break ;
      case ExceptMask: 
        esaved_->clrBit(fd);
      break ;
    }
  }
}

// inserts a socket node for fd into the socket list
void Dispatcher::attach(int fd,DispatcherMask mask,IOHandler* handler)
{
  SocketNode* pSNTemp;

  if(mask==ReadMask)
  {
    _rmask->setBit(fd);
    pSNTemp=_rtable->find(fd);
    if(!pSNTemp)
    {
      pSNTemp=new SocketNode(fd,handler);
      _rtable->addTail(pSNTemp);
    }
    else
      pSNTemp->setHandler(handler);
  }
  else if(mask==WriteMask)
  {
    _wmask->setBit(fd);
    pSNTemp=_wtable->find(fd);
    if(!pSNTemp)
    {
      pSNTemp=new SocketNode(fd,handler);
      _wtable->addTail(pSNTemp);
    }
    else
      pSNTemp->setHandler(handler);
  }
  else if(mask==ExceptMask)
  {
    _emask->setBit(fd);
    pSNTemp=_etable->find(fd);
    if(!pSNTemp)
    {
      pSNTemp=new SocketNode(fd,handler);
      _etable->addTail(pSNTemp);
    }
    else
      pSNTemp->setHandler(handler);
  }
  else
    abort();
  _nfds++;
}

void Dispatcher::detach(int fd)
{
  SocketNode* pSNTemp;
  _rmask->clrBit(fd);
  pSNTemp=_rtable->find(fd);
  if(pSNTemp)
  {
    pSNTemp->valid_=false;
    bItemsChanged_=true;
    _nfds--;
  }

  _wmask->clrBit(fd);
  pSNTemp=_wtable->find(fd);
  if(pSNTemp)
  {
    pSNTemp->valid_=false;
    bItemsChanged_=true;
    _nfds--;
  }

  _emask->clrBit(fd);
  pSNTemp=_etable->find(fd);
  if(pSNTemp)
  {
    pSNTemp->valid_=false;
    bItemsChanged_=true;
    _nfds--;
  }
}

void Dispatcher::detach(int fd, DispatcherMask mask)
{
  SocketNode* pSNTemp;
  if(mask==ReadMask)
  {
    _rmask->clrBit(fd);
    pSNTemp=_rtable->find(fd);
    if(pSNTemp)
    {
      pSNTemp->valid_=false;
      bItemsChanged_=true;
      _nfds--;
    }
  }
  else if(mask==WriteMask)
  {
    _wmask->clrBit(fd);
    pSNTemp=_wtable->find(fd);
    if(pSNTemp)
    {
      pSNTemp->valid_=false;
      bItemsChanged_=true;
      _nfds--;
    }
  }
  else if(mask==ExceptMask)
  {
    _emask->clrBit(fd);
    pSNTemp=_etable->find(fd);
    if(pSNTemp)
    {
      pSNTemp->valid_=false;
      bItemsChanged_=true;
      _nfds--;
    }
  }
}

void Dispatcher::startTimer(long sec,long usec,IOHandler* handler)
{
  DEBUGNL ("Dispatcher::startTimer(): handler: "<<handler) ;
  timeval deltaTime;
  deltaTime.tv_sec=sec;
  deltaTime.tv_usec=usec;
  _queue->insert(TimerQueue::currentTime()+deltaTime,handler);
}

void Dispatcher::stopTimer(IOHandler* handler)
{
  DEBUGNL ("Dispatcher::stopTimer(): handler: "<<handler) ;
  _queue->remove(handler);
}

boolean Dispatcher::setReady(int fd,DispatcherMask mask)
{
  if(handler(fd,mask)==nil)
  {
    return false;
  }
  if(mask==ReadMask)
  {
    _rmaskready->setBit(fd);
  }
  else if(mask==WriteMask)
  {
    _wmaskready->setBit(fd);
  }
  else if(mask==ExceptMask)
  {
    _emaskready->setBit(fd);
  }
  else
    return false;

  return true;
}

void Dispatcher::dispatch()
{
  // we have to implement a very lomg timeout, because the select call 
  // does not always block, if NULL is given as paramter
  timeval howlong;
  howlong.tv_sec=120000000L;
  howlong.tv_usec=0L;
  dispatch(&howlong);
}

boolean Dispatcher::dispatch(long& sec, long& usec)
{
  timeval howlong;
  timeval prevTime;
  timeval elapsedTime;

  howlong.tv_sec = sec;
  howlong.tv_usec = usec;
  prevTime = TimerQueue::currentTime();

  boolean success = dispatch(&howlong);

  elapsedTime=TimerQueue::currentTime()-prevTime;
  if(howlong>elapsedTime)
  {
    howlong=howlong-elapsedTime;
  }
  else
  {
    /* Used all of timeout */
    howlong=_queue->zeroTime(); 
  }

  sec = howlong.tv_sec;
  usec = howlong.tv_usec;
  return success;
}

boolean Dispatcher::dispatch(timeval* howlong)
{
  FdMask rmaskret;
  FdMask wmaskret;
  FdMask emaskret;
  int nfound;

  SocketNode* pSNTemp;
  SocketNode* pSNNext;
  
  // clean up the socket lists, but only if something was detached before
  // this is done only one time per dispatch and needed because sockets
  // can be detached within the callback functions, theoretically from
  // everywhere at any time
  if(bItemsChanged_)
  {
    for(pSNTemp=_rtable->getFirst();pSNTemp;)
    {
      pSNNext=_rtable->getNext(pSNTemp);
      if(!pSNTemp->valid_)
      {
        _rtable->remove(pSNTemp);
        delete pSNTemp;
      }
      pSNTemp=pSNNext;
    }
    for(pSNTemp=_wtable->getFirst();pSNTemp;)
    {
      pSNNext=_wtable->getNext(pSNTemp);
      if(!pSNTemp->valid_)
      {
        _wtable->remove(pSNTemp);
        delete pSNTemp;
      }
      pSNTemp=pSNNext;
    }
    for(pSNTemp=_etable->getFirst();pSNTemp;)
    {
      pSNNext=_etable->getNext(pSNTemp);
      if(!pSNTemp->valid_)
      {
        _etable->remove(pSNTemp);
        delete pSNTemp;
      }
      pSNTemp=pSNNext;
    }
    bItemsChanged_=false;
  }

  if(anyReady())
    nfound=fillInReady(rmaskret,wmaskret,emaskret);
  else
    nfound=waitFor(rmaskret,wmaskret,emaskret,howlong);

  // if we should drop out, do it!
  if(rmaskret.isSet(m_iDropoutPort))
    return 0;

  notify(nfound,rmaskret,wmaskret,emaskret);

  return(nfound!=0);
}


void Dispatcher::setActive(const FdMask& rmask, const FdMask& wmask,const FdMask& emask)
{
  saved_=true;
  *rsaved_=*_rmask;
  *wsaved_=*_wmask;
  *esaved_=*_emask;
  *rrsaved_=*_rmaskready;
  *wrsaved_=*_wmaskready;
  *ersaved_=*_emaskready;
  *_rmask=rmask;
  *_wmask=wmask;
  *_emask=emask;
  _rmaskready->zero();
  _wmaskready->zero();
  _emaskready->zero();
}

void Dispatcher::resetActive()
{
  saved_=false;
  *_rmask=*rsaved_;
  *_wmask=*wsaved_;
  *_emask=*esaved_;
  *_rmaskready=*rrsaved_;
  *_wmaskready=*wrsaved_;
  *_emaskready=*ersaved_;
}

boolean Dispatcher::anyReady() const
{
  return _rmaskready->anySet()||
         _wmaskready->anySet()||
         _emaskready->anySet();
}

int Dispatcher::fillInReady(FdMask& rmaskret,FdMask& wmaskret,FdMask& emaskret)
{
  rmaskret = *_rmaskready;
  wmaskret = *_wmaskready;
  emaskret = *_emaskready;
  _rmaskready->zero();
  _wmaskready->zero();
  _emaskready->zero();
  return rmaskret.numSet()+wmaskret.numSet()+emaskret.numSet();
}

int Dispatcher::waitFor(FdMask& rmaskret,FdMask& wmaskret,FdMask& emaskret,timeval* howlong)
{
  int nfound = 0 ;
  // gorasche 240796
  // if waitfor is called and no valid fds are selected just return
  // gorasche 030197
  // this cannot happen anymore, because we always wait for the dropout socket
  /*if(_nfds==0&&howlong&&howlong->tv_sec==0&&howlong->tv_usec==0)
    return 0;*/

  if(!dropout_)
  {
    do
    {
      rmaskret = *_rmask;
      wmaskret = *_wmask;
      emaskret = *_emask;
      howlong=calculateTimeout(howlong);
            
      nfound=select(_nfds,&rmaskret,&wmaskret,&emaskret,howlong);
            
      if(nfound<0)
      {
        handleError();
        nfound=0;
      }
    }
    while(nfound<0&&!dropout_);
  }
  dropout_=false;
  /* Timed out or input available */
  return nfound;
}

void Dispatcher::notify(int nfound,FdMask& rmaskret,FdMask& wmaskret,FdMask& emaskret)
{
  int status;
  int fd;
  SocketNode* pSDTemp;

  // I did not want to create a function for this, because
  // didn't want to change the header. Therefore Copy&Paste
  // gorasche 121196 for recursive calls of the dispatcher
  // the additional nfound check should help to not access
  // the next item. If there are any other found sockets
  // then let us start to pray!!!!!!
  for(pSDTemp=_rtable->getFirst();pSDTemp&&nfound;pSDTemp=nfound?_rtable->getNext(pSDTemp):nil)
  {
    fd=pSDTemp->fd_;
    if(rmaskret.isSet(fd))
      if(pSDTemp->valid_)
      {
        DEBUGNL ("Dispatcher::notify(): readmask, fd: "<<fd) ;
        status=pSDTemp->handler_->inputReady(fd);
	      if(status<0)
          detach(fd);
	      else if(status>0)
          _rmaskready->setBit(fd);
  	    nfound--;
	    }
      else
      {
        DEBUGNL ("Dispatcher::notify(): readmask, fd: invalidated element (handler not called!)"<<fd) ;
      }
  }

  for(pSDTemp=_wtable->getFirst();pSDTemp&&nfound;pSDTemp=nfound?_wtable->getNext(pSDTemp):nil)
  {
    fd=pSDTemp->fd_;
    if(wmaskret.isSet(fd))
      if(pSDTemp->valid_)
      {
        DEBUGNL ("Dispatcher::notify(): writemask, fd: "<<fd) ;
        status=pSDTemp->handler_->outputReady(fd);
	      if(status<0)
        {
          pSDTemp=_wtable->getPrev(pSDTemp);
          detach(fd);
        }
	      else if(status>0)
          _wmaskready->setBit(fd);
  	    nfound--;
	    }
      else
      {
        DEBUGNL ("Dispatcher::notify(): writemask, fd: invalidated element (handler not called!)"<<fd) ;
      }
  }

  for(pSDTemp=_etable->getFirst();pSDTemp&&nfound;pSDTemp=nfound?_etable->getNext(pSDTemp):nil)
  {
    fd=pSDTemp->fd_;
    if(emaskret.isSet(fd))
      if(pSDTemp->valid_)
      {
        DEBUGNL ("Dispatcher::notify(): exceptmask, fd: "<<fd) ;
        status=pSDTemp->handler_->exceptionRaised(fd);
	      if(status<0)
        {
          pSDTemp=_etable->getPrev(pSDTemp);
          detach(fd);
        }
  	    else if(status>0)
          _emaskready->setBit(fd);
    	  nfound--;
  	  }
      else
      {
        DEBUGNL ("Dispatcher::notify(): exceptmask, fd: invalidated element (handler not called!)"<<fd) ;
      }
  }

  if(!_queue->isEmpty())
    _queue->expire(TimerQueue::currentTime());
}

// calculates timeout out of the queue
timeval* Dispatcher::calculateTimeout(timeval* howlong) const
{
  static timeval timeout;

  if (!_queue->isEmpty())
  {
    timeval curTime;

    curTime = TimerQueue::currentTime();
    if(_queue->earliestTime()>curTime)
    {
      timeout = _queue->earliestTime() - curTime;
      if(howlong == nil || *howlong > timeout)
      {
        howlong = &timeout;
      }
    }
    else
    {
      timeout = TimerQueue::zeroTime();
      howlong = &timeout;
    }
  }
  return howlong;
}

void Dispatcher::handleError()
{
  int errNo=WSAGetLastError();
  if(errNo == WSAEWOULDBLOCK || errNo == WSAEINTR)
    return;

/*  dropOut(m_iDropoutPort);*/
  dropOut();
  if (errNo == WSAEBADF||WSAENOTSOCK)
  {
    checkConnections();
    return;
  }
  if(errNo==WSAEINVAL)
  {
    cerr << "somebody kicked my ass off (WSAEINVAL)!" << endl;
    return;
  }
  if(errNo==WSANOTINITIALISED)
  {
    cerr << "Uuupps forgot to initialize WinSock!! (WSANOTINITIALISED)!" << endl;
    exit(-2);
  }


  cerr << "Dispatcher error:" << errNo << endl;
  //exit(1);
  return;
}

// checks all active read sockets and detaches errornous
void Dispatcher::checkConnections()
{
  FdMask rmask;
  FdMask wmask;
  FdMask emask;

  timeval poll=TimerQueue::zeroTime();
  int fd;
  SocketNode* pSDTemp;
  int fdcount=1;

  if(_rtable->count()==0&&_wtable->count()&&_etable->count())
  {
    cerr << "Sorry! I have got no valid handle entries anymore" << endl;
    cerr << "COMITTING SUICIDE..." << endl;
    ExitProcess(4718);
  }

  for(pSDTemp=_rtable->getFirst();pSDTemp;pSDTemp=_rtable->getNext(pSDTemp))
  {
    if(pSDTemp->valid_)
    {
      fd=pSDTemp->fd_;
      rmask.setBit(fd);
      if(select(fdcount,&rmask,&wmask,&emask,&poll)<0)
      {
        cerr << "deleting fd " << fd << "in panic!!!!!" << endl;
        detach(fd);
      }
      rmask.clrBit(fd);
      fdcount++;
    }
  }
}

void Dispatcher::dropOut()
{
  int fd=::socket(PF_INET,SOCK_STREAM,0);
  struct sockaddr_in name;
  name.sin_family=AF_INET;
  name.sin_port=m_iDropoutPort;
  name.sin_addr.s_addr=inet_addr("127.0.0.1");
  // now let it fall out!
  ::connect(fd,(struct sockaddr*)&name,sizeof(name));
  ::closesocket(fd);
} 
