//$Id$

#include "ace/QtReactor/QtReactor.h"

ACE_BEGIN_VERSIONED_NAMESPACE_DECL

ACE_ALLOC_HOOK_DEFINE (ACE_QtReactor)

// Must be called with lock held

ACE_QtReactor::ACE_QtReactor (QApplication *qapp ,
    ACE_Sig_Handler *sh,
    ACE_Timer_Queue *tq,
    int disable_notify_pipe,
    ACE_Reactor_Notify *notify,
    bool mask_signals,
    int s_queue ):
    ACE_Select_Reactor( sh, tq, disable_notify_pipe,
        notify, mask_signals, s_queue),
    qapp_(qapp),
    qtime_ (0)
{
    reopen_notification_pipe();
}

// Must be called with lock held
ACE_QtReactor::ACE_QtReactor (size_t size,
    QApplication *qapp,
    bool restart,
    ACE_Sig_Handler *sh,
    ACE_Timer_Queue *tq,
    int disable_notify_pipe,
    ACE_Reactor_Notify *notify,
    bool mask_signals,
    int s_queue):
    ACE_Select_Reactor( size, restart, sh, tq,
        disable_notify_pipe, notify, mask_signals,
        s_queue ),
    qapp_(qapp),
    qtime_ (0)

{
    reopen_notification_pipe();
}

void ACE_QtReactor::reopen_notification_pipe( void)
{
  // When the ACE_Select_Reactor is constructed it creates the notify
  // pipe and registers it with the register_handler_i() method. The
  // QtReactor overloads this method BUT because the
  // register_handler_i occurs when constructing the base class
  // ACE_Select_Reactor, the ACE_Select_Reactor register_handler_i()
  // is called not the QtReactor register_handler_i().  This means
  // that the notify pipe is registered with the ACE_Select_Reactor
  // event handling code not the QtReactor and so notfications don't
  // work.  To get around this we simply close and re-opened the
  // notification handler in the constructor of the QtReactor.

#if defined (ACE_MT_SAFE) && (ACE_MT_SAFE != 0)
    if ( initialized_ )
    {
        this->notify_handler_->close ();

        // Patch for MS Windows: close and open doesn't clear the read
        // fd_set, so reset it manually
        this->wait_set_.rd_mask_.reset ();

        this->notify_handler_->open (this, 0);
    }
#endif /* ACE_MT_SAFE */
}

ACE_QtReactor::~ACE_QtReactor (void)
{
    // iterate over QSocketNotifiers for read and release them
    MAP::ITERATOR iter = this->read_notifier_.begin ();
    MAP::ITERATOR iterEnd = this->read_notifier_.end ();
    while( iter != iterEnd )
    {
        MAP::ENTRY &entry = *iter;
        // QOBject destructor notifies qapplication (hopefully) on delete
        delete entry.int_id_;
        ++iter;
    }

    // iterate over QSocketNotifiers for write and release them
    this->write_notifier_.begin ();
    this->write_notifier_.end ();
    while( iter != iterEnd )
    {
        MAP::ENTRY &entry = *iter;
        // QOBject destructor notifies qapplication (hopefully) on delete
        delete entry.int_id_;
        ++iter;
    }

    // iterate over QSocketNotifiers for exceptions and release them
    this->exception_notifier_.begin ();
    this->exception_notifier_.end ();
    while( iter != iterEnd )
    {
        MAP::ENTRY &entry = *iter;
        // QOBject destructor notifies qapplication (hopefully) on delete
        delete entry.int_id_;
        ++iter;
    }

    // QOBject destructor notifies qapplication (hopefully) on delete
    delete qtime_;
}

void
ACE_QtReactor::qapplication (QApplication *qapp)
{
  // reparent QSocketNotifiers and QTimer
  qapp_ = qapp ;
}

void
ACE_QtReactor::timeout_event (void)
{
  // Deal with any timer events
  ACE_Select_Reactor_Handle_Set handle_set;
  this->dispatch (0, handle_set );

  // Set next timeout signal
  this->reset_timeout ();
}

