/*
 *  sablot.c
 *  Sablot module for Ruby
 *  Copyright 2000-2001 TAKAHASHI 'Maki' Masayoshi

 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 * 
 * The Original Code is: Sablotron module for Ruby
 * 
 * The Developer of the Original Code is
 * TAKAHASHI 'Maki' Masayoshi. All Rights Reserved.
 * 
 * Contributor(s):
 * Takaaki Tateishi (setEncoding support)
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL"), in which case the provisions of the GPL are applicable 
 * instead of those above.  If you wish to allow use of your 
 * version of this file only under the terms of the GPL and not to
 * allow others to use your version of this file under the MPL,
 * indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by
 * the GPL.  If you do not delete the provisions above, a recipient
 * may use your version of this file under either the MPL or the
 * GPL.

 *  $Id: sablot.c,v 1.5 2001/03/04 02:16:50 maki Exp maki $
 *  
 */

#include <string.h>
#include "ruby.h"
#include "intern.h"

#include <sablot.h>

static VALUE cSablot;
static VALUE eSablotError;

static ID id_MessageHandlerCode;
static ID id_MessageHandlerLog;
static ID id_MessageHandlerError;

static MessageHandler my_message_handler;

typedef struct _sablot_data {
  SablotHandle processor;
} sablot_data;

#define GetProcessor(obj, sab) {\
    Data_Get_Struct(obj, sablot_data, sab);\
}


static void free_sablot(sablot_data *sab)
{
  if (sab->processor)
    SablotDestroyProcessor(sab->processor);
  /* sab->processor = NULL; */
}


static VALUE rb_sablot_create_processor(int argc, VALUE *argv, VALUE klass)
{
  VALUE obj;
  int err;
  sablot_data *sab;

  obj = Data_Make_Struct(klass, sablot_data,
			 0,
			 free_sablot,
			 sab);

  err = SablotCreateProcessor(&(sab->processor));
  if (err) {
    rb_raise(eSablotError, "cannot create a new instance");
  }
  rb_obj_call_init(obj, argc, argv);
  return  obj;
}


static char**
rb_makeparam(VALUE obj)
{
  VALUE keys, key, val;
  char **namedbuf;
  long len;
  long keylen, vallen;
  char *str;
  int i;
  
  if NIL_P(obj) {
    namedbuf = ALLOC_N(char*,2);
    *namedbuf = '\0';
    *(namedbuf+1) = '\0';
    return namedbuf;
  }

  if (TYPE(obj) != T_HASH)
    rb_raise(eSablotError, "parametar must be a Hash");
  
  keys = rb_funcall(obj, rb_intern("keys"), 0);
  len = RARRAY(keys)->len;
  namedbuf = ALLOC_N(char*, len*2+2);
  for (i = 0; i < len*2; i+=2) {
    key = rb_ary_pop(keys);
    val = rb_hash_aref(obj, key);
    Check_Type(key, T_STRING);
    Check_Type(val, T_STRING);
    keylen = RSTRING(key)->len;
    vallen = RSTRING(val)->len;
    str = ALLOC_N(char, keylen+vallen+2);

    *(namedbuf+i) = str;
    MEMCPY(str, STR2CSTR(key), char, keylen);
    str += keylen;
    *str = '\0';
    str++;
    *(namedbuf+i+1) = str;
    MEMCPY(str, STR2CSTR(val), char, vallen);
    *(str+vallen) = '\0';
  }
  *(namedbuf+i) = '\0';
  *(namedbuf+i+1) = '\0';
  /**
  printf(":%s:\n",*namedbuf);
  printf(":%s:\n",*(namedbuf+1));
  printf(":%s:\n",*(namedbuf+2));
  printf(":%s:\n",*(namedbuf+3));
  **/
  return namedbuf;
}

static VALUE rb_sablot_run_processor(VALUE self, VALUE sheet,
				     VALUE input, VALUE result,
				     VALUE params, VALUE args)
{
    int err;
    sablot_data *sab;

    char *inputURI, *sheetURI, *resultURI;
    char **params_str;
    char **args_str;

    GetProcessor(self, sab);
    inputURI  = STR2CSTR(input);
    sheetURI  = STR2CSTR(sheet);
    resultURI = STR2CSTR(result);
    params_str = rb_makeparam(params);
    args_str = rb_makeparam(args);

    /* printf("%s:\n%s:\n%s\n",inputURI, sheetURI, resultURI); */

    

    SablotRegHandler(sab->processor, HLR_MESSAGE,
		     (void*)&my_message_handler, (void*)self);

    err = SablotRunProcessor(sab->processor,
			     sheetURI, inputURI, resultURI,
			     params_str, args_str);

    SablotUnregHandler(sab->processor, HLR_MESSAGE, NULL, NULL);

    if (err) {
      rb_raise(eSablotError, "Sablotron Error");
    }
    return self;
}

