/*-----------------------------------------------------------------*/
/*! 
  \file scscpprocedurecall.cxx
  \brief SCSCP message : procedure call.
   class SCSCP_procedurecalloptions, SCSCP_procedurecall
   types SCSCP_option_return
 
  \author  M. Gastineau 
           Astronomie et Systemes Dynamiques, IMCCE, CNRS, Observatoire de Paris. 

   Copyright, 2008, 2009, 2010, 2011, 2012  M. Gastineau, CNRS
   email of the author : gastineau@imcce.fr

   \bug M. GASTINEAU 12/09/08 : update for SCSCP 1.2
   \bug M. GASTINEAU 21/04/09 : update for SCSCP 1.3
   \bug M. GASTINEAU 29/03/12 : correct message if type of action is missing 
    (SCSCP_option_return_???)  (ticket #8275)
*/
/*-----------------------------------------------------------------*/

/*-----------------------------------------------------------------*/
/* License  of this file :
 This file is "dual-licensed", you have to choose one  of the two licenses 
 below to apply on this file.
 
    CeCILL-C
    	The CeCILL-C license is close to the GNU LGPL.
    	( http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html )
 
 or CeCILL v2.0
      The CeCILL license is compatible with the GNU GPL.
      ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
 

This library is governed by the CeCILL-C or the CeCILL license under 
French law and abiding by the rules of distribution of free software.  
You can  use, modify and/ or redistribute the software under the terms 
of the CeCILL-C or CeCILL license as circulated by CEA, CNRS and INRIA  
at the following URL "http://www.cecill.info". 

As a counterpart to the access to the source code and  rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty  and the software's author,  the holder of the
economic rights,  and the successive licensors  have only  limited
liability. 

In this respect, the user's attention is drawn to the risks associated
with loading,  using,  modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean  that it is complicated to manipulate,  and  that  also
therefore means  that it is reserved for developers  and  experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or 
data to be ensured and,  more generally, to use and operate it in the 
same conditions as regards security. 

The fact that you are presently reading this means that you have had
knowledge of the CeCILL-C or CeCILL license and that you accept its terms.
*/
/*-----------------------------------------------------------------*/



#if HAVE_CONFIG_H
#include "scscpconfig.h"
#endif /*HAVE_CONFIG_H*/
#if HAVE_WINDOWS_H
#include <windows.h>
#endif /*HAVE_CONFIG_H*/
#include <stdio.h>
#if HAVE_STRING_H
#include <string.h>
#endif
#if HAVE_TIME_H
#include <time.h>
#endif
#define __SCSCP_WITHIN_SCSCP
#include "scscp.h"
#include "scscpinternal.h"
#include "scscpfileclient.h"
#include "scscpfileserver.h"
#include "scscpprocedurecall.h"
#include "scscpoptions.h"
#include "scscpdebug.h"
#include "scscptags.h"

