/* CSL - Common Sound Layer
 * Copyright (C) 2000-2001 Stefan Westerfeld and Tim Janik
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 */
#include	"artsmcopdispatcher.h"

#include	"artssoundserver.h"
#include	<stdlib.h>
#include	<unistd.h>
#include	<assert.h>
#include	<stdio.h>
#include	<string.h>
#include	<time.h>
#include	"artsbinbuffer.h"
#include	"artsmcopglobalcomm.h"
#include	"artsmd5.h"
#include	"artsmcopmethods.h"
#include	"csldefs.h"


#ifndef	MAXHOSTNAMELEN
#define	MAXHOSTNAMELEN 64
#endif

enum
{
  MCOP_OBJECT_LOOKUP_METHOD,            /* long		_lookupMethod	(MethodDef methodDef);	*/
  MCOP_OBJECT_INTERFACE_NAME,           /* string	_interfaceName	();			*/
  MCOP_OBJECT_QUERY_INTERFACE,          /* InterfaceDef	_queryInterface	(string	   name);	*/
  MCOP_OBJECT_QUERY_TYPE,               /* TypeDef	_queryType	(string	   name);	*/
  MCOP_OBJECT_QUERY_ENUM                /* EnumDef	_queryEnum	(string	   name);	*/
};

enum {	  /* dispatcher states */
  DOWN,
  WAIT_SERVER_HELLO,
  WAIT_AUTH_ACCEPT,
  CONNECTED
};

#define	REQUEST_FREE(req)			(!(req)->pending && !(req)->completed)
#define	REQUEST_RESULT(dispatcher, request_id)	(&(dispatcher)->requests[(request_id)].result)


/* --- prototypes -- */
static void	_arts_mcop_dispatcher_process_message	(ArtsMcopDispatcher	*dispatcher);
static int	_arts_mcop_dispatcher_send_lookup_method	(ArtsMcopDispatcher	*dispatcher,
								 int			 object_id,
								 const char		*hex_method_def);
static int	dispatcher_setup_invocation		(ArtsMcopDispatcher	*dispatcher,
							 ArtsBinBuffer		*buffer,
							 int			 object_id,
							 int			 method_id,
							 ArtsMcopResultType	 result_type);
static CslBool	dispatcher_invoke			(ArtsMcopDispatcher	*dispatcher,
							 ArtsBinBuffer		*buffer,
							 unsigned int		 request_id);


