// Contains operations useful in php typemaps
// typemaps don't support "subroutine" typemaps where one typemap can
// include another, so we define useful bodies here to include inother places

// _strbuf_out is designed to be included as part of an argout typemap
// and _strbuf_in which should be part of the in typemap for the same argument
// They share knowledge of the "force" temporary variable.
// You probably don't want to make direct use of _strbuf_out or _strbuf_in but
// you may want strbufsize_inout which uses these
%define _strbuf_out(BUFFER,SIZE)
  if (force$argnum) {  /* pass back arg$argnum through params ($arg) if we can */
    if(! PZVAL_IS_REF(*$arg)) {
      zend_error(E_WARNING, "Parameter %d of $symname wasn't passed by reference [argout TYPES *, TYPES &]",$argnum-argbase);
    } else {
      #if SIZE
      // SIZE
      ZVAL_STRINGL(*$arg,BUFFER$argnum, SIZE, 1);
      #else
      // Measure length
      ZVAL_STRING(*$arg,BUFFER$argnum, 1);
      #endif
    }
  }  
%enddef

%define 
_strbuf_in(BUFFER)
  // _strbuf_in 
  if(! SWIG_ConvertPtr(*$input, (void **) &$1, $1_descriptor) < 0) {
    /* Using a _p_ SWIG pointer, so they will have to manage size themselves */
    force=0;
  } else if ((*$input)->type==IS_STRING ||
             (*$input)->type==IS_LONG ||
             /* Null passed by reference means we want a value back */
             (*$input)->type==IS_NULL ||
             (*$input)->type==IS_BOOL ||
             (*$input)->type==IS_DOUBLE) {
       
    // Only pass back if we can...
    if (PZVAL_IS_REF(*$input)) force=1;
    else force=0;

    convert_to_string_ex($input);
    // Init temp buffer
    strncpy((char *)temp,Z_STRVAL_PP($input),sizeof(BUFFER));
    $1=temp;
  } else {
    force=0;
    $1=NULL;
    zend_error(E_ERROR, "Type error in argument %d of $symname. Expected %s or at least something looking vaguely like a string hopefully passed by reference", $argnum-argbase, $1_descriptor->name);
  }
%enddef

// strbufsize_inout defines a typemap which
// Example: strbufsize_inout(UCHAR FAR * szErrorMsg, SWORD cbErrorMsgMax,1024)
// defines a typeemap for UCHAR FAR * szErrorMsg, SWORD cbErrorMsgMax with a
// max buffer size of 1024
%define strbufsize_inout(BUFFER,SIZE,MAXSIZE)
%typemap(in) (BUFFER, SIZE) ($*1_ltype temp[MAXSIZE], int force) {
  _strbuf_in(temp)
  $2=sizeof(temp);
}
%typemap(argout) (BUFFER, SIZE) {
  _strbuf_out((char *)temp,strlen(temp))
}
%enddef

// char array can be in/out, though the passed string may not be big enough...
// so we have to size it
// e.g. Do: strarray_inout(char [ANY])
%define strarray_inout(TYPE) 
%typemap(in) TYPE ($*1_ltype temp[$1_dim0], int force) %{
        _strbuf_in(temp)
%}
%typemap(argout) TYPE %{
        _strbuf_out((char *)temp,$1_dim0);
%}
%enddef

%define strarraysize_inout(TYPE,SIZE) 
%typemap(in) TYPE ($*1_ltype temp[SIZE], int force) %{
        _strbuf_in(temp)
%}
%typemap(argout) TYPE %{
        _strbuf_out((char *)temp,SIZE);
%}
%enddef

%define strbuf_inout(BUFFER,MAXSIZE)
%typemap(in) (BUFFER) ($*1_ltype temp[MAXSIZE], int force) {
	_strbuf_in(temp)
}
%typemap(argout) (BUFFER) {
  _strbuf_out(temp,strlen(temp))
}
%enddef

/* Typemap for in/argout references
   NOTE: we don't choose to use these for arrays yet, maybe later */
%define outLONG(ZVALARG,CARG)
      ZVAL_LONG(*$arg,intr$argnum);
%enddef

// Defines an on/argout typemap for ordinal types
//Usage: %typemap_inout_ord(bool,convert_to_bool_ex,ZVAL_BOOL)
%define %typemap_inout_ord(TYPES,TYPE_IN,TYPE_OUT)
%typemap(in) TYPES * ($*1_ltype intr, int force), 
             TYPES & ($*1_ltype intr, int force) %{
   /* inout typemap for TYPES using TYPE_IN and TYPE_OUT */
  if(SWIG_ConvertPtr(*$input, (void **) &$1, $1_descriptor) < 0) {
    /* So... we didn't get a ref or ptr, but can it be reasonably 
       co-erced into what we were looking for a ref of or ptr to? */
    if (!PZVAL_IS_REF(*$input) && (*$input)->type==IS_NULL) {
      // null passed not by reference means pass NULL
      $1 = NULL;
      force=0;
    } else if (PZVAL_IS_REF(*$input) &&
        ((*$input)->type==IS_STRING ||
         (*$input)->type==IS_LONG ||
         /* Null passed by reference means we want a value back */
         (*$input)->type==IS_NULL ||
         (*$input)->type==IS_BOOL ||
         (*$input)->type==IS_DOUBLE)) {
      TYPE_IN($input);
      intr = ($*1_ltype) (*$input)->value.lval;
      $1 = &intr;
      /* have to passback arg$arg too */
      force=1;
    } else {  /* wasn't a pre/ref/thing, OR anything like an int thing */
      force=0;
      zend_error(E_ERROR, "Type error in argument %d of $symname. Expected %s or at least something looking vaguely like a number hopefully passed by reference", $argnum-argbase, $1_descriptor->name);
    }
  } else force=0;
%}

%typemap(argout) TYPES *, TYPES & %{
  if (force$argnum) {  /* pass back arg$argnum through params ($arg) if we can */
    if(! PZVAL_IS_REF(*$arg)) {
      zend_error(E_WARNING, "Parameter %d of $symname wasn't passed by reference [argout TYPES *, TYPES &]",$argnum-argbase);
    } else {
       TYPE_OUT(*$arg,intr$argnum);	
    }
  }
%}
%enddef
