/* $Id: libxslt.c 42 2007-12-07 06:09:35Z transami $ */

/* Please see the LICENSE file for copyright and distribution information */

#include "libxslt.h"
#include "libxml/xmlversion.h"

VALUE mXML;
VALUE cXMLDocument;

VALUE cXSLT;
VALUE eXMLXSLTStylesheetRequireParsedDoc;

/* call-seq:
 *    xslt.doc => #<XML::Document...>
 * 
 * Obtain the source XML::Document.
 */
VALUE
ruby_xslt_doc_get(VALUE self) {
  ruby_xslt *rxslt;
  Data_Get_Struct(self, ruby_xslt, rxslt);
  return(rxslt->xml_doc_obj);
}

/* call-seq:
 *    xslt.doc = some_xml_document
 * 
 * Set the source XML::Document.
 */
VALUE
ruby_xslt_doc_set(VALUE self, VALUE xml_doc_obj) {
  ruby_xslt *rxslt;
  ruby_xml_document_t *rxd;
  ruby_xslt_transform_context *rxtc;

  if (rb_obj_is_kind_of(xml_doc_obj, cXMLDocument) == Qfalse)
    rb_raise(rb_eTypeError, "Invalid argument: must be of type XML::Document");

  Data_Get_Struct(self, ruby_xslt, rxslt);
  rxslt->xml_doc_obj = xml_doc_obj;

  Data_Get_Struct(xml_doc_obj, ruby_xml_document_t, rxd);
  if (rxd->doc == NULL)
      return(Qnil);

  rxslt->ctxt = ruby_xslt_transform_context_new3(self);
  Data_Get_Struct(rxslt->ctxt, ruby_xslt_transform_context, rxtc);

  if (rxslt->xsp == NULL || rxd->doc == NULL)
    return(Qnil);

  rxtc->ctxt = xsltNewTransformContext(rxslt->xsp, rxd->doc) ;

  return(rxslt->xml_doc_obj);
}

/* call-seq:
 *    xslt.filename => "filename.xsl"
 * 
 * Obtain the stylesheet filename.
 */
VALUE
ruby_xslt_filename_get(VALUE self) {
  ruby_xslt *rxslt;
  Data_Get_Struct(self, ruby_xslt, rxslt);

  if (rxslt->data_type != RUBY_LIBXSLT_SRC_TYPE_FILE)
    return(Qnil);
  else
    return((VALUE)rxslt->data);
}


/* call-seq:
 *    xslt.filename = "filename.xsl"
 * 
 * Set the stylesheet filename.
 */
VALUE
ruby_xslt_filename_set(VALUE self, VALUE filename) {
  ruby_xslt *rxslt;
  Check_Type(filename, T_STRING);
  Data_Get_Struct(self, ruby_xslt, rxslt);

  if (rxslt->data_type != RUBY_LIBXSLT_SRC_TYPE_NULL)
    return(Qnil);

  rxslt->data_type = RUBY_LIBXSLT_SRC_TYPE_FILE;
  rxslt->data = (void *)filename;
  return(filename);
}


void
ruby_xslt_free(ruby_xslt *rxslt) {
  if (rxslt != NULL)
    ruby_xfree(rxslt);
}


void
ruby_xslt_mark(ruby_xslt *rxslt) {
  if (rxslt == NULL) return;
  if (!NIL_P(rxslt->ctxt))        rb_gc_mark(rxslt->ctxt);
  if (!NIL_P(rxslt->str))         rb_gc_mark(rxslt->str);
  if (!NIL_P(rxslt->xml_doc_obj)) rb_gc_mark(rxslt->xml_doc_obj);

  switch(rxslt->data_type) {
  case RUBY_LIBXSLT_SRC_TYPE_FILE:
    if (rxslt->data != NULL)
      rb_gc_mark((VALUE)rxslt->data);
    break;
  }
}


/* call-seq:
 *    XML::XSLT.new => #<XML::XSLT...>
 * 
 * Create a new XSLT instance. You will need to specify
 * a filename and document for the instance after it is
 * created.
 */
VALUE
ruby_xslt_new(VALUE class) {
  ruby_xslt *rxslt = ALLOC(ruby_xslt);

  if (rxslt == NULL)
    rb_raise(rb_eNoMemError, "No memory left for XSLT struct");

  rxslt->ctxt = Qnil;
  rxslt->data = NULL;
  rxslt->data_type = RUBY_LIBXSLT_SRC_TYPE_NULL;
  rxslt->str = Qnil;
  rxslt->xml_doc_obj = Qnil;
  rxslt->xsp = NULL;

  return(Data_Wrap_Struct(class, ruby_xslt_mark, ruby_xslt_free, rxslt));
}

/* call-seq:
 *    XML::XSLT.file("filename.xsl") => #<XML::XSLT...>
 * 
 * Create a new XSLT instance with the supplied stylesheet filename.
 */
VALUE
ruby_xslt_new_file(VALUE class, VALUE filename) {
  VALUE xslt;

  xslt = ruby_xslt_new(class);
  ruby_xslt_filename_set(xslt, filename);
  return(xslt);
}