/*-----------------------------------------------------------------*/
/*!
    write the object to the stream
  @param options (in) object to be checked
   @param stream (inout) output stream
   @param status (inout) error code
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_write(SCSCP_io *stream, const SCSCP_calloptions options,  SCSCP_status* status)
{
 int res;
 SCSCP_debugprint("SCSCP_co_write() - enter\n");
 
 res = SCSCP_io_writebeginOMATP(stream, NULL, status);

 //write call id
 if (options->m_set_callid) 
  res &= SCSCP_callid_write(stream, &options->m_callid, status);
 else
 {
  SCSCP_status_seterror(status, SCSCP_STATUS_CALLIDISNOTSET);
  res = 0;
 }
 //write runtime limit
 if (options->m_set_runtime_limit) res &=SCSCP_io_writePairOMSOMIsizet(stream,SCSCP_TAGS_cdname, SCSCP_TAGS_option_runtime, options->m_runtime_limit, status);
 //write min memory
 if (options->m_set_minimal_memory) res &=SCSCP_io_writePairOMSOMIsizet(stream,SCSCP_TAGS_cdname, SCSCP_TAGS_option_min_memory, options->m_minimal_memory, status);
 //write max memory
 if (options->m_set_maximal_memory) res &=SCSCP_io_writePairOMSOMIsizet(stream,SCSCP_TAGS_cdname, SCSCP_TAGS_option_max_memory, options->m_maximal_memory, status);
 //write debug level
 if (options->m_set_minimal_memory) res &=SCSCP_io_writePairOMSOMIint(stream,SCSCP_TAGS_cdname, SCSCP_TAGS_option_debuglevel, options->m_debuglevel, status);
 //write return type
 if (options->m_set_returntype) 
 {
  const char* returntype = NULL;
  if (options->m_returntype==SCSCP_option_return_object) returntype = SCSCP_TAGS_option_return_object;
  if (options->m_returntype==SCSCP_option_return_cookie) returntype = SCSCP_TAGS_option_return_cookie;
  if (options->m_returntype==SCSCP_option_return_nothing) returntype = SCSCP_TAGS_option_return_nothing;

  res &=SCSCP_io_writePairOMSOMSTR(stream,SCSCP_TAGS_cdname, returntype, NULL,status);
 }
 else
 {
  SCSCP_status_seterror(status, SCSCP_STATUS_RETURNTYPEISNOTSET);
  res = 0;
 }

 res &= SCSCP_io_writeendOMATP(stream, status);

 SCSCP_debugprint("SCSCP_co_write(,%d) - returns %d\n", SCSCP_status_is(status), res);
 return res;
}



/*-----------------------------------------------------------------*/
/*! read the object from the stream
   @param stream (inout) output stream
   @param iter (inout) iterator on the stream

  @bug M. GASTINEAU 12/09/08 : update for SCSCP 1.2
  @bug M. GASTINEAU 29/03/12 : correct message if type of action (SCSCP_option_return_???) is missing 
*/
/*-----------------------------------------------------------------*/
static int SCSCP_co_readoptions(SCSCP_io *stream, SCSCP_calloptions options, SCSCP_xmlnodeptr* iter, 
                         SCSCP_status* status)
{
 int res = 1;
 int  bresfinal = 1;
 SCSCP_xmlnodeptr iterend;
 SCSCP_xmlnodeptr iterchild1, iterchild2, iterchild3;
 
 SCSCP_debugprint("SCSCP_co_readoptions() - enter\n");
 
 SCSCP_xmlnode_readbeginOMATTR(iter);
 iterend = *iter;
 bresfinal = res = SCSCP_xmlnode_readbeginOMATP(iter);
 
 /* try to read all attributes */
 if (res)
 {
  SCSCP_xmlnodeptr iterchild = *iter;
  SCSCP_time timer;
  SCSCP_memory mem;
  SCSCP_debuglevel debuglevel;

  res  = SCSCP_callid_read(&iterchild, &options->m_callid, status);
  
  iterchild = *iter;
  res = SCSCP_xmlnode_readpairOMSOMIsizet(&iterchild, SCSCP_TAGS_cdname, SCSCP_TAGS_option_runtime, &timer); 
  if (res) res = SCSCP_co_set_runtimelimit(&options, timer, status);
  
  iterchild = *iter;
  res = SCSCP_xmlnode_readpairOMSOMIsizet(&iterchild, SCSCP_TAGS_cdname, SCSCP_TAGS_option_min_memory, &mem); 
  if (res) res = SCSCP_co_set_minmemory(&options, mem, status);
  
  iterchild = *iter;
  res = SCSCP_xmlnode_readpairOMSOMIsizet(&iterchild, SCSCP_TAGS_cdname, SCSCP_TAGS_option_max_memory, &mem); 
  if (res) res = SCSCP_co_set_maxmemory(&options, mem, status);
  
  iterchild  = *iter;
  res = SCSCP_xmlnode_readpairOMSOMIint(&iterchild, SCSCP_TAGS_cdname, SCSCP_TAGS_option_debuglevel, &debuglevel); 
  if (res) res = SCSCP_co_set_debuglevel(&options, mem, status);
  
   iterchild = *iter;
   iterchild1 = iterchild;
   iterchild2 = iterchild;
   iterchild3 = iterchild;
   if (SCSCP_xmlnode_readcheckOMS(&iterchild1, SCSCP_TAGS_cdname, SCSCP_TAGS_option_return_object)) 
   { res = SCSCP_co_set_returntype(&options,SCSCP_option_return_object, status);  }
   else  if (SCSCP_xmlnode_readcheckOMS(&iterchild2, SCSCP_TAGS_cdname, SCSCP_TAGS_option_return_cookie)) 
   { res = SCSCP_co_set_returntype(&options,SCSCP_option_return_cookie, status);  }
   else  if (SCSCP_xmlnode_readcheckOMS(&iterchild3, SCSCP_TAGS_cdname, SCSCP_TAGS_option_return_nothing)) 
   { res = SCSCP_co_set_returntype(&options,SCSCP_option_return_nothing, status); }
   else 
   { 
    int mandatory = 0;
    int j;
    bresfinal = res = 0; 
    /* check if we use the version 1.3 or later */
    for (j=0; j<(*stream)->m_allowedversionscount; j++)
    {
     if (strcmp((*stream)->m_allowedversions[j], SCSCP_PROTOCOL_VERSION_1_3)==0) mandatory=1;
    }
    if (mandatory==1)
    { 
     SCSCP_status_seterror(status, SCSCP_STATUS_USAGEUNKNOWNRETURNTYPE);
     bresfinal = res = 0; 
    }
    else
    { /* SCSCP_TAGS_option_return_??? a re optional in older version of the protocol */
     bresfinal = res = 1;
    }
   }
  
  iterend = SCSCP_xmlnode_getnext(iterend);
 }
 
 *iter = iterend;

 SCSCP_debugprint("SCSCP_co_readoptions() - returns %d\n", bresfinal);
 return bresfinal;
}