void
ACE_QtReactor::read_event (int p_handle)
{
  ACE_TRACE ("ACE_QtReactor::read_event");

  ACE_HANDLE handle = ACE_HANDLE( p_handle );

#ifdef ACE_QTREACTOR_CLEAR_PENDING_EVENTS
  // disable socket notifier to clear pending events
  QSocketNotifier *qsock_notifier = 0;
  if ( ( this->read_notifier_.find( handle,
             qsock_notifier) != -1) )
    qsock_notifier->setEnabled( false );
#endif /* ACE_QTREACTOR_CLEAR_PENDING_EVENTS  */

  // The core of read event handling
  ACE_Select_Reactor_Handle_Set dispatch_set;

  dispatch_set.rd_mask_.set_bit ( handle );
  this->dispatch (1, dispatch_set);

#ifdef ACE_QTREACTOR_CLEAR_PENDING_EVENTS
  // enable socket notifier according to current mask
  ACE_Reactor_Mask mask = 0;
  mask = mask_ops( handle, mask, ACE_Reactor::GET_MASK );
  if ( -1 != mask )
        set_enable_flag_by_mask ( 1, handle, mask);
#endif /* ACE_QTREACTOR_CLEAR_PENDING_EVENTS  */
}

void
ACE_QtReactor::write_event (int p_handle)
{
  ACE_TRACE ("ACE_QtReactor::write_event");

  ACE_HANDLE handle = ACE_HANDLE( p_handle );

#ifdef ACE_QTREACTOR_CLEAR_PENDING_EVENTS
  // disable socket notifier to clear pending events
  QSocketNotifier *qsock_notifier = 0;
  if ( ( this->write_notifier_.find( handle, qsock_notifier) != -1) )
    qsock_notifier->setEnabled( false );
#endif /* ACE_QTREACTOR_CLEAR_PENDING_EVENTS  */

  // The core of write event handling
  ACE_Select_Reactor_Handle_Set dispatch_set;

  dispatch_set.wr_mask_.set_bit( handle );
  this->dispatch (1, dispatch_set);

#ifdef ACE_QTREACTOR_CLEAR_PENDING_EVENTS
  // enable socket notifier according to current mask
  ACE_Reactor_Mask mask = 0;
  mask = mask_ops( handle, mask, ACE_Reactor::GET_MASK );
  if ( -1 != mask )
        set_enable_flag_by_mask ( 1, handle, mask);
#endif /* ACE_QTREACTOR_CLEAR_PENDING_EVENTS  */
}

void
ACE_QtReactor::exception_event (int p_handle)
{
  ACE_TRACE ("ACE_QtReactor::exception_event");

  ACE_HANDLE handle = ACE_HANDLE( p_handle );


#ifdef ACE_QTREACTOR_CLEAR_PENDING_EVENTS
  // disable socket notifier to clear pending events
  QSocketNotifier *qsock_notifier = 0;
  if ( ( this->exception_notifier_.find( handle, qsock_notifier) != -1) )
    qsock_notifier->setEnabled( false );
#endif /* ACE_QTREACTOR_CLEAR_PENDING_EVENTS  */

  // The core of exception event handling
  ACE_Select_Reactor_Handle_Set dispatch_set;

  dispatch_set.ex_mask_.set_bit( handle );
  dispatch (1, dispatch_set);

#ifdef ACE_QTREACTOR_CLEAR_PENDING_EVENTS
  // enable socket notifier according to current mask
  ACE_Reactor_Mask mask = 0;
  mask = mask_ops( handle, mask, ACE_Reactor::GET_MASK );
  if ( -1 != mask )
        set_enable_flag_by_mask ( 1, handle, mask);
#endif /* ACE_QTREACTOR_CLEAR_PENDING_EVENTS  */
}