/* call-seq:
 *    xslt.parse => #<XML::XSLT::Stylesheet...>
 * 
 * Parse the xsl source (specified by +filename+) and create
 * a +Stylesheet+ instance that will apply it against the
 * source document. If a parsed XML::Document isn't associated
 * with this +XSLT+ instance (via #doc=) then a 
 * Stylesheet::RequireParsedDoc exception is raised.
 */
VALUE
ruby_xslt_parse(VALUE self) {
  ruby_xml_document_t *rxd;
  ruby_xslt *rxslt;
  ruby_xslt_stylesheet *xss;
  VALUE xssobj;
  xsltStylesheetPtr sheet;

  Data_Get_Struct(self, ruby_xslt, rxslt);

  if (rxslt->data_type == RUBY_LIBXSLT_SRC_TYPE_FILE) {
    /*xssobj = ruby_xslt_stylesheet_new(cXSLTStylesheet,
              xsltParseStylesheetFile((const xmlChar *)
                    STR2CSTR(rxslt->data)));*/
  sheet = xsltParseStylesheetFile((const xmlChar *) STR2CSTR(rxslt->data));

    if (sheet) {
        xssobj = ruby_xslt_stylesheet_new(cXSLTStylesheet, sheet);
        Data_Get_Struct(xssobj, ruby_xslt_stylesheet, xss);
        xss->data = (void *)rb_obj_dup((VALUE)rxslt->data);
        xss->xml_doc_obj = rxslt->xml_doc_obj;
    } else
        xssobj = Qnil;

  } else if (rxslt->xml_doc_obj != Qnil) {
    Data_Get_Struct(rxslt->xml_doc_obj, ruby_xml_document_t, rxd);
    /*xssobj = ruby_xslt_stylesheet_new(cXSLTStylesheet,
              xsltParseStylesheetDoc(rxd->doc));*/
    sheet = xsltParseStylesheetDoc(rxd->doc);
    if (sheet) {
        xssobj = ruby_xslt_stylesheet_new(cXSLTStylesheet,sheet);
        Data_Get_Struct(xssobj, ruby_xslt_stylesheet, xss);
        xss->xml_doc_obj = rxslt->xml_doc_obj;
    } else
        xssobj = Qnil;

  } else {
    xssobj = Qnil;
  }

  return(xssobj);
}

#if defined(_WIN32)
__declspec(dllexport) 
#endif

void
Init_libxslt_ruby(void) {
  LIBXML_TEST_VERSION;
  
  // Must load libxml bindings to use xslst
  mXML = rb_const_get(rb_cObject, rb_intern("XML"));
  cXMLDocument = rb_const_get(mXML, rb_intern("Document"));

  cXSLT = rb_define_class_under(mXML, "XSLT", rb_cObject);

  rb_define_const(cXSLT, "MAX_DEPTH", INT2NUM(xsltMaxDepth));
  rb_define_const(cXSLT, "MAX_SORT", INT2NUM(XSLT_MAX_SORT));
  rb_define_const(cXSLT, "ENGINE_VERSION", rb_str_new2(xsltEngineVersion));
  rb_define_const(cXSLT, "LIBXSLT_VERSION", INT2NUM(xsltLibxsltVersion));
  rb_define_const(cXSLT, "LIBXML_VERSION", INT2NUM(xsltLibxmlVersion));
  rb_define_const(cXSLT, "XSLT_NAMESPACE", rb_str_new2((const char*)XSLT_NAMESPACE));
  rb_define_const(cXSLT, "DEFAULT_VENDOR", rb_str_new2(XSLT_DEFAULT_VENDOR));
  rb_define_const(cXSLT, "DEFAULT_VERSION", rb_str_new2(XSLT_DEFAULT_VERSION));
  rb_define_const(cXSLT, "DEFAULT_URL", rb_str_new2(XSLT_DEFAULT_URL));
  rb_define_const(cXSLT, "NAMESPACE_LIBXSLT", rb_str_new2((const char*)XSLT_LIBXSLT_NAMESPACE));
  rb_define_const(cXSLT, "NAMESPACE_NORM_SAXON", rb_str_new2((const char*)XSLT_NORM_SAXON_NAMESPACE));
  rb_define_const(cXSLT, "NAMESPACE_SAXON", rb_str_new2((const char*)XSLT_SAXON_NAMESPACE));
  rb_define_const(cXSLT, "NAMESPACE_XT", rb_str_new2((const char*)XSLT_XT_NAMESPACE));
  rb_define_const(cXSLT, "NAMESPACE_XALAN", rb_str_new2((const char*)XSLT_XALAN_NAMESPACE));

  ruby_init_xslt_stylesheet();
  ruby_init_xslt_transform_context();


  rb_define_singleton_method(cXSLT, "file", ruby_xslt_new_file, 1);
  rb_define_singleton_method(cXSLT, "new", ruby_xslt_new, 0);

  rb_define_method(cXSLT, "doc", ruby_xslt_doc_get, 0);
  rb_define_method(cXSLT, "doc=", ruby_xslt_doc_set, 1);
  rb_define_method(cXSLT, "filename", ruby_xslt_filename_get, 0);
  rb_define_method(cXSLT, "filename=", ruby_xslt_filename_set, 1);
  rb_define_method(cXSLT, "parse", ruby_xslt_parse, 0);

  //exsltRegisterAll();
}