/* Curb - helper macros for ruby integration
 * Copyright (c)2006 Ross Bamford. 
 * Licensed under the Ruby License. See LICENSE for details.
 * 
 * $Id: curb_macros.h 13 2006-11-23 23:54:25Z roscopeco $
 */

#ifndef __CURB_MACROS_H
#define __CURB_MACROS_H

#define rb_easy_sym(sym) ID2SYM(rb_intern(sym))
#define rb_easy_hkey(key) ID2SYM(rb_intern(key))
#define rb_easy_set(key,val) rb_hash_aset(rbce->opts, rb_easy_hkey(key) , val)
#define rb_easy_get(key) rb_hash_aref(rbce->opts, rb_easy_hkey(key))
#define rb_easy_del(key) rb_hash_delete(rbce->opts, rb_easy_hkey(key))
#define rb_easy_nil(key) (rb_hash_aref(rbce->opts, rb_easy_hkey(key)) == Qnil)
#define rb_easy_type_check(key,type) (rb_type(rb_hash_aref(rbce->opts, rb_easy_hkey(key))) == type)

// TODO: rb_sym_to_s may not be defined?
#define rb_easy_get_str(key) \
  RSTRING_PTR((rb_easy_type_check(key,T_STRING) ? rb_easy_get(key) : rb_str_to_str(rb_easy_get(key))))

/* getter/setter macros for various things */
/* setter for anything that stores a ruby VALUE in the struct */
#define CURB_OBJECT_SETTER(type, attr)                              \
            type *ptr;                                              \
                                                                    \
            Data_Get_Struct(self, type, ptr);                       \
            ptr->attr = attr;                                       \
                                                                    \
            return attr;

/* getter for anything that stores a ruby VALUE */
#define CURB_OBJECT_GETTER(type, attr)                              \
            type *ptr;                                              \
                                                                    \
            Data_Get_Struct(self, type, ptr);                       \
            return ptr->attr; 

/* setter for anything that stores a ruby VALUE in the struct opts hash */
#define CURB_OBJECT_HSETTER(type, attr)                             \
            type *ptr;                                              \
                                                                    \
            Data_Get_Struct(self, type, ptr);                       \
            rb_hash_aset(ptr->opts, rb_easy_hkey(#attr), attr);       \
                                                                    \
            return attr;

/* getter for anything that stores a ruby VALUE in the struct opts hash */
#define CURB_OBJECT_HGETTER(type, attr)                              \
            type *ptr;                                              \
                                                                    \
            Data_Get_Struct(self, type, ptr);                       \
            return rb_hash_aref(ptr->opts, rb_easy_hkey(#attr)); 

/* setter for bool flags */
#define CURB_BOOLEAN_SETTER(type, attr)                             \
            type *ptr;                                              \
            Data_Get_Struct(self, type, ptr);                       \
                                                                    \
            if (attr == Qnil || attr == Qfalse) {                   \
              ptr->attr = 0;                                        \
            } else {                                                \
              ptr->attr = 1;                                        \
            }                                                       \
                                                                    \
            return attr;

/* getter for bool flags */
#define CURB_BOOLEAN_GETTER(type, attr)                             \
            type *ptr;                                              \
            Data_Get_Struct(self, type, ptr);                       \
                                                                    \
            return((ptr->attr) ? Qtrue : Qfalse);

/* special setter for on_event handlers that take a block */
#define CURB_HANDLER_PROC_SETTER(type, handler)                     \
            type *ptr;                                              \
            VALUE oldproc;                                          \
                                                                    \
            Data_Get_Struct(self, type, ptr);                       \
                                                                    \
            oldproc = ptr->handler;                                 \
            rb_scan_args(argc, argv, "0&", &ptr->handler);          \
                                                                    \
            return oldproc;                                         \

/* special setter for on_event handlers that take a block, same as above but stores int he opts hash */
#define CURB_HANDLER_PROC_HSETTER(type, handler)                        \
            type *ptr;                                                  \
            VALUE oldproc, newproc;                                     \
                                                                        \
            Data_Get_Struct(self, type, ptr);                           \
                                                                        \
            oldproc = rb_hash_aref(ptr->opts, rb_easy_hkey(#handler));   \
            rb_scan_args(argc, argv, "0&", &newproc);                   \
                                                                        \
            rb_hash_aset(ptr->opts, rb_easy_hkey(#handler), newproc);    \
                                                                        \
            return oldproc;

/* setter for numerics that are kept in c longs */
#define CURB_IMMED_SETTER(type, attr, nilval)                       \
            type *ptr;                                              \
                                                                    \
            Data_Get_Struct(self, type, ptr);                       \
            if (attr == Qnil) {                                     \
              ptr->attr = nilval;                                   \
            } else {                                                \
              ptr->attr = NUM2LONG(attr);                           \
            }                                                       \
                                                                    \
            return attr;                                            \

/* setter for numerics that are kept in c longs */
#define CURB_IMMED_GETTER(type, attr, nilval)                       \
            type *ptr;                                              \
                                                                    \
            Data_Get_Struct(self, type, ptr);                       \
            if (ptr->attr == nilval) {                              \
              return Qnil;                                          \
            } else {                                                \
              return LONG2NUM(ptr->attr);                           \
            }

/* special setter for port / port ranges */
#define CURB_IMMED_PORT_SETTER(type, attr, msg)                     \
            type *ptr;                                              \
                                                                    \
            Data_Get_Struct(self, type, ptr);                       \
            if (attr == Qnil) {                                     \
              ptr->attr = 0;                                        \
            } else {                                                \
              int port = NUM2INT(attr);                             \
                                                                    \
              if ((port) && ((port & 0xFFFF) == port)) {            \
                ptr->attr = port;                                   \
              } else {                                              \
                rb_raise(rb_eArgError, "Invalid " msg " %d (expected between 1 and 65535)", port); \
              }                                                     \
            }                                                       \
                                                                    \
            return attr;                                            \

/* special getter for port / port ranges */
#define CURB_IMMED_PORT_GETTER(type, attr)                          \
            type *ptr;                                              \
                                                                    \
            Data_Get_Struct(self, type, ptr);                       \
            if (ptr->attr == 0) {                                   \
              return Qnil;                                          \
            } else {                                                \
              return INT2NUM(ptr->attr);                            \
            }

#define CURB_DEFINE(name) \
  rb_define_const(mCurl, #name, LONG2NUM(name))

/* copy and raise exception */
#define CURB_CHECK_RB_CALLBACK_RAISE(did_raise) \
  VALUE exception = rb_hash_aref(did_raise, rb_easy_hkey("error")); \
  if (FIX2INT(rb_hash_size(did_raise)) > 0 && exception != Qnil) { \
    rb_hash_clear(did_raise); \
    VALUE message   = rb_funcall(exception, rb_intern("message"), 0); \
    VALUE aborted_exception = rb_exc_new_str(eCurlErrAbortedByCallback, message); \
    VALUE backtrace = rb_funcall(exception, rb_intern("backtrace"), 0); \
    rb_funcall(aborted_exception,  rb_intern("set_backtrace"), 1, backtrace); \
    rb_exc_raise(aborted_exception); \
  }

#endif