/* --- functions --- */
ArtsMcopDispatcher*
_arts_mcop_dispatcher_new (const char *server_spec,
			   const char *cookie_file)
{
  ArtsMcopDispatcher *dispatcher = csl_new0 (ArtsMcopDispatcher, 1);
  char *tmp, *string, *dom;
  int i;
  
  /* _arts_csl_adapter_sample_wait_done can take virtually forever, so no
   * 1000ms timeout here:
   * dispatcher->timeout = 1000; */
  dispatcher->timeout = -1;
  
  /* find a socket to connect to server */
  if (!dispatcher->csock && server_spec)
    dispatcher->csock = _arts_socket_new_tcp (server_spec);
  if (!dispatcher->csock)
    {
      char *server = _arts_mcop_global_comm_get_tmp ("Arts_SoundServer");
      
      if (server)
	{
	  ArtsMcopObjectReference *oref = _arts_mcop_object_reference_from_hexurl (server);
	  
	  if (oref)
	    {
	      for (i = 0; i < oref->n_urls && !dispatcher->csock; i++)
		if (strncmp (oref->urls[i], "unix:", 5) == 0)
		  dispatcher->csock = _arts_socket_new_unix (oref->urls[i] + 5);
		else if (strncmp (oref->urls[i], "tcp:", 4) == 0)
		  dispatcher->csock = _arts_socket_new_tcp (oref->urls[i] + 4);
	      _arts_mcop_object_reference_free (oref);
	    }
	}
      csl_free (server);
    }
  if (!dispatcher->csock)
    {
      csl_free (dispatcher);
      return NULL;
    }
  
  /* setup servername and patch message length field into socket */
  dispatcher->csock->msg_length_offset = 4;
  tmp = csl_malloc (MAXHOSTNAMELEN + 1);
  tmp[0] = 0;
  gethostname (tmp, MAXHOSTNAMELEN);
  dom = csl_malloc (MAXHOSTNAMELEN + 1);
  dom[0] = 0;
  getdomainname (dom, MAXHOSTNAMELEN);
  i = 32 + 32 + 32 + MAXHOSTNAMELEN + MAXHOSTNAMELEN + 10;
  string = csl_malloc (i);
  snprintf (string, i, "CSL-%u.%lu+%lu@%s.%s", getpid (), time (0), _arts_md5_auth_mkrand (), tmp, dom);
  csl_free (tmp);
  csl_free (dom);
  dispatcher->csl_server_name = csl_strdup (string);
  csl_free (string);
  
  /* exchange server/client hello and authenticate */
  if (cookie_file)
    dispatcher->cookie_file = csl_strdup (cookie_file);
  dispatcher->state = WAIT_SERVER_HELLO;
  i = 3000;
  while (dispatcher->state != CONNECTED && dispatcher->state != DOWN && i)
    if (_arts_socket_wait_for_msg (dispatcher->csock, &i))
      _arts_mcop_dispatcher_process_message (dispatcher);
  
  /* lookup methods on global comm object */
  if (dispatcher->state == CONNECTED)
    {
      int request_id = _arts_mcop_dispatcher_send_lookup_method (dispatcher,
								 dispatcher->global_comm_ref->object_id,
								 CSL_Arts_GlobalComm_get);
      
      if (!_arts_mcop_dispatcher_wait_for_result (dispatcher, request_id) ||
	  dispatcher->requests[request_id].result_broken)
	dispatcher->state = DOWN;
      else
	dispatcher->global_comm__get = dispatcher->requests[request_id].result.v_long;
      _arts_mcop_dispatcher_free_request (dispatcher, request_id);
    }
  if (dispatcher->state == CONNECTED)	// FIXME: just a test
    {
      int method_id = _arts_mcop_dispatcher_lookup_hex_method (dispatcher,
							       dispatcher->global_comm_ref->object_id,
							       CSL_Arts_GlobalComm_get);
      if (method_id != dispatcher->global_comm__get)
	dispatcher->state = DOWN;
    }
  
  /* retrive Arts_SoundServer from GlobalComm */
  if (dispatcher->state == CONNECTED)
    {
      char *hex_string = _arts_mcop_dispatcher_global_comm_get (dispatcher, "Arts_SoundServer");
      
      if (hex_string)
	dispatcher->arts_sound_server = _arts_mcop_object_reference_from_hexurl (hex_string);
      csl_free (hex_string);
      if (!dispatcher->arts_sound_server)
	dispatcher->state = DOWN;
    }
  
  if (dispatcher->state != CONNECTED)
    {
      _arts_mcop_dispatcher_destroy (dispatcher);
      dispatcher = NULL;
    }
  
  return dispatcher;
}

void
_arts_mcop_dispatcher_destroy (ArtsMcopDispatcher *dispatcher)
{
  unsigned int i;
  
  csl_return_if_fail (dispatcher != NULL);
  
  for (i = 0; i < dispatcher->n_requests; i++)
    if (!REQUEST_FREE (dispatcher->requests + i))
      _arts_mcop_dispatcher_free_request (dispatcher, i);
  
  _arts_socket_free (dispatcher->csock);
  csl_free (dispatcher->csl_server_name);
  csl_free (dispatcher->arts_server_name);
  csl_free (dispatcher->cookie_file);
  if (dispatcher->interface_repo_ref)
    _arts_mcop_object_reference_free (dispatcher->interface_repo_ref);
  if (dispatcher->global_comm_ref)
    _arts_mcop_object_reference_free (dispatcher->global_comm_ref);
  if (dispatcher->arts_sound_server)
    _arts_mcop_object_reference_free (dispatcher->arts_sound_server);
  csl_free (dispatcher->requests);
  csl_free (dispatcher);
}