/*-----------------------------------------------------------------*/
/*! checks if options is null and returns 0 on error
  @param options (in) object to be checked
  @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
static int SCSCP_co_checkNULL(SCSCP_calloptions* options, SCSCP_status* status)
{
   int res = 1;
   
   if (options==NULL || *options==NULL)
   {
     SCSCP_status_seterror(status,  SCSCP_STATUS_CALLOPTIONSOBJECTNULL);
     res = 0;
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*! initialize the object options and set status if an error occurs
   @param options (out) options
   @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_init(SCSCP_calloptions* options, SCSCP_status* status)
{
   int res;
   
   if (options==NULL)
   {
     SCSCP_status_seterror(status,  SCSCP_STATUS_CALLOPTIONSOBJECTNULL);
     res = 0;
   }
   else
   {
    *options = (struct SCSCP_procedurecalloptions*)SCSCP_malloc(sizeof(struct SCSCP_procedurecalloptions), status);
    res = (*options!=NULL);
    if (res)
    {
     SCSCP_callid_init(&(*options)->m_callid);
     (*options)->m_set_callid = 0;
     (*options)->m_set_runtime_limit = 0;
     (*options)->m_set_minimal_memory = 0;
     (*options)->m_set_maximal_memory = 0;
     (*options)->m_set_debuglevel = 0;
     (*options)->m_encodingtype = SCSCP_encodingtype_XML;
     res = SCSCP_co_set_returntype(options, SCSCP_option_return_object, SCSCP_STATUS_IGNORE);
     res &= SCSCP_co_generateunique_callid(options, status);
    }
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*!  clear the object options previously initialized by SCSCP_co_init
   @param options (inout) options
   @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_clear(SCSCP_calloptions* options, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   if (res) 
   {
     SCSCP_callid_clear(&(*options)->m_callid);
     free( (*options));
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*! set the procedure call ID (buffer can't be released before calling SCSCP_co_clear)
   @param options (inout) options
   @param buffer (in) string buffer
   @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_set_callid(SCSCP_calloptions* options, const char *buffer, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   if (res) 
   {
    (*options)->m_set_callid  = 1;
    SCSCP_callid_set(&(*options)->m_callid, buffer);
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*! set the procedure call ID (buffer can't be used after )
   @param options (inout) options
   @param buffer (inout) string buffer (not duplicated)
   @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_set_writablecallid(SCSCP_calloptions* options, char *buffer, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   if (res)  
   {
    (*options)->m_set_callid  = 1;
    SCSCP_callid_setwritable(&(*options)->m_callid, buffer);
   } 
   return res;
}


/*-----------------------------------------------------------------*/
/*! \internal generate and set the procedure call ID.
   To be thread-safe, it will use
        address of options, 
        current time,
        large counter
   @param options (inout) options
   @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_generateunique_callid(SCSCP_calloptions* options, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   if (res) 
   {
     static long long counter = 0;
     const char *prefix="libSCSCP";
     size_t ncount = strlen(prefix)+100; /* for time, address and counter */
     char *buffer = (char *)SCSCP_malloc(ncount*sizeof(char), status);
     if (buffer)
     {
      time_t ltime;
      time(&ltime);
      if (snprintf(buffer, ncount, "%s:%p:%lld:%lld", prefix, options, (long long)ltime, counter)<=0)
      {
       SCSCP_status_seterror(status,SCSCP_STATUS_ERRNO);
       res = 0;
      }
      else
      {
       (*options)->m_set_callid  = 1;
       SCSCP_callid_setwritable(&(*options)->m_callid, buffer);
       counter++;
      }
     }
     else
     {
      res = 0;
     }
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*! get the procedure call ID 
   @param options (inout) options
   @param buffer (out) string buffer
   @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_get_callid(SCSCP_calloptions* options, const char **buffer, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   SCSCP_debugprint("SCSCP_co_get_callid() -enter\n");
   if (res)  *buffer = SCSCP_callid_get(&((*options)->m_callid));
   SCSCP_debugprint("SCSCP_co_get_callid(,'%s',%d) - returns %d\n",buffer==NULL?"NULL":*buffer, SCSCP_status_is(status), res);
   return res;
}