int
ACE_QtReactor::set_enable_flag_by_mask (int flag_value,
                                        ACE_HANDLE handle,
                                        ACE_Reactor_Mask mask)
{
  QSocketNotifier *qs_not;

  if (ACE_BIT_ENABLED(mask, ACE_Event_Handler::READ_MASK) ||
      ACE_BIT_ENABLED( mask, ACE_Event_Handler::ACCEPT_MASK))
    {
      // Find the current notifier
      qs_not = 0;
      if ((this->read_notifier_.find (handle, qs_not) == -1))
        return -1;

      qs_not->setEnabled (flag_value);
    }

  if (ACE_BIT_ENABLED( mask, ACE_Event_Handler::WRITE_MASK) ||
      ACE_BIT_ENABLED( mask, ACE_Event_Handler::ACCEPT_MASK) ||
      ACE_BIT_ENABLED( mask, ACE_Event_Handler::CONNECT_MASK))
    {
      qs_not = 0;
      if ((this->write_notifier_.find (handle, qs_not) == -1))
        return -1;

      qs_not->setEnabled (flag_value);
    }

  if (ACE_BIT_ENABLED( mask,
                       ACE_Event_Handler::EXCEPT_MASK))
    {
      qs_not = 0;
      if ((this->exception_notifier_.find (handle, qs_not) == -1))
        return -1;

      qs_not->setEnabled (flag_value);
    }

  return 0;
}

int
ACE_QtReactor::bit_ops (ACE_HANDLE handle,
                        ACE_Reactor_Mask mask,
                        ACE_Select_Reactor_Handle_Set &handle_set,
                        int ops)
{
  int result;
  ACE_Select_Reactor_Handle_Set preserved_handle_set = handle_set;

  // Call regular bit_ops
  if ((result = ACE_Select_Reactor::bit_ops (handle, mask, handle_set, ops)) == -1)
    return -1;

  // disable or enable the notifiers based on handle_set and mask
  int enableFlag = -1;
  if (&handle_set == &this->suspend_set_)
    enableFlag = 0;
  else if (&handle_set == &this->wait_set_)
    enableFlag = 1;
  else
    // We have no work to do here, so just return
    return result;

  switch (ops)
    {
    case ACE_Reactor::SET_MASK:
    case ACE_Reactor::ADD_MASK:
      // Enable or disable notifiers based on the specified masks
      if (this->set_enable_flag_by_mask (enableFlag, handle, mask) == -1)
        {
          // We can't just return -1 here because we'll have half-changed things.
          // So, we need to restore the old handle_set, then return -1.
          handle_set = preserved_handle_set;
          return -1;
        }
      break;

    case ACE_Reactor::CLR_MASK:
      if (this->set_enable_flag_by_mask (!enableFlag, handle, mask) == -1)
        {
          handle_set = preserved_handle_set;
          return -1;
        }
      break;

    default:
      // we take no action for any other operations
      break;
    }

  return result;
}

void
ACE_QtReactor::create_notifiers_for_handle (ACE_HANDLE handle)
{
    QSocketNotifier *qsock_notifier = 0;

    // if there is already a read socket notifier for this handle, do nothing
    // otherwise create read notifier
    if ( ( this->read_notifier_.find (handle,
               qsock_notifier) == -1) )
    {
        ACE_NEW (qsock_notifier,
            QSocketNotifier (int(handle), QSocketNotifier::Read, this));
        this->read_notifier_.bind (handle,
            qsock_notifier);
        QObject::connect (qsock_notifier,
            SIGNAL (activated (int)),
            this,
            SLOT (read_event (int))) ;
        // disable; it will be enabled by the regular register_handler_i if
        // necessary
        qsock_notifier->setEnabled (0);
    }

    qsock_notifier = 0;


    // if there is already a write socket notifier for this handle, do nothing
    // otherwise create read notifier
    if ((this->write_notifier_.find (handle,
             qsock_notifier) == -1))
    {
        ACE_NEW (qsock_notifier,
            QSocketNotifier (int(handle), QSocketNotifier::Write, this));

        this->write_notifier_.bind (handle,
            qsock_notifier);

        QObject::connect (qsock_notifier,
            SIGNAL (activated (int)),
            this,
            SLOT (write_event (int)));
        // disable; it will be enabled by the regular register_handler_i if
        // necessary
        qsock_notifier->setEnabled (0);
    }


    qsock_notifier = 0;

    // if there is already a write socket notifier for this handle, do nothing
    // otherwise create read notifier
    if ((this->exception_notifier_.find (handle,
             qsock_notifier) == -1))
    {

        ACE_NEW (qsock_notifier,
            QSocketNotifier (int(handle), QSocketNotifier::Exception, this));

        this->exception_notifier_.bind (handle,
            qsock_notifier);

        QObject::connect (qsock_notifier,
            SIGNAL (activated (int)),
            this,
            SLOT (exception_event (int))) ;
        // disable; it will be enabled by the regular register_handler_i if
        // necessary
        qsock_notifier->setEnabled (0);
    }
}