static void
send_msg_buffer_patched (ArtsMcopDispatcher *dispatcher,
			 ArtsBinBuffer      *buffer)
{
  int buffer_magic, *p_int, length;
  
  csl_return_if_fail (buffer->bound - buffer->bytes > 12);
  
  p_int = (int*) buffer->bytes;
  buffer_magic = ntohl (*p_int);
  
  csl_return_if_fail (buffer_magic == ARTS_MCOP_MAGIC);
  
  p_int += 1;
  length = buffer->bp.cur - buffer->bytes;
  *p_int = htonl (length);
  
  _arts_socket_write (dispatcher->csock, length, buffer->bytes);
}

unsigned int
_arts_mcop_dispatcher_alloc_request (ArtsMcopDispatcher *dispatcher,
				     ArtsMcopResultType  result_type)
{
  unsigned int i;
  
  for (i = 0; i < dispatcher->n_requests; i++)
    if (REQUEST_FREE (dispatcher->requests + i))
      break;
  if (i >= dispatcher->n_requests)
    {
      i = dispatcher->n_requests++;
      dispatcher->requests = csl_realloc (dispatcher->requests, dispatcher->n_requests * sizeof (dispatcher->requests[0]));
    }
  dispatcher->requests[i].result_type = result_type;
  dispatcher->requests[i].pending = TRUE;
  dispatcher->requests[i].completed = FALSE;
  dispatcher->requests[i].result_broken = FALSE;
  memset (&dispatcher->requests[i].result, 0, sizeof (dispatcher->requests[i].result));
  return i;
}

void
_arts_mcop_dispatcher_free_request (ArtsMcopDispatcher *dispatcher,
				    unsigned int       request_id)
{
  ArtsMcopRequest *request = dispatcher->requests + request_id;
  
  csl_return_if_fail (request_id < dispatcher->n_requests);
  csl_return_if_fail (!REQUEST_FREE (request));
  
  switch (request->result_type)
    {
    case ARTS_MCOP_RESULT_STRING:
      csl_free (request->result.v_string);
      break;
    case ARTS_MCOP_RESULT_STRING_SEQ:
      csl_strfreevn (request->result.v_string_seq.n_items, request->result.v_string_seq.items);
      break;
    case ARTS_MCOP_RESULT_BYTE_SEQ:
      csl_free (request->result.v_byte_seq.bytes);
      break;
    case ARTS_MCOP_RESULT_OBJECT:
      if (request->result.v_object)
	_arts_mcop_object_reference_free (request->result.v_object);
      break;
    case ARTS_MCOP_RESULT_CUSTOM:
      if (request->result.v_custom.free != NULL && request->result.v_custom.data != NULL)
	request->result.v_custom.free (request->result.v_custom.data);
      break;
     default:
      break;
    }
  request->result_type = ARTS_MCOP_RESULT_VOID;
  request->pending = FALSE;
  request->completed = FALSE;
}