/*-----------------------------------------------------------------*/
/*! set the runtime limit
   @param options (inout) options
   @param p_time (in) runtime limit
   @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_set_runtimelimit(SCSCP_calloptions* options, size_t p_time, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   if (res) 
   {
    (*options)->m_set_runtime_limit = 1;
    (*options)->m_runtime_limit = p_time;
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*! get the runtime limit
  if the runtime limit is unknown => it returns 0 
     and status is set to  SCSCP_STATUS_USAGEUNKNOWNRUNTIMELIMIT
   @param options (inout) options
   @param p_time (out) runtime limit
   @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_get_runtimelimit(SCSCP_calloptions* options, size_t* p_time, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   if (res) 
   {
    res = (*options)->m_set_runtime_limit;
    if (!res) SCSCP_status_seterror(status, SCSCP_STATUS_USAGEUNKNOWNRUNTIMELIMIT);
    *p_time = (*options)->m_runtime_limit;
   }
   return res;
}


/*-----------------------------------------------------------------*/
/*! set the minimal memory
   @param options (inout) options
   @param memsize (in) minimal memory required
   @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_set_minmemory(SCSCP_calloptions* options, size_t memsize, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   if (res)  
   {
    (*options)->m_set_minimal_memory = 1;
    (*options)->m_minimal_memory = memsize;
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*! get the minimal memory
  if the minimal memory is unknown => it returns 0 
     and status is set to  SCSCP_STATUS_USAGEUNKNOWNMINMEMORY
   @param options (inout) options
   @param memsize (out) minimal memory required
   @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_get_minmemory(SCSCP_calloptions* options, size_t* memsize, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   if (res) 
   {
    res = (*options)->m_set_minimal_memory;
    if (!res) SCSCP_status_seterror(status, SCSCP_STATUS_USAGEUNKNOWNMINMEMORY);
    *memsize = (*options)->m_minimal_memory;
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*! set the maximal memory
   @param options (inout) options
   @param memsize (in) maximal memory required
   @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_set_maxmemory(SCSCP_calloptions* options, size_t memsize, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   if (res) 
   {
    (*options)->m_set_maximal_memory = 1;
    (*options)->m_maximal_memory = memsize;
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*! get the maximal memory
  if the minimal memory is unknown => it returns 0 
     and status is set to  SCSCP_STATUS_USAGEUNKNOWNMAXMEMORY
   @param options (inout) options
   @param memsize (out) maximal memory required
   @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_get_maxmemory(SCSCP_calloptions* options, size_t* memsize, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   if (res) 
   {
    res = (*options)->m_set_maximal_memory;
    if (!res) SCSCP_status_seterror(status, SCSCP_STATUS_USAGEUNKNOWNMAXMEMORY);
    *memsize = (*options)->m_maximal_memory;
   }
   return res;
}


/*-----------------------------------------------------------------*/
/*! set the return type
   @param options (inout) options
   @param returntype (in) return type
   @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_set_returntype(SCSCP_calloptions* options, SCSCP_option_return returntype, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   if (res) 
   {
    (*options)->m_set_returntype = 1;
    (*options)->m_returntype = returntype;
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*! get the return type
  if the minimal memory is unknown => it returns 0 
     and status is set to  SCSCP_STATUS_USAGEUNKNOWNRETURNTYPE
   @param options (inout) options
   @param returntype (out) return type
   @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_get_returntype(SCSCP_calloptions* options, SCSCP_option_return* returntype, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   if (res) 
   {
    res = (*options)->m_set_returntype;
    if (!res) SCSCP_status_seterror(status, SCSCP_STATUS_USAGEUNKNOWNRETURNTYPE);
    *returntype = (*options)->m_returntype;
   }
   return res;
}


/*-----------------------------------------------------------------*/
/*! set the debug level
   @param options (inout) options
   @param debuglevel (in) debug level
   @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_set_debuglevel(SCSCP_calloptions* options, int debuglevel, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   if (res)  
   {
    (*options)->m_set_debuglevel = 1;
    (*options)->m_debuglevel = debuglevel;
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*! get the debug level
  if the minimal memory is unknown => it returns 0 
     and status is set to  SCSCP_STATUS_USAGEUNKNOWNDEBUGLEVEL
   @param options (inout) options
   @param debuglevel (out) debug level
   @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_get_debuglevel(SCSCP_calloptions* options, int* debuglevel, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   if (res) 
   {
    res = (*options)->m_set_debuglevel;
    if (!res) SCSCP_status_seterror(status, SCSCP_STATUS_USAGEUNKNOWNDEBUGLEVEL);
    *debuglevel = (*options)->m_debuglevel;
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*! set the current encoding for the OpenMath objects of this call
 @param client (inout) scscp client
 @param encodingtype (out) current encoding
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
static int SCSCP_co_set_encodingtype(SCSCP_calloptions* options, SCSCP_encodingtype encodingtype, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   if (res) 
   {
    (*options)->m_encodingtype = encodingtype;
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*! get the current encoding for the OpenMath objects of this call
 @param client (inout) scscp client
 @param encodingtype (out) current encoding
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_co_get_encodingtype(SCSCP_calloptions* options, SCSCP_encodingtype* encodingtype, SCSCP_status* status)
{
   int res = SCSCP_co_checkNULL(options, status);
   if (res) 
   {
    *encodingtype = (*options)->m_encodingtype;
   }
   return res;
}

/*-----------------------------------------------------------------*/
/*! read the header of the object from the stream
   @param stream (inout) output stream
   @param option (inout) options
   @param iter (out) iterator on the stream
   @param status (inout) status information 
*/ 
/*-----------------------------------------------------------------*/
int SCSCP_procedurecall_read(SCSCP_io *stream, SCSCP_calloptions options, SCSCP_xmlnodeptr* iter, 
                             SCSCP_status* status)
{
 int res = 1;

 SCSCP_debugprint("SCSCP_procedurecall_read(%p) - enter\n", stream);

 res = SCSCP_io_readxmldoc(stream, iter, status);
 if (res)
 {
  SCSCP_debugprint("SCSCP_procedurecall_read() - ReadXmlDoc returns 1\n");

  res = SCSCP_xmlnode_readbeginOMOBJ(iter);
  
  // parse attributes
  if (options)
  {
   SCSCP_co_set_encodingtype(&options, (*stream)->m_recvencodingtype, status);
   SCSCP_co_set_returntype(&options, SCSCP_option_return_object, status);
   SCSCP_co_set_callid(&options,"N/A", status);
   res &= SCSCP_co_readoptions(stream, options, iter, status);
  } 
  //parse the name of the procedure
  res &= SCSCP_xmlnode_readbeginOMA(iter);
  if (res)
  {
   res = SCSCP_xmlnode_readcheckOMS(iter, SCSCP_TAGS_cdname, SCSCP_TAGS_procedure_call);
  }
 }
 else
 {
  *iter = NULL;
 }
 SCSCP_debugprint("SCSCP_procedurecall_read() - returns %d\n", res);
 return res;
}