static VALUE rb_sablot_get_result_arg(VALUE self, VALUE result)
{
  char *arg_uri, *arg_val;
  int err;
  VALUE ret;
  sablot_data *sab;
  
  if (TYPE(result) != T_STRING)
      rb_raise(eSablotError, "Sablotron Error");
  
  GetProcessor(self, sab);
  arg_uri = STR2CSTR(result);
  arg_val = NULL;
  err = SablotGetResultArg(sab->processor, arg_uri, &arg_val);
  if (err) {
      rb_raise(eSablotError, "Sablotron Error");
      SablotFree(arg_val);
  } else if (arg_val) {
    ret = rb_tainted_str_new2(arg_val);
    SablotFree(arg_val);
  } else {
    ret = Qnil;
  }
  return ret;
}

static VALUE rb_sablot_set_log(VALUE self, VALUE filename, VALUE level)
{
  char *log;
  int lev;
  int err;
  VALUE ret;
  sablot_data *sab;
  
  Check_Type(level, T_FIXNUM);
  
  GetProcessor(self, sab);

  if NIL_P(filename) {
    log = NULL;
  } else {
    /* Check_Type(filename, T_STRING); */
    log = STR2CSTR(filename);
  }
  lev = FIX2INT(level);
  err = SablotSetLog(sab->processor, log, lev);
  if (err) {
      rb_raise(eSablotError, "Sablotron Error");
  }
  return self;
}

static VALUE rb_sablot_set_base(VALUE self, VALUE base)
{
  char *base_uri;
  int err;
  VALUE ret;
  sablot_data *sab;
  
  Check_Type(base, T_STRING);

  GetProcessor(self, sab);
  base_uri = STR2CSTR(base);

  err = SablotSetBase(sab->processor, base_uri);
  if (err) {
      rb_raise(eSablotError, "Sablotron Error");
  }
  return self;
}

static VALUE rb_sablot_set_base_for_scheme(VALUE self, VALUE scheme, VALUE base)
{
  char *scheme_name;
  char *base_uri;
  int err;
  VALUE ret;
  sablot_data *sab;
  
  Check_Type(base, T_STRING);
  Check_Type(scheme, T_STRING);

  GetProcessor(self, sab);
  scheme_name = STR2CSTR(scheme);
  base_uri = STR2CSTR(base);

  err = SablotSetBaseForScheme(sab->processor, scheme_name, base_uri);
  if (err) {
      rb_raise(eSablotError, "Sablotron Error");
  }
  return self;
}


static VALUE rb_sablot_process_strings(VALUE obj, VALUE xsl, VALUE xml)
{
  VALUE retval;
  char *templ;
  char *input;
  char *result;

  Check_Type(xsl, T_STRING);
  Check_Type(xml, T_STRING);

  input = RSTRING(xml)->ptr;
  templ = RSTRING(xsl)->ptr;
  
  if (SablotProcessStrings(templ, input, &result))
    rb_raise(eSablotError, "Sablotron Error");

  retval = rb_tainted_str_new2(result);
  SablotFree(result);
  return retval;
}

static VALUE rb_sablot_process_files(VALUE obj, VALUE xsl, VALUE xml, VALUE xml2)
{
  VALUE retval;
  char *templ;
  char *input;
  char *result;

  Check_Type(xsl, T_STRING);
  Check_Type(xml, T_STRING);
  Check_Type(xml2, T_STRING);

  templ = RSTRING(xsl)->ptr;
  input = RSTRING(xml)->ptr;
  result = RSTRING(xml2)->ptr;
  
  if (SablotProcessFiles(templ, input, result))
    rb_raise(eSablotError, "Sablotron Error");

  SablotFree(result);
  return Qtrue;
}

static VALUE rb_sablot_set_encoding(VALUE self, VALUE enc)
{
  sablot_data *sab;
  char *encoding;

  GetProcessor(self, sab);

  if (NIL_P(enc)) {
    encoding = NULL;
  } else {
    encoding = STR2CSTR(enc);
  };

  SablotSetEncoding(sab->processor, encoding);

  return self;
}