static void
_arts_mcop_dispatcher_process_message (ArtsMcopDispatcher *dispatcher)
{
  void *message_data;
  int message_length;
  ArtsBinBuffer msg_buffer = { 0, };
  ArtsMcopMessage *msg;
  
  message_data = _arts_socket_peek_msg (dispatcher->csock, &message_length);
  if (!message_data)
    return;
  
  _arts_bin_buffer_setup_readable (&msg_buffer, message_length, message_data);
  
  switch (dispatcher->state)
    {
      int i;
    case CONNECTED:
      msg = _arts_mcop_message_demarshal (&msg_buffer);
      if (msg)	/* process results */
	{
	  switch (msg->type)
	    {
	      int request_id;
	    case ARTS_MCOP_RETURN:
	      request_id = msg->body.return_body.request_id;
	      if (request_id >= dispatcher->n_requests || !dispatcher->requests[request_id].pending)
		csl_warning ("got bogus request response (request_id:%d)", request_id);
	      else
		{
		  dispatcher->requests[request_id].pending = FALSE;
		  switch (dispatcher->requests[request_id].result_type)
		    {
		    case ARTS_MCOP_RESULT_VOID:
		      break;
		    case ARTS_MCOP_RESULT_LONG:
		      dispatcher->requests[request_id].result.v_long = _arts_bin_buffer_get_int (&msg_buffer);
		      break;
		    case ARTS_MCOP_RESULT_FLOAT:
		      dispatcher->requests[request_id].result.v_float = _arts_bin_buffer_get_float (&msg_buffer);
		      break;
		    case ARTS_MCOP_RESULT_BYTE:
		      dispatcher->requests[request_id].result.v_byte = _arts_bin_buffer_get_byte (&msg_buffer);
		      break;
		    case ARTS_MCOP_RESULT_BYTE_SEQ:
		      dispatcher->requests[request_id].result.v_byte_seq.bytes = 
			_arts_bin_buffer_get_bytes (&msg_buffer,
						    &dispatcher->requests[request_id].result.v_byte_seq.n_bytes);
		      break;
		    case ARTS_MCOP_RESULT_BOOLEAN:
		      dispatcher->requests[request_id].result.v_boolean = _arts_bin_buffer_get_byte (&msg_buffer);
		      break;
		    case ARTS_MCOP_RESULT_STRING:
		      dispatcher->requests[request_id].result.v_string = _arts_bin_buffer_get_string (&msg_buffer);
		      break;
		    case ARTS_MCOP_RESULT_STRING_SEQ:
		      dispatcher->requests[request_id].result.v_string_seq.items =
			_arts_bin_buffer_get_string_seq (&msg_buffer,
							 &dispatcher->requests[request_id].result.v_string_seq.n_items);
		      break;
		    case ARTS_MCOP_RESULT_OBJECT:
		      dispatcher->requests[request_id].result.v_object = _arts_mcop_object_reference_demarshal (&msg_buffer);
		      break;
		    case ARTS_MCOP_RESULT_CUSTOM:
		      dispatcher->requests[request_id].result.v_custom.data =
			dispatcher->requests[request_id].result.v_custom.demarshal (&msg_buffer);
		      break;
		    }
		  dispatcher->requests[request_id].completed = TRUE;
		  dispatcher->requests[request_id].result_broken = msg_buffer.error;
		}
	      break;
	    default:
	      csl_warning ("got bogus message (msg_type:%d)", msg->type);
	      break;
	    }
	  _arts_mcop_message_free (msg);
	}
      break;
    case WAIT_AUTH_ACCEPT:
      msg = _arts_mcop_message_demarshal (&msg_buffer);
      if (msg)
	{
          CslBool error = msg->type != ARTS_MCOP_AUTH_ACCEPT;
	  
	  if (!error)
	    {
	      for (i = 0; i < msg->body.auth_accept.n_hints; i++)
		if (msg->body.auth_accept.hints[i] && !dispatcher->interface_repo_ref &&
		    strncmp (msg->body.auth_accept.hints[i], "InterfaceRepo=", 14) == 0)
		  dispatcher->interface_repo_ref = _arts_mcop_object_reference_from_hexurl (msg->body.auth_accept.hints[i] + 14);
		else if (msg->body.auth_accept.hints[i] && !dispatcher->global_comm_ref &&
			 strncmp (msg->body.auth_accept.hints[i], "GlobalComm=", 11) == 0)
		  dispatcher->global_comm_ref = _arts_mcop_object_reference_from_hexurl (msg->body.auth_accept.hints[i] + 11);
	      error = !dispatcher->interface_repo_ref || !dispatcher->global_comm_ref;
	    }
	  dispatcher->state = error ? DOWN : CONNECTED;
	  _arts_mcop_message_free (msg);
	}
      break;
    case WAIT_SERVER_HELLO:
      msg = _arts_mcop_message_demarshal (&msg_buffer);
      if (msg)
	{
	  CslBool error = msg->type != ARTS_MCOP_SERVER_HELLO;
	  CslBool need_md5 = TRUE;
	  
	  if (!error)
	    dispatcher->arts_server_name = csl_strdup (msg->body.server_hello.server_id);
	  if (!error && msg->body.server_hello.n_auth_protocols > 0)
	    {
	      error = TRUE;
	      for (i = 0; i < msg->body.server_hello.n_auth_protocols; i++)
		if (strcmp (msg->body.server_hello.auth_protocols[i], "md5auth") == 0)
		  error = FALSE;
		else if (strcmp (msg->body.server_hello.auth_protocols[i], "noauth") == 0)
		  {
		    error = FALSE;
		    need_md5 = FALSE;
		  }
	    }
	  
	  if (!error)
	    {
	      ArtsMcopMessage response = { ARTS_MCOP_MAGIC, 0, ARTS_MCOP_CLIENT_HELLO, };
	      ArtsBinBuffer buffer;
	      
	      response.body.client_hello.server_id = dispatcher->csl_server_name;
	      response.body.client_hello.auth_protocol = "noauth";
	      response.body.client_hello.auth_data = NULL;
	      if (need_md5)
		{
		  char tmp_cookie[ARTS_MD5_COOKIE_LEN + 1];
		  
		  response.body.client_hello.auth_protocol = "md5auth";
		  if (dispatcher->cookie_file && _arts_md5_load_cookie (dispatcher->cookie_file, tmp_cookie))
		    {
		      _arts_md5_auth_set_cookie (tmp_cookie);
		      response.body.client_hello.auth_data = _arts_md5_auth_mangle (msg->body.server_hello.auth_seed);
		      memset (tmp_cookie, 0, sizeof (tmp_cookie));
		    }
		  else
		    {
		      char *cookie = _arts_mcop_global_comm_get_tmp ("secret-cookie");
		      
		      if (cookie && strlen (cookie) == ARTS_MD5_COOKIE_LEN)
			{
			  _arts_md5_auth_set_cookie (cookie);
			  response.body.client_hello.auth_data = _arts_md5_auth_mangle (msg->body.server_hello.auth_seed);
			  memset (cookie, 0, ARTS_MD5_COOKIE_LEN);
			}
		      csl_free (cookie);
		    }
		}
	      _arts_bin_buffer_setup_writable (&buffer, 256);
	      _arts_mcop_message_marshal (&response, &buffer);
	      csl_free (response.body.client_hello.auth_data);
	      
	      send_msg_buffer_patched (dispatcher, &buffer);
	      _arts_bin_buffer_free_writable (&buffer);
	      dispatcher->state = WAIT_AUTH_ACCEPT;
	    }
	  else
	    {
	      printf ("unable to authorize to server\n");
	      /* g_conn_close (conx); FIXME */ 
	    }
	  _arts_mcop_message_free (msg);
	}
      else
	printf ("got some bytes of invalid non-message junk\n"); // FIXME
      break;
    case DOWN:
    default:
      printf ("got data from nirvana\n"); // FIXME
    }
  _arts_socket_skip_msg (dispatcher->csock, message_length);
}

