package nanomsg;

import com.sun.jna.Memory;
import java.util.HashMap;
import java.util.Map;

import nanomsg.NativeLibrary.NNPollEvent;
import nanomsg.exceptions.IOException;
import nanomsg.Socket;
import static nanomsg.Nanomsg.*;


public class Poller {
  public static final int POLLIN = nn_symbols.get("NN_POLLIN");
  public static final int POLLOUT = nn_symbols.get("NN_POLLOUT");

  private static final int SIZE_DEFAULT = 32;
  private static final int SIZE_INCREMENT = 16;
  private static final int EVENT_SIZE = (new NNPollEvent()).size();

  private final Map<Integer,Integer> offsetMap = new HashMap<Integer,Integer>(SIZE_DEFAULT);
  private Memory items;
  private int next;
  private int timeout;

  protected Poller(int size, int timeout) {
    this.items = new Memory(size * EVENT_SIZE);
    this.timeout = timeout;
    this.next = 0;
  }

  public Poller(int size) {
    this(size, -1);
  }

  public Poller() {
    this(SIZE_DEFAULT);
  }

  public void register(Socket socket) {
    this.register(socket, POLLIN | POLLOUT);
  }

  public void register(Socket socket, int flags) {
    int pos = this.next;
    this.next += EVENT_SIZE;

    if (pos > this.items.size()) {
      final long newSize = this.items.size() + (EVENT_SIZE * SIZE_INCREMENT);
      final Memory newItems = new Memory(newSize);

      for(int i=0; i<this.items.size(); i++) {
        newItems.setByte(i, this.items.getByte(i));
      }
    }

    final int socketFd = socket.getNativeSocket();
    final NNPollEvent.ByReference event = new NNPollEvent.ByReference();
    event.reuse(this.items, pos);
    event.fd = socketFd;
    event.events = (short)flags;

    this.offsetMap.put(socketFd, pos);
  }

  public void unregister(Socket socket) {
    final NNPollEvent.ByReference event = new NNPollEvent.ByReference();
    final int fd = socket.getNativeSocket();

    this.offsetMap.remove(fd);

    for (int i=0; i<this.items.size(); i = i+EVENT_SIZE) {
      event.reuse(this.items, i);

      if (event.fd == fd) {
        this.next -= EVENT_SIZE;

        if (i != this.next) {
          event.reuse(this.items, this.next);
          final int socketFd = event.fd;
          final short socketEvents = event.events;
          final short socketRevents = event.revents;

          event.reuse(this.items, i);
          event.fd = socketFd;
          event.events = socketEvents;
          event.revents = socketRevents;

          this.offsetMap.put(socketFd, i);
        }
        break;
      }
    }
  }

  public int poll() {
    return this.poll(this.timeout);
  }

  public int size() {
    return this.offsetMap.size();
  }

  public int poll(int timeout) {
    final int maxEvents = this.offsetMap.size();
    final int rc = NativeLibrary.nn_poll(this.items, maxEvents, timeout);

    if (rc < 0) {
      final int errno = getErrorNumber();
      final String msg = getError();
      throw new IOException(msg, errno);
    }

    return rc;
  }
}

@@ -375,24 +375,102 @@
 POLLOUT%22);%0A%0A
+  // TODO: rename constants%0A  public static final int TIMEOUT_DEFAULT = 6000;%0A
   private st
@@ -503,16 +503,16 @@
 T = 32;%0A
-
   privat
@@ -620,16 +620,100 @@
 ize();%0A%0A
+  private final NNPollEvent.ByReference base_event = new NNPollEvent.ByReference();%0A
   privat
@@ -872,16 +872,13 @@
 %0A  p
-rotected
+ublic
  Pol
@@ -1053,10 +1053,23 @@
 ze, 
--1
+TIMEOUT_DEFAULT
 );%0A 
@@ -1811,16 +1811,35 @@
 t)flags;
+%0A    event.write();
 %0A%0A    th
@@ -2591,24 +2591,49 @@
 cketRevents;
+%0A          event.write();
 %0A%0A          
@@ -2790,24 +2790,24 @@
 nt size() %7B%0A
-
     return t
@@ -2825,32 +2825,752 @@
 ap.size();%0A  %7D%0A%0A
+  public boolean isReadable(final Socket socket) %7B%0A    final Integer fd = socket.getNativeSocket();%0A    if (!this.offsetMap.containsKey(fd)) %7B%0A      return false;%0A    %7D%0A%0A    final int offset = this.offsetMap.get(fd);%0A    this.base_event.reuse(this.items, offset);%0A    final int revents = this.base_event.revents;%0A    return (revents & POLLIN) == POLLIN;%0A  %7D%0A%0A  public boolean isWritable(final Socket socket) %7B%0A    final Integer fd = socket.getNativeSocket();%0A    if (!this.offsetMap.containsKey(fd)) %7B%0A      return false;%0A    %7D%0A%0A    final int offset = this.offsetMap.get(fd);%0A    this.base_event.reuse(this.items, offset);%0A    final int revents = this.base_event.revents;%0A    return (revents & POLLOUT) == POLLOUT;%0A  %7D%0A%0A
   public int pol

Fix bugs and add missing methods on poller.