/*-----------------------------------------------------------------*/
/*! send the beginning of the procedure call to the SCSCP server with some options 
 until the arguments of the procedure call
 @param stream (inout) scscp stream
 @param options (in) options of the call
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_procedurecall_write_start(SCSCP_io *stream, SCSCP_calloptions options, SCSCP_status* status)
{
 int res;
 SCSCP_debugprint("SCSCP_procedurecall_write_start(%p) - enter\n", stream);
   
   res = SCSCP_io_writeSCSCPSTART(stream, status);
   res &= SCSCP_io_writebeginOMOBJ(stream, status);
   res &= SCSCP_io_writebeginOMATTR(stream, NULL, status);
   
   
   //write the options
   if (options)
   {
      res &= SCSCP_co_write(stream, options, status);
   }
   else if (res)
   { /* generate a unique call ID and the call returns nothing */
      SCSCP_calloptions localoptions;
      res = SCSCP_co_init(&localoptions, status);
      if (res) res = SCSCP_co_set_returntype(&localoptions, SCSCP_option_return_nothing,status);
      if (res) res = SCSCP_co_write(stream, localoptions, status);
      SCSCP_co_clear(&localoptions, status);
   }
   res &= SCSCP_io_writebeginOMA(stream, NULL, status);
   
   //write procedure name
   res &= SCSCP_io_writeOMS(stream, SCSCP_TAGS_cdname, SCSCP_TAGS_procedure_call, NULL, status);
   
   SCSCP_debugprint("SCSCP_procedurecall_write_start() - return %d\n", res);
   return res;
}