CslBool
_arts_mcop_dispatcher_wait_for_result (ArtsMcopDispatcher *dispatcher,
				       unsigned int       request_id)
{
  int milliseconds;
  
  csl_return_val_if_fail (dispatcher != NULL, FALSE);
  csl_return_val_if_fail (request_id < dispatcher->n_requests, FALSE);
  csl_return_val_if_fail (!REQUEST_FREE (dispatcher->requests + request_id), FALSE);
  
  milliseconds = dispatcher->timeout;
  
  if (dispatcher->state == CONNECTED && dispatcher->requests[request_id].pending)
    do
      {
	if (_arts_socket_wait_for_msg (dispatcher->csock, &milliseconds))
	  _arts_mcop_dispatcher_process_message (dispatcher);
      }
    while (dispatcher->state == CONNECTED &&
	   dispatcher->requests[request_id].pending &&
	   (milliseconds == -1 || milliseconds > 0));
  
  return dispatcher->requests[request_id].completed;
}

static int
_arts_mcop_dispatcher_send_lookup_method (ArtsMcopDispatcher *dispatcher,
					  int                object_id,
					  const char        *hex_method_def)
{
  ArtsMcopMessage msg = { ARTS_MCOP_MAGIC, 0, ARTS_MCOP_INVOCATION, };
  ArtsBinBuffer buffer;
  int request_id = _arts_mcop_dispatcher_alloc_request (dispatcher, ARTS_MCOP_RESULT_LONG);
  
  msg.body.invocation.object_id = object_id;
  msg.body.invocation.method_id = MCOP_OBJECT_LOOKUP_METHOD;
  msg.body.invocation.request_id = request_id;
  
  _arts_bin_buffer_setup_writable (&buffer, 0);
  _arts_mcop_message_marshal (&msg, &buffer);
  _arts_bin_buffer_put_hex (&buffer, hex_method_def);
  
  send_msg_buffer_patched (dispatcher, &buffer);
  _arts_bin_buffer_free_writable (&buffer);
  
  return request_id;
}

