/*
 * Copyright (C) 2007 Tony Arcieri
 * You may redistribute this under the terms of the Ruby license.
 * See LICENSE for details
 */

#include <assert.h>
#include "ruby.h"

#define EV_STANDALONE 1
#include "../libev/ev.h"

#include "rev.h"

/* Module and object handles */
static VALUE mRev = Qnil;
static VALUE cRev_Loop = Qnil;

/* Data allocators and deallocators */
static VALUE Rev_Loop_allocate(VALUE klass);
static void Rev_Loop_mark(struct Rev_Loop *loop);
static void Rev_Loop_free(struct Rev_Loop *loop);

/* Method implementations */
static VALUE Rev_Loop_default(VALUE klass);
static VALUE Rev_Loop_ev_loop_new(VALUE self, VALUE flags);
static VALUE Rev_Loop_run_once(VALUE self);
static VALUE Rev_Loop_run_once_blocking(void *ptr);
static VALUE Rev_Loop_run_nonblock(VALUE self);

static void Rev_Loop_dispatch_events(struct Rev_Loop *loop_data);

#define DEFAULT_EVENTBUF_SIZE 32

void Init_rev_loop()
{
  mRev = rb_define_module("Rev");
  cRev_Loop = rb_define_class_under(mRev, "Loop", rb_cObject);
  rb_define_alloc_func(cRev_Loop, Rev_Loop_allocate);
  rb_define_singleton_method(cRev_Loop, "default", Rev_Loop_default, 0);

  rb_define_private_method(cRev_Loop, "ev_loop_new", Rev_Loop_ev_loop_new, 1);
  rb_define_method(cRev_Loop, "run_once", Rev_Loop_run_once, 0);
  rb_define_method(cRev_Loop, "run_nonblock", Rev_Loop_run_nonblock, 0);

  rb_cv_set(cRev_Loop, "@@default_loop", Qnil);
}

static VALUE Rev_Loop_allocate(VALUE klass)
{
  struct Rev_Loop *loop = (struct Rev_Loop *)xmalloc(sizeof(struct Rev_Loop));

  loop->ev_loop = 0;
  loop->default_loop = 0;

  loop->events_received = 0;
  loop->eventbuf_size = DEFAULT_EVENTBUF_SIZE;
  loop->eventbuf = (struct Rev_Event *)xmalloc(sizeof(struct Rev_Event) * DEFAULT_EVENTBUF_SIZE);

  return Data_Wrap_Struct(klass, Rev_Loop_mark, Rev_Loop_free, loop);
}

static void Rev_Loop_mark(struct Rev_Loop *loop)
{
}

static void Rev_Loop_free(struct Rev_Loop *loop)
{
  if(!loop->ev_loop)
    return;

  if(loop->default_loop)
    ev_default_destroy();
  else
    ev_loop_destroy(loop->ev_loop);

  xfree(loop->eventbuf);
  xfree(loop);
}


/**
 *  call-seq:
 *    Rev::Loop.default -> Rev::Loop
 *
 * Retrieve a singleton instance of the default loop for the application
 */ 
static VALUE Rev_Loop_default(VALUE klass)
{
  struct Rev_Loop *loop_data;
  VALUE default_loop = rb_cv_get(klass, "@@default_loop");

  if(default_loop == Qnil) {
    default_loop = rb_obj_alloc(klass);
    Data_Get_Struct(default_loop, struct Rev_Loop, loop_data);

    loop_data->ev_loop = ev_default_loop(0);
    loop_data->default_loop = 1;

    rb_cv_set(klass, "@@default_loop", default_loop);
    rb_iv_set(default_loop, "@active_watchers", INT2NUM(0));
    rb_iv_set(default_loop, "@watchers", rb_ary_new());
  }

  return default_loop;
}

/* Wrapper for populating a Rev_Loop struct with a new event loop */
static VALUE Rev_Loop_ev_loop_new(VALUE self, VALUE flags)
{
  struct Rev_Loop *loop_data;
  Data_Get_Struct(self, struct Rev_Loop, loop_data);

  if(loop_data->ev_loop)
    rb_raise(rb_eRuntimeError, "loop already initialized");

  loop_data->ev_loop = ev_loop_new(NUM2INT(flags));
  loop_data->default_loop = 0;

  return Qnil;
}