void
ACE_QtReactor::destroy_notifiers_for_handle (ACE_HANDLE handle)
{
  QSocketNotifier *qsock_notifier = 0;

  // Looks for the handle in the maps and removes them.

  if ((this->read_notifier_.find (handle,
                                  qsock_notifier) != -1))
    {
      this->read_notifier_.unbind (handle,
                                   qsock_notifier);
      delete qsock_notifier;
    }

  if ((this->write_notifier_.find (handle,
                                   qsock_notifier) != -1))
    {
      this->write_notifier_.unbind (handle,
                                    qsock_notifier);
      delete qsock_notifier;
    }


  if ((this->exception_notifier_.find (handle,
                                       qsock_notifier) != -1))
    {
      this->exception_notifier_.unbind (handle,
                                        qsock_notifier);
      delete qsock_notifier;
    }
}

int
ACE_QtReactor::register_handler_i (ACE_HANDLE handle ,
                                   ACE_Event_Handler *handler,
                                   ACE_Reactor_Mask mask)
{
  ACE_TRACE ("ACE_QtReactor::register_handler_i");

  this->create_notifiers_for_handle (handle);

  int result;
  if ((result = ACE_Select_Reactor::register_handler_i(handle,
                                                       handler,
                                                       mask ))
      == -1)
    {
        // destroy notifiers only when there is no handler for handle
        if ( !ACE_Select_Reactor::find_handler( handle ) )
            this->destroy_notifiers_for_handle (handle);
      return -1;
    }

  return 0;
}

int
ACE_QtReactor::register_handler_i (const ACE_Handle_Set &handles,
                                   ACE_Event_Handler *handler,
                                   ACE_Reactor_Mask mask)
{
  return ACE_Select_Reactor::register_handler_i(handles,
                                                handler,
                                                mask);
}

int ACE_QtReactor::remove_handler_i (ACE_HANDLE handle ,
                                     ACE_Reactor_Mask mask   )
{
  ACE_TRACE ("ACE_QtReactor::remove_handler_i");

  int result = ACE_Select_Reactor::remove_handler_i (handle, mask);
  // destroy notifiers only when there is no handler for handle
  if ( !ACE_Select_Reactor::find_handler( handle ) )
      this->destroy_notifiers_for_handle (handle);
  return result;
}


int
ACE_QtReactor::remove_handler_i (const ACE_Handle_Set &handles,
                                 ACE_Reactor_Mask  mask)
{
  return ACE_Select_Reactor::remove_handler_i (handles,
                                               mask);
}

// The following functions ensure that there is an Qt timeout for the
// first timeout in the Reactor's Timer_Queue.

void
ACE_QtReactor::reset_timeout (void)
{
  if (this->qtime_ != 0)
    {
      delete this->qtime_;
      this->qtime_ = 0;
    }

  ACE_Time_Value *max_wait_time =
    this->timer_queue_->calculate_timeout (0) ;

  if (max_wait_time)
  {
    ACE_NEW (this->qtime_,
             QTimer);

    QObject::connect (qtime_,
                      SIGNAL (timeout ()),
                      this,
                      SLOT (timeout_event ()));

#if QT_VERSION >= 0x040000
    qtime_->setSingleShot (1);
    qtime_->start(max_wait_time->msec());
#else
    qtime_->start(max_wait_time->msec(), 1);
#endif
  }

}


long
ACE_QtReactor::schedule_timer (ACE_Event_Handler *handler,
                               const void *arg,
                               const ACE_Time_Value &delay_time,
                               const ACE_Time_Value &interval)
{
  ACE_TRACE ("ACE_QtReactor::schedule_timer");
  ACE_MT (ACE_GUARD_RETURN (ACE_Select_Reactor_Token,
                            ace_mon,
                            this->token_,
                            -1));

  long result;
  if ((result = ACE_Select_Reactor::schedule_timer(handler,
                                                   arg,
                                                   delay_time,
                                                   interval)) == -1 )
    return -1;
  else
  {
    this->reset_timeout ();
    return result;
  }
}