static int
dispatcher_setup_invocation (ArtsMcopDispatcher *dispatcher,
			     ArtsBinBuffer      *buffer,
			     int                object_id,
			     int		method_id,
			     ArtsMcopResultType  result_type)
{
  ArtsMcopMessage msg;
  
  msg.mcop_magic = ARTS_MCOP_MAGIC;
  msg.message_length = 0;	/* to be patched */
  msg.type = ARTS_MCOP_INVOCATION;
  msg.body.invocation.object_id = object_id;
  msg.body.invocation.method_id = method_id;
  msg.body.invocation.request_id = _arts_mcop_dispatcher_alloc_request (dispatcher, result_type);
  
  _arts_bin_buffer_setup_writable (buffer, 0);
  _arts_mcop_message_marshal (&msg, buffer);
  
  return msg.body.invocation.request_id;
}

static CslBool
dispatcher_invoke (ArtsMcopDispatcher *dispatcher,
		   ArtsBinBuffer	     *buffer,
		   unsigned int	      request_id)
{
  send_msg_buffer_patched (dispatcher, buffer);
  _arts_bin_buffer_free_writable (buffer);
  
  if (!_arts_mcop_dispatcher_wait_for_result (dispatcher, request_id) ||
      dispatcher->requests[request_id].result_broken)
    return FALSE;
  else
    return TRUE;
}

/* TODO: is it ok to globalize these? */
CslBool
_arts_mcop_dispatcher_invoke (ArtsMcopDispatcher *dispatcher,
			      ArtsBinBuffer	     *buffer,
			      unsigned int	      request_id)
{
  return dispatcher_invoke(dispatcher,buffer,request_id);
}

int
_arts_mcop_dispatcher_setup_invocation(ArtsMcopDispatcher *dispatcher,
				       ArtsBinBuffer      *buffer,
				       int                object_id,
				       int		method_id,
				       ArtsMcopResultType  result_type)
{
  return dispatcher_setup_invocation(dispatcher,buffer,object_id,method_id,result_type); 
}

void
_arts_mcop_dispatcher_setup_oneway_invocation (ArtsMcopDispatcher      *dispatcher,
					       ArtsBinBuffer           *buffer,
					       int                     object_id,
					       int		      method_id)
{
  ArtsMcopMessage msg;
  
  msg.mcop_magic = ARTS_MCOP_MAGIC;
  msg.message_length = 0;	/* to be patched */
  msg.type = ARTS_MCOP_ONEWAY_INVOCATION;
  msg.body.oneway_invocation.object_id = object_id;
  msg.body.oneway_invocation.method_id = method_id;
  
  _arts_bin_buffer_setup_writable (buffer, 0);
  _arts_mcop_message_marshal (&msg, buffer);
}

void	
_arts_mcop_dispatcher_oneway_invoke (ArtsMcopDispatcher      *dispatcher,
				     ArtsBinBuffer	        *buffer)
{
  send_msg_buffer_patched (dispatcher, buffer);
  _arts_bin_buffer_free_writable (buffer);
}


char*
_arts_mcop_dispatcher_global_comm_get (ArtsMcopDispatcher *dispatcher,
				       const char        *var_name)
{
  ArtsBinBuffer buffer;
  unsigned int request_id;
  char *result;
  
  csl_return_val_if_fail (dispatcher != NULL, NULL);
  csl_return_val_if_fail (var_name != NULL, NULL);
  
  /* setup method header and return type */
  request_id = dispatcher_setup_invocation (dispatcher, &buffer,
					    dispatcher->global_comm_ref->object_id,
					    dispatcher->global_comm__get,
					    ARTS_MCOP_RESULT_STRING);
  /* marshal method args */
  _arts_bin_buffer_put_string (&buffer, var_name);
  /* do invocation, fetch return value */
  if (dispatcher_invoke (dispatcher, &buffer, request_id))
    result = csl_strdup (REQUEST_RESULT (dispatcher, request_id)->v_string);
  else
    result = NULL;
  /* free request slot */
  _arts_mcop_dispatcher_free_request (dispatcher, request_id);
  
  return result;
}

