/*
 * Copyright 2011 Steven Gribble
 *
 *  This file is part of the UW CSE 333 course project sequence
 *  (333proj).
 *
 *  333proj 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  333proj 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 333proj.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <assert.h>
#include <signal.h>

#include "./EventLoop.h"

namespace hw4 {

#define INITIAL_NUM_FDS 1024

EventLoop::EventLoop() {
  fds_ = new struct pollfd[INITIAL_NUM_FDS];
  fds_size_ = INITIAL_NUM_FDS;
  registrations_updated_ = false;
  quit_ = false;

  // Set things up to ignore SIGPIPE.  Without this, our process would
  // receive a SIGPIPE signal when a client closes its end of a TCP
  // socket and then a handler is tries to write to it.
  struct sigaction newaction;
  newaction.sa_handler = SIG_IGN;
  sigemptyset(&newaction.sa_mask);
  newaction.sa_flags = 0;
  sigaction(SIGPIPE, &newaction, NULL);
}

EventLoop::~EventLoop() {
  delete[] fds_;
  fds_size_ = 0;
}

bool EventLoop::RegisterFD(const int fd,
                           const unsigned int events,
                           EventLoopHandler *eh) {
  // Set up the Registration structure.
  Registration r = {fd, events, eh};

  // Insert it into the map.
  fd_registrations_[fd] = r;

  // Update the "registrations_updated_" flags.
  registrations_updated_ = true;

  return true;
}

bool EventLoop::UnregisterFD(const int fd) {
  // Remove the registration from the map.
  if (fd_registrations_.find(fd) == fd_registrations_.end())
    return false;

  Registration reg = fd_registrations_[fd];
  if (fd_registrations_.erase(fd) == 0) {
    assert(0);
  }

  // Store it in the list of deregistrations to be processed at
  // the end of the event loop iteration.
  fd_deregistrations_.push_back(reg.eh);

  // Set the "something changed" flag.
  registrations_updated_ = true;
  return true;
}

void EventLoop::Quit() {
  // By setting quit_ to true, the event loop will exit on the next
  // iteration start.  This means all handlers in the current iteration
  // will complete, and then all handlers will be deregistered and
  // deleted.
  quit_ = true;
}

void EventLoop::Run() {
  // Make sure the fds_ array is initialized prepped.
  UpdateFDS();

  // This is the main event loop.
  while ((!quit_) && (fd_registrations_.size() > 0)) {
    // Prepare to invoke the poll() system call. ("man 3 poll")
    int num_fds = fd_registrations_.size();

    // Do the poll.  We rely on UpdateFDS() to keep the fds_ array
    // in good shape.
    int num_selected = poll(fds_, fd_registrations_.size(), -1);
    if (num_selected > 0) {
      // Polling succeeded.  Loop through the fds_ array and service
      // the selected fds by invoking their handler.
      for (int i = 0; i < num_fds; i++) {
        if (fds_[i].revents) {
          // Service this fd.  Lookup the handler registration in the
          // fd_registrations_ map.
          Registration x = fd_registrations_[fds_[i].fd];
          unsigned int eventfield = 0;

          // Set up the eventfield bitfield to pass to the handler.
          if ((x.events & FD_IS_READABLE) && (fds_[i].revents & POLLIN))
            eventfield |= FD_IS_READABLE;
          if ((x.events & FD_IS_WRITEABLE) && (fds_[i].revents & POLLOUT))
            eventfield |= FD_IS_WRITEABLE;
          if ((x.events & FD_ERR) && (fds_[i].revents & POLLERR))
            eventfield |= FD_ERR;
          if ((x.events & FD_HUP) && (fds_[i].revents & POLLHUP))
            eventfield |= FD_HUP;

          // Invoke the registered EventLoopHandler, passing it the
          // event bitfield.
          x.eh->Handle(x.fd, eventfield, this);
        }
        // Clear the fds_[i].revents field, now that we have
        // dispatched the event.  This ensures that on the next poll,
        // we'll only dispatch newly set event conditions.
        fds_[i].revents = 0;
      }
    }

    // During the servicing of fds, the invoked EventLoopHandlers may
    // have called RegisterFD or UnregisterFD.  If so, the
    // registrations_updated_ flag is set to true, meaning we need
    // to update the fds_ array and delete the unregistered handlers.
    if (registrations_updated_) {
      // Update the fds_array.
      UpdateFDS();

      // Process the deregistrations; delete the handlers.
      while (fd_deregistrations_.size() > 0) {
        EventLoopHandler *eh = fd_deregistrations_.front();
        fd_deregistrations_.pop_front();
        delete eh;
      }
    }
  }

  // We're about to quit out of the event loop.  Loop through the
  // remaining handlers, invoke Quitting() on them, then deleting
  // them.  Make sure we never double-delete a handler.
  while (fd_registrations_.size() > 0) {
    Registration x = (*(fd_registrations_.begin())).second;
    fd_registrations_.erase(fd_registrations_.begin());
    x.eh->Quitting(x.fd);
    std::map<int, Registration>::iterator it;
    bool done = false;

    // Loop over the registrations, looking for another registered
    // version of this handler.  If we find it, remove it from the
    // registrations array to avoid double-deleting it.
    while (!done) {
      done = true;
      for (it = fd_registrations_.begin();
           it != fd_registrations_.end();
           it++) {
        if ((*it).second.eh == x.eh) {
          // We found one, remove it from the registrations table and
          // so set "done" to false to loop back and look for more.
          done = false;
          fd_registrations_.erase(it);
          break;
        }
      }
    }

    // OK, it's safe to delete this guy now.
    delete x.eh;
  }
}

void EventLoop::UpdateFDS(void) {
  // This is invoked by an event handler to indicate it that it is
  // either (a) a new handler that wants to start receiving events,
  // or (b) an existing handler that is changing the set of events
  // that it is interested in.

  // First, make sure that the fds_ array is big enough to store all
  // of the handler fds_ that are registered.
  if (fd_registrations_.size() > fds_size_) {
    // We need to grow the array; we'll grow by INITIAL_NUM_FDS.
    fds_size_ += INITIAL_NUM_FDS;
    delete[] fds_;
    fds_ = new struct pollfd[fds_size_];
  }

  // Loop through the registrations map, updating the fds_ array.
  int count = 0;
  std::map<int, Registration>::iterator it;
  for (it = fd_registrations_.begin();
       it != fd_registrations_.end();
       it++) {
    // Set up the fds_[count] structure element associated with this
    // registration.
    unsigned int eventfield = ((*it).second).events;
    fds_[count].fd = (*it).first;
    fds_[count].events = fds_[count].revents = 0;

    // Set the events flags that the customer is interested in.
    if (eventfield & FD_IS_READABLE)
      fds_[count].events |= POLLIN;
    if (eventfield & FD_IS_WRITEABLE)
      fds_[count].events |= POLLOUT;
    if (eventfield & FD_ERR)
      fds_[count].events |= POLLERR;
    if (eventfield & FD_HUP)
      fds_[count].events |= POLLHUP;
    count++;
  }

  // Clear the registrations_updated_ flag.
  registrations_updated_ = false;
}

}  // namespace hw4