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

#ifndef _HW4_EVENTLOOP_H_
#define _HW4_EVENTLOOP_H_

#include <poll.h>
#include <list>
#include <map>

#include "./EventLoopHandler.h"

namespace hw4 {

// An EventLoop object captures a thread of execution and uses the
// Linux poll system call ("man 3 poll") to receive and dispatch
// events on registered file descriptors to EventLoopHandler objects.
class EventLoop {
 public:
  // The following bitmasks define the kinds of file descriptor events
  // that an EventLoop can deliver.  You can register interest in any
  // combination of these events using RegisterFD, and when a covered
  // event fires, the EventLoop will invoke your HandleEvent() method
  // indicating what event or events have fired.  For example, to
  // register interest in FD_IS_READABLE, FD_ERR, and FD_HUP, you
  // would pass in an integer like:
  //
  //   unsigned int events = EventLoop::FD_IS_READABLE |
  //                         EventLoop::FD_ERR |
  //                         EventLoop::FD_HUP;
  enum eventtypes {
    FD_IS_READABLE  = (1 << 0),  // fd is ready to read
    FD_IS_WRITEABLE = (1 << 1),  // fd is ready to write
    FD_ERR          = (1 << 2),  // an error happened on fd
    FD_HUP          = (1 << 3)   // the remote party disconnected
  };

  // Constructors / destructor.
  EventLoop();
  ~EventLoop();

  // Register interest in receiving the events indicated in the bitfield
  // "events" on file descriptor "fd".  Whenever one of the indicated
  // events fires, EventLoop will invoke eh.handle, passing in fd and a
  // unsigned int bitfield showing which events happened.  If the
  // handler returns false, the event loop will invoke "delete" on the
  // handler.
  //
  // Returns true on success, false on error.  The only error case is
  // if "fd" is not a valid file descriptor.
  bool RegisterFD(const int fd,
                  const unsigned int events,
                  EventLoopHandler *eh);

  // Unregister interest in receiving any events on "fd."  Returns
  // true on success, false on error.  The only error case is if fd is
  // not currently registered.  Does not call "delete" on the handler.
  bool UnregisterFD(const int fd);

  // Begin processing events.  This method captures the caller's thread
  // to power the event loop.  Run() returns if there are ever no more
  // registered fds.
  void Run();

  // Event handlers can invoke this function to tell the event loop
  // to return out of its loop.
  void Quit();

 private:
  // This private helper function updates the fds_ structure
  // if new registrations have arrived.
  void UpdateFDS(void);

  // We use this structure to store an fd registration.
  typedef struct {
    int fd;                // the fd that was registered
    unsigned int events;   // the events of interest
    EventLoopHandler *eh;  // the event handler object to call
  } Registration;

  // This is the struct pollfd array we pass to the poll
  // system call.  As file descriptors are added to it,
  // we may need to dynamically resize it.
  struct pollfd *fds_;

  // This is the size of the pollfd array; this represents the total
  // allocated capacity of the array, not the number of fds currently
  // stored in it.  As the capacity is exhausted, we resize it.
  unsigned int fds_size_;

  // This map stores the set of file descriptors and associated
  // EventLoop::Registration objects that have been registered.
  std::map<int, Registration> fd_registrations_;

  // This list stores the set of event handlers that have been
  // unregistered during the last iteration through.
  std::list<EventLoopHandler *> fd_deregistrations_;

  // This flag tracks whether or not the fd_registrations_ map has
  // been updated.
  bool registrations_updated_;

  // This flag tracks whether or not somebody has called Quit().
  bool quit_;
};

}  // namespace hw4

#endif  // _HW4_EVENTLOOP_H_