/*-----------------------------------------------------------------*/
/*! send the end of the procedure call to the SCSCP server : 
  when all is ok !
 @param stream (inout) scscp stream
 @param options (in) options of the call
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_procedurecall_write_finish(SCSCP_io *stream, SCSCP_status* status)
{
 int res;
      
      res = SCSCP_io_writeendOMA(stream, status); 
      res &= SCSCP_io_writeendOMATTR(stream, status);
      res &= SCSCP_io_writeendOMOBJ(stream, status);
      res &= SCSCP_io_writeSCSCPEND(stream, status);
 SCSCP_debugprint("SCSCP_procedurecall_write_finish() - return %d\n", res);
 return res;
}

/*-----------------------------------------------------------------*/
/*! send the end of the procedure call to the SCSCP server : 
  when the procedure call is cancelled
 @param stream (inout) scscp stream
 @param options (in) options of the call
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_procedurecall_write_cancel(SCSCP_io *stream, SCSCP_status* status)
{
 return SCSCP_io_writeSCSCPCANCEL(stream, status);
}

/*-----------------------------------------------------------------*/
/*! send a procedure call to the SCSCP server with some options 
    The parameter of the procedure call are written 
    by the callback function "callbackwriteargs"
 @param stream (inout) scscp stream
 @param options (in) options of the call
 @param callbackwriteargs (in) callback function 
             to write the parameters of the procedure call
             can be NULL.
 @param param (inout) opaque data given to callbackwriteargs.
               can be NULL.
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_procedurecall_write(SCSCP_io *stream, SCSCP_calloptions options, 
                          int (*callbackwriteargs)(SCSCP_io* client, void *param, SCSCP_status* status),
                          void *param, SCSCP_status* status)
{
 int res;
 SCSCP_debugprint("SCSCP_procedurecall_write(%p) - enter\n", stream);
   
   res = SCSCP_procedurecall_write_start(stream, options, status);
   if (res && callbackwriteargs!=NULL) res = callbackwriteargs(stream, param, status);
 
   if (res)
   {
      res = SCSCP_procedurecall_write_finish(stream, status);
   }
   else
   { /*error => generate a cancel to the server */
      res = SCSCP_procedurecall_write_cancel(stream, status);
   }
   
 SCSCP_debugprint("SCSCP_procedurecall_write() - returns %d\n", res);
 return res;
}