static MH_ERROR my_MessageHandlerCode(void * userData,
					  SablotHandle processor_,
					  int severity,
					  unsigned short facility,
					  unsigned short code)
{
  if (rb_method_boundp(CLASS_OF((VALUE)userData),
		       id_MessageHandlerCode, 0)) {

    rb_funcall((VALUE)userData, id_MessageHandlerCode, 1,
	       INT2NUM(code));

  }
}

static MH_ERROR my_MessageHandlerLog(void * userData,
				     SablotHandle processor_,
				     MH_ERROR code,
				     MH_LEVEL level,
				     char ** fields)
{
  VALUE log_ary;
  unsigned long log_len;
  char *log_str;
  VALUE log_msg;

  if (rb_method_boundp(CLASS_OF((VALUE)userData),
		       id_MessageHandlerLog, 0)) {

    log_ary = rb_ary_new();
    if (fields != NULL) {
      while (*fields != NULL) {
	log_len = strlen(*fields);
	log_str = ALLOC_N(char, log_len+1);
	strncpy(log_str, *fields, log_len+1);
	log_msg = rb_tainted_str_new2(log_str);
	rb_ary_push(log_ary, log_msg);
	fields++;
      }
    }
    rb_funcall((VALUE)userData, id_MessageHandlerLog, 3, 
	       INT2NUM(code), INT2NUM(level), log_ary);
  }
}

static MH_ERROR my_MessageHandlerError(void *userData,
				       SablotHandle processor_,
				       MH_ERROR code,
				       MH_LEVEL level,
				       char ** fields)
{
  VALUE err_ary;
  char *err_str;
  VALUE err_msg;
  unsigned long err_len;

  if (rb_method_boundp(CLASS_OF((VALUE)userData),
		       id_MessageHandlerError, 0)) {

    err_ary = rb_ary_new();
    if (fields != NULL) {
      while (*fields != NULL) {
	err_len = strlen(*fields);
	err_str = ALLOC_N(char, err_len+1);
	strncpy(err_str, *fields, err_len+1);
	err_msg = rb_tainted_str_new2(err_str);
	rb_ary_push(err_ary, err_msg);
	fields++;
      }
    }
    rb_funcall((VALUE)userData, id_MessageHandlerError, 3, 
	       INT2NUM(code), INT2NUM(level), err_ary);
  }
}

static MessageHandler my_message_handler = {
  my_MessageHandlerCode,
  my_MessageHandlerLog,
  my_MessageHandlerError
};

void Init_sablot()
{
    cSablot = rb_define_class("Sablot", rb_cObject);
    eSablotError = rb_define_class("SablotError", rb_eStandardError);

    rb_define_singleton_method(cSablot, "createProcessor",
			       rb_sablot_create_processor, -1);
    rb_define_singleton_method(cSablot, "new",
			       rb_sablot_create_processor, -1);

    rb_define_method(cSablot, "runProcessor",
		     rb_sablot_run_processor, 5);
    rb_define_method(cSablot, "resultArg",
		     rb_sablot_get_result_arg, 1);
    rb_define_method(cSablot, "setLog",
		     rb_sablot_set_log, 2);
    rb_define_method(cSablot, "setBase",
		     rb_sablot_set_base, 1);
    rb_define_method(cSablot, "setBaseForScheme",
		     rb_sablot_set_base_for_scheme, 2);
    rb_define_method(cSablot, "setEncoding",
		     rb_sablot_set_encoding, 1);

    rb_define_singleton_method(cSablot, "process_strings",
			      rb_sablot_process_strings, 2);
    rb_define_singleton_method(cSablot, "processStrings",
			      rb_sablot_process_strings, 2);
    rb_define_singleton_method(cSablot, "process_files",
			      rb_sablot_process_files, 3);
    rb_define_singleton_method(cSablot, "processFiles",
			      rb_sablot_process_files, 3);

    rb_define_const(cSablot, "MH_DEBUG",    INT2FIX(MH_LEVEL_DEBUG));
    rb_define_const(cSablot, "MH_INFO",     INT2FIX(MH_LEVEL_INFO));
    rb_define_const(cSablot, "MH_WARN",     INT2FIX(MH_LEVEL_WARN));
    rb_define_const(cSablot, "MH_ERROR",    INT2FIX(MH_LEVEL_ERROR));
    rb_define_const(cSablot, "MH_CRITICAL", INT2FIX(MH_LEVEL_CRITICAL));

    id_MessageHandlerCode = rb_intern("messageCode");
    id_MessageHandlerLog = rb_intern("messageLog");
    id_MessageHandlerError = rb_intern("messageError");

    /* rb_define_const(cSablot, "Sablot_VERSION", rb_str_new2("0.44")); */
}