/* libev callback for receiving events */
void Rev_Loop_process_event(VALUE watcher, int revents)
{
  struct Rev_Loop *loop_data;
  struct Rev_Watcher *watcher_data;

  /* The Global VM lock isn't held right now, but hopefully
   * we can still do this safely */
  Data_Get_Struct(watcher, struct Rev_Watcher, watcher_data);
  Data_Get_Struct(watcher_data->loop, struct Rev_Loop, loop_data);

  /*  Well, what better place to explain how this all works than
   *  where the most wonky and convoluted stuff is going on!
   *
   *  Our call path up to here looks a little something like:
   *
   *  -> release GVL -> event syscall -> libev callback
   *  (GVL = Global VM Lock)             ^^^ You are here
   *
   *  We released the GVL in the Rev_Loop_run_once() function
   *  so other Ruby threads can run while we make a blocking 
   *  system call (one of epoll, kqueue, port, poll, or select,
   *  depending on the platform).
   *
   *  More specifically, this is a libev callback abstraction
   *  called from a real libev callback in every watcher,
   *  hence this function not being static.  The real libev
   *  callbacks are event-specific and handled in a watcher.
   *
   *  For syscalls like epoll and kqueue, the kernel tells libev
   *  a pointer (to a structure with a pointer) to the watcher 
   *  object.  No data structure lookups are required at all
   *  (beyond structs), it's smooth O(1) sailing the entire way.  
   *  Then libev calls out to the watcher's callback, which
   *  calls this function.
   *
   *  Now, you may be curious: if the watcher already knew what
   *  event fired, why the hell is it telling the loop?  Why
   *  doesn't it just rb_funcall() the appropriate callback?
   *
   *  Well, the problem is the Global VM Lock isn't held right
   *  now, so we can't rb_funcall() anything.  In order to get
   *  it back we have to:
   *
   *  stash event and return -> acquire GVL -> dispatch to Ruby
   *
   *  Which is kinda ugly and confusing, but still gives us 
   *  an O(1) event loop whose heart is in the kernel itself. w00t!
   *
   *  So, stash the event in the loop's data struct.  When we return
   *  the ev_loop() call being made in the Rev_Loop_run_once_blocking()
   *  function below will also return, at which point the GVL is
   *  reacquired and we can call out to Ruby */

  /* Grow the event buffer if it's too small */
  if(loop_data->events_received >= loop_data->eventbuf_size) {
    loop_data->eventbuf_size *= 2;
    loop_data->eventbuf = (struct Rev_Event *)xrealloc(
        loop_data->eventbuf, 
        sizeof(struct Rev_Event) * loop_data->eventbuf_size
        );
  }

  loop_data->eventbuf[loop_data->events_received].watcher = watcher;
  loop_data->eventbuf[loop_data->events_received].revents = revents;

  loop_data->events_received++;
}

/**
 *  call-seq:
 *    Rev::Loop.run_once -> nil
 * 
 * Run the Rev::Loop once, blocking until events are received.
 */
static VALUE Rev_Loop_run_once(VALUE self)
{
  struct Rev_Loop *loop_data;
  Data_Get_Struct(self, struct Rev_Loop, loop_data);

  assert(loop_data->ev_loop && !loop_data->events_received);
  rb_thread_blocking_region(Rev_Loop_run_once_blocking, loop_data->ev_loop, RB_UBF_DFL, 0);
  Rev_Loop_dispatch_events(loop_data);
  loop_data->events_received = 0;

  return Qnil;
}

static VALUE Rev_Loop_run_once_blocking(void *ptr) 
{
  /* The libev loop has now escaped through the Global VM Lock unscathed! */
  struct ev_loop *loop = (struct ev_loop *)ptr;

  ev_loop(loop, EVLOOP_ONESHOT);
  return Qnil;
}

/**
 *  call-seq:
 *    Rev::Loop.run_once -> nil
 * 
 * Run the Rev::Loop once, but return immediately if there are no pending events.
 */
static VALUE Rev_Loop_run_nonblock(VALUE self)
{
  struct Rev_Loop *loop_data;
  Data_Get_Struct(self, struct Rev_Loop, loop_data);

  assert(loop_data->ev_loop && !loop_data->events_received);
  ev_loop(loop_data->ev_loop, EVLOOP_NONBLOCK);
  Rev_Loop_dispatch_events(loop_data);
  loop_data->events_received = 0;

  return Qnil;
}

static void Rev_Loop_dispatch_events(struct Rev_Loop *loop_data)
{
  int i;
  struct Rev_Watcher *watcher_data;

  for(i = 0; i < loop_data->events_received; i++) {
    /* A watcher with pending events may have been detached from the loop
     * during the dispatch process.  If so, the watcher clears the pending
     * events, so skip over them */
    if(loop_data->eventbuf[i].watcher == Qnil)
      continue;

    Data_Get_Struct(loop_data->eventbuf[i].watcher, struct Rev_Watcher, watcher_data);
    watcher_data->dispatch_callback(loop_data->eventbuf[i].watcher, loop_data->eventbuf[i].revents);
  }
}