/*-----------------------------------------------------------------*/
/*!  read the attribute of the answer for the procedure call 
    and the type of the answer message 
 @param client (inout) scscp client
 @param options (inout) options of the procedure return
 @param msgtype (out) type of returned message
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_sc_callrecvheader(SCSCP_socketclient* client, SCSCP_returnoptions* options,
                            SCSCP_msgtype *msgtype, SCSCP_status* status)
{
 int res;
 SCSCP_debugprint("SCSCP_sc_callrecvheader() - start\n");
 res = SCSCP_sc_checkNULL(client, status);
 if (res)
 {
    char bufferPI[SCSCP_PI_MAXLENBUFFER];
    /* lecture du nouveau PI */
    res = SCSCP_io_ReadPI(client, msgtype, bufferPI, status);
    /* read the header of the procedure call */
    if (res && *msgtype == SCSCP_msgtype_ProcedureCall)
    { 
     res = SCSCP_ro_read(client, options==SCSCP_RETURNOPTIONS_IGNORE?NULL:*options,
                         SCSCP_sc_getxmlnodeptr(client, status), msgtype, status);
    } 
 }

 SCSCP_debugprint("SCSCP_sc_callrecvheader(%d) - returns %d\n",SCSCP_status_is(status), res);
 return res;
}                            

/*-----------------------------------------------------------------*/
/*! read the answer of the procedure call and store it in a string buffer 
 @param client (inout) scscp client
 @param options (inout) options of the procedure return
 @param msgtype (out) type of returned message
 @param openmathbuffer (out) openmath buffer. 
                 This buffer must be freed with 'free'
 @param status (inout) status error
*/
/*-----------------------------------------------------------------*/
int SCSCP_sc_callrecvstr(SCSCP_socketclient* client, SCSCP_returnoptions* options,
                         SCSCP_msgtype *msgtype, char **openmathbuffer, SCSCP_status* status)
{
 int res = SCSCP_sc_callrecvheader(client, options, msgtype, status);
 if (res)
 {
  *openmathbuffer = SCSCP_sc_getxmlnoderawstring(client, SCSCP_sc_getxmlnode(client, status), status);
 }
 return res;
}