int
_arts_mcop_dispatcher_lookup_hex_method (ArtsMcopDispatcher *dispatcher,
					 int                object_id,
					 const char        *hex_method_def)
{
  ArtsBinBuffer buffer;
  unsigned int request_id;
  int result;
  
  csl_return_val_if_fail (dispatcher != NULL, 0);
  csl_return_val_if_fail (hex_method_def != NULL, 0);
  
  /* method header and return type */
  request_id = dispatcher_setup_invocation (dispatcher, &buffer,
					    object_id,
					    MCOP_OBJECT_LOOKUP_METHOD,
					    ARTS_MCOP_RESULT_LONG);
  /* method args */
  _arts_bin_buffer_put_hex (&buffer, hex_method_def);
  /* do invocation */
  if (dispatcher_invoke (dispatcher, &buffer, request_id))
    result = REQUEST_RESULT (dispatcher, request_id)->v_long;
  else
    result = 0;
  /* free request slot */
  _arts_mcop_dispatcher_free_request (dispatcher, request_id);
  
  return result;
}

void
_arts_mcop_object_use_remote (ArtsMcopObject *object)
{
  ArtsBinBuffer buffer;
  unsigned int request_id;
  
  csl_return_if_fail (object != NULL);
  
  if(!object->method_use_remote)
    {
      object->method_use_remote = _arts_mcop_dispatcher_lookup_hex_method (object->dispatcher,
									   object->object_id, CSL_Arts_Object__useRemote);
    }
  
  /* method header and return type */
  request_id = _arts_mcop_dispatcher_setup_invocation (object->dispatcher, &buffer,
						       object->object_id,
						       object->method_use_remote,
						       ARTS_MCOP_RESULT_VOID);
  /* do invocation */
  _arts_mcop_dispatcher_invoke (object->dispatcher, &buffer, request_id);
  
  /* free request slot */
  _arts_mcop_dispatcher_free_request (object->dispatcher, request_id);
}

void
_arts_mcop_object_release_remote (ArtsMcopObject *object)
{
  ArtsBinBuffer buffer;
  unsigned int request_id;
  
  csl_return_if_fail (object != NULL);
  
  if (!object->method_release_remote)
    {
      object->method_release_remote = _arts_mcop_dispatcher_lookup_hex_method (object->dispatcher,
									       object->object_id, CSL_Arts_Object__releaseRemote);
    }
  
  /* method header and return type */
  request_id = _arts_mcop_dispatcher_setup_invocation (object->dispatcher, &buffer,
						       object->object_id,
						       object->method_release_remote,
						       ARTS_MCOP_RESULT_VOID);
  /* do invocation */
  _arts_mcop_dispatcher_invoke (object->dispatcher, &buffer, request_id);
  
  /* free request slot */
  _arts_mcop_dispatcher_free_request (object->dispatcher, request_id);
}

void
_arts_mcop_object_copy_remote (ArtsMcopObject *object)
{
  ArtsBinBuffer buffer;
  unsigned int request_id;
  
  csl_return_if_fail (object != NULL);
  
  if (!object->method_copy_remote)
    {
      object->method_copy_remote = _arts_mcop_dispatcher_lookup_hex_method (object->dispatcher,
									    object->object_id, CSL_Arts_Object__copyRemote);
    }
  
  /* method header and return type */
  request_id = _arts_mcop_dispatcher_setup_invocation (object->dispatcher, &buffer,
						       object->object_id,
						       object->method_copy_remote,
						       ARTS_MCOP_RESULT_VOID);
  /* do invocation */
  _arts_mcop_dispatcher_invoke (object->dispatcher, &buffer, request_id);
  
  /* free request slot */
  _arts_mcop_dispatcher_free_request (object->dispatcher, request_id);
}

/* vim:ts=8:sw=2:sts=2
 */