int
ACE_QtReactor::cancel_timer (ACE_Event_Handler *handler,
                             int dont_call_handle_close)
{
  ACE_TRACE ("ACE_QtReactor::cancel_timer");

  if (ACE_Select_Reactor::cancel_timer (handler,
                                        dont_call_handle_close ) == -1 )
    return -1 ;
  else
  {
    this->reset_timeout( ) ;
    return 0 ;
  }
}

int ACE_QtReactor::cancel_timer (long  timer_id,
                                 const void **arg,
                                 int dont_call_handle_close )
{
  ACE_TRACE ("ACE_QtReactor::cancel_timer") ;

  if (ACE_Select_Reactor::cancel_timer (timer_id,
                                        arg,
                                        dont_call_handle_close ) == -1 )
    return -1 ;
  else
  {
    this->reset_timeout( ) ;
    return 0 ;
  }
}

// mbrudka: who needs QtWaitForMultipleEvents? It seems it's cargo load now!
int
ACE_QtReactor::QtWaitForMultipleEvents (int width,
                                        ACE_Select_Reactor_Handle_Set &wait_set,
                                        ACE_Time_Value * /*max_wait_time*/)
{
  // Keep a copy of the wait set in case the wait_set be changed
  // between the two select calls in this function. It could happen
  // while waiting for an event, another event is handled and dispatched
  // which changes the dispatch_set_/wait_set.
  ACE_Select_Reactor_Handle_Set orig_wait_set = wait_set;

  // Check to make sure our handle's are all usable.
  ACE_Select_Reactor_Handle_Set temp_set = wait_set;

  if (ACE_OS::select (width,
                      temp_set.rd_mask_,
                      temp_set.wr_mask_,
                      temp_set.ex_mask_,
                      (ACE_Time_Value *) &ACE_Time_Value::zero ) == -1)
    return -1; // Bad file arguments...

  // Qt processing.
#if QT_VERSION >= 0x040000
  this->qapp_->processEvents();
#else
  this->qapp_->processOneEvent ();
#endif

  // Reset the width, in case it changed during the upcalls.
  width = handler_rep_.max_handlep1 ();

  // Now actually read the result needed by the <Select_Reactor> using
  // <select>.
  return ACE_OS::select(width,
                        orig_wait_set.rd_mask_,
                        orig_wait_set.wr_mask_,
                        orig_wait_set.ex_mask_,
                        (ACE_Time_Value *) &ACE_Time_Value::zero);
}

// mbrudka: who needs wait_for_multiple_events? It seems it's cargo load now!
int
ACE_QtReactor::wait_for_multiple_events (
  ACE_Select_Reactor_Handle_Set &handle_set,
  ACE_Time_Value *max_wait_time)
{
  ACE_TRACE ("ACE_QtReactor::wait_for_multiple_events");

  int nfound = 0;
  do
  {
    max_wait_time = this->timer_queue_->calculate_timeout (max_wait_time);
    size_t width = this->handler_rep_.max_handlep1 ();
    handle_set.rd_mask_ = this->wait_set_.rd_mask_;
    handle_set.wr_mask_ = this->wait_set_.wr_mask_;
    handle_set.ex_mask_ = this->wait_set_.ex_mask_;

    nfound = QtWaitForMultipleEvents (width,
                                      handle_set,
                                      max_wait_time);

  } while( nfound == -1 && this->handle_error () > 0 );

  if (nfound > 0)
  {
#if !defined (ACE_WIN32)
    handle_set.rd_mask_.sync (this->handler_rep_.max_handlep1 ());
    handle_set.wr_mask_.sync (this->handler_rep_.max_handlep1 ());
    handle_set.ex_mask_.sync (this->handler_rep_.max_handlep1 ());
#endif /* ACE_WIN32 */
  }

  return nfound;
  // Timed out or input available
}

ACE_END_VERSIONED_NAMESPACE_DECL
