
/* ruby support for custom scheme handlers      */
/* Author: Martin Povolny (xpovolny@fi.muni.cz) */

#include "libxml.h"
#include "ruby_xml_input_cbg.h"

static ic_scheme *first_scheme = 0;

int ic_match (char const *filename) {
    ic_scheme *scheme;

    //fprintf( stderr, "ic_match: %s\n", filename );

    scheme = first_scheme;
    while (0 != scheme) {
        if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST scheme->scheme_name, scheme->name_len)) {
            return 1;
        }
        scheme = scheme->next_scheme;
    }
    return 0;
}

void* ic_open (char const *filename) {
    ic_doc_context *ic_doc;
    ic_scheme *scheme;
    VALUE res;
    
    scheme = first_scheme;
    while (0 != scheme) {
        if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST scheme->scheme_name, scheme->name_len)) {
          ic_doc = (ic_doc_context*)malloc( sizeof(ic_doc_context) );

// MUFF         res = rb_funcall( 
//                  rb_funcall( rb_mKernel, 
//                              rb_intern("const_get"), 1, 
//                              rb_str_new2(scheme->class) ),
//                  rb_intern("document_query"), 1, rb_str_new2(filename) );
          res = rb_funcall( scheme->class,
                            rb_intern("document_query"), 
                            1, 
                            rb_str_new2(filename) );

          ic_doc->buffer = strdup( StringValuePtr(res) );

          ic_doc->bpos = ic_doc->buffer;
          ic_doc->remaining = strlen(ic_doc->buffer);
          return ic_doc;
        }
        scheme = scheme->next_scheme;
    }
    return 0;
}

int ic_read (void *context, char *buffer, int len) {
    ic_doc_context *ic_doc;
    int ret_len;
    ic_doc = (ic_doc_context*)context;

    if (len >= ic_doc->remaining) {
        ret_len =  ic_doc->remaining;
    } else {
        ret_len = len;
    }
    ic_doc->remaining -= ret_len;
    strncpy( buffer, ic_doc->bpos, ret_len );
    ic_doc->bpos += ret_len;

    return ret_len;
}

int ic_close (void *context) {
    free( ((ic_doc_context*)context)->buffer );
    free( context );
    return 1;
}

VALUE input_callbacks_register_input_callbacks() {
    xmlRegisterInputCallbacks( ic_match, ic_open, ic_read, ic_close );
    return(Qtrue);
}

VALUE
input_callbacks_add_scheme (VALUE self, VALUE scheme_name, VALUE class) {
  ic_scheme *scheme;

  Check_Type(scheme_name, T_STRING);
  //MUFF Check_Type(class, T_STRING);

  scheme              = (ic_scheme*)malloc(sizeof(ic_scheme));
  scheme->next_scheme = 0;
  scheme->scheme_name = strdup(StringValuePtr(scheme_name));  /* TODO alloc, dealloc */
  scheme->name_len    = strlen(scheme->scheme_name);
  //MUFF scheme->class       = strdup(StringValuePtr(class));        /* TODO alloc, dealloc */
  scheme->class       = class;        /* TODO alloc, dealloc */

  //fprintf( stderr, "registered: %s, %d, %s\n", scheme->scheme_name, scheme->name_len, scheme->class );
  
  if (0 == first_scheme)
    first_scheme = scheme;
  else {
    ic_scheme *pos;
    pos = first_scheme;
    while (0 != pos->next_scheme)
      pos = pos->next_scheme;
    pos->next_scheme = scheme;
  }
  
  return(Qtrue);
}

VALUE
input_callbacks_remove_scheme (VALUE self, VALUE scheme_name) {
    char *name;
    ic_scheme *save_scheme, *scheme;

    Check_Type(scheme_name, T_STRING);
    name = StringValuePtr(scheme_name);

    if (0 == first_scheme)
        return Qfalse;
    
    /* check the first one */
    if (!strncmp(name, first_scheme->scheme_name, first_scheme->name_len)) {
        save_scheme = first_scheme->next_scheme;

        free(first_scheme->scheme_name);
        //MUFF free(first_scheme->class);
        free(first_scheme);

        first_scheme = save_scheme;
        return Qtrue;
    }
    
    scheme = first_scheme;
    while (0 != scheme->next_scheme) {
        if ( !strncmp( name, scheme->next_scheme->scheme_name, scheme->next_scheme->name_len ) ) {
            save_scheme = scheme->next_scheme->next_scheme;
            
            free(scheme->next_scheme->scheme_name);
            //MUFF free(scheme->next_scheme->class);
            free(scheme->next_scheme);

            scheme->next_scheme = save_scheme;
            return Qtrue;
        }
        scheme = scheme->next_scheme;
    }
    return Qfalse;
}

// Rdoc needs to know 
#ifdef RDOC_NEVER_DEFINED
  mXML = rb_define_module("XML");
#endif

void
ruby_init_input_callbacks(void) {
  VALUE cInputCallbacks;
  cInputCallbacks = rb_define_class_under(mXML, "InputCallbacks", rb_cObject);

  /* Class Methods */
  rb_define_singleton_method(cInputCallbacks, "register",      input_callbacks_register_input_callbacks, 0);
  rb_define_singleton_method(cInputCallbacks, "add_scheme",    input_callbacks_add_scheme, 2);
  rb_define_singleton_method(cInputCallbacks, "remove_scheme", input_callbacks_remove_scheme, 1);
}
