/* libsizzle/cvar.c - C variable wrapper
 
  Copyright (C) 1999, 2000 Martin Grabmueller <mgrabmue@cs.tu-berlin.de>
 
  This is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2, or (at your option)
  any later version.
  
  This software 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 General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this package; see the file COPYING.  If not, write to
  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  Boston, MA 02111-1307, USA.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif 

#include <assert.h>
#include <string.h>

#if ENABLE_NLS
# include <libintl.h>
# define _(String) gettext(String)
#else
# define _(String) (String)
#endif

#include "alloc.h"
#include "node.h"
#include "port.h"
#include "eval.h"
#include "vector.h"
#include "cvar.h"

unsigned long zzz_tagged_type_magic_var;

static zzz_scm_t
make_magic_var (void * data, 
		int (* handler) (int, void *, zzz_scm_t *),
		int read_only)
{
  return zzz_create_tagged_cell (read_only ?
				 zzz_tagged_type_magic_var | tagged_info_ro:
				 zzz_tagged_type_magic_var, data,
				 (void *) handler, NULL);
}



static void *
magic_var_constructor (zzz_tagged_t tagged, void * data0,
		       void * data1, void * data2)
{
  tagged->fill[1] = (zzz_object_t) data0;
  tagged->fill[2] = (zzz_object_t) data1;
  tagged->fill[3] = (zzz_object_t) data2;
  return tagged;
}

static void
magic_var_print (zzz_scm_t port, zzz_tagged_t tagged, zzz_print_state_t state)
{
  abort ();			/* This must not be called! */
}

static void
magic_var_mark (zzz_tagged_t tagged)
{
  zzz_scm_t temp;
  magic_var_handler (tagged) (MAGIC_MODE_MARK,
			      magic_var_data (tagged),
			      &temp);
}

static void
magic_var_free (zzz_tagged_t tagged)
{
  zzz_scm_t temp;
  magic_var_handler (tagged) (MAGIC_MODE_FREE,
			      magic_var_data (tagged),
			      &temp);
}



static int
int_var_handler (int mode, void * data, zzz_scm_t * value)
{
  switch (mode)
    {
    case MAGIC_MODE_SET:
      if (!integer_p (*value))
	{
	  *value = zzz_make_error
	    (zzz_make_string (_("integer value expected"), -1),
	     NULL);
	  return 1;
	}
      *((int *) data) = integer_val (*value);
      break;
    case MAGIC_MODE_GET:
      *value = zzz_make_integer (*((int *) data));
      break;
    case MAGIC_MODE_MARK:
      break;
    case MAGIC_MODE_FREE:
      break;
    default:
      abort ();
      break;
    }
  return 0;
}

static int
bool_var_handler (int mode, void * data, zzz_scm_t * value)
{
  switch (mode)
    {
    case MAGIC_MODE_SET:
      if (!bool_p (*value))
	{
	  *value = zzz_make_error
	    (zzz_make_string (_("boolean value expected"), -1),
	     NULL);
	  return 1;
	}
      *((int *) data) = bool_val (*value);
      break;
    case MAGIC_MODE_GET:
      *value = zzz_make_bool (*((int *) data));
      break;
    case MAGIC_MODE_MARK:
      break;
    case MAGIC_MODE_FREE:
      break;
    default:
      abort ();
      break;
    }
  return 0;
}

static int
str_var_handler (int mode, void * data, zzz_scm_t * value)
{
  str_var_desc_t desc = data;
  switch (mode)
    {
    case MAGIC_MODE_SET:
      if (!string_p (*value))
	{
	  *value = zzz_make_error
	    (zzz_make_string (_("string value expected"), -1),
	     NULL);
	  return 1;
	}
      if ((int) string_len (*value) >= desc->size)
	{
	  *value = zzz_make_error
	    (zzz_make_string (_("string value too large"), -1),
	     NULL);
	  return 1;
	}
      memmove (desc->val, string_val (*value), string_len (*value));
      desc->val[string_len (*value)] = '\0';
      desc->len = string_len (*value);
      break;
    case MAGIC_MODE_GET:
      *value = zzz_make_ro_string (desc->val, desc->len);
      break;
    case MAGIC_MODE_MARK:
      break;
    case MAGIC_MODE_FREE:
      zzz_free (desc, sizeof (struct str_var_descriptor));
      break;
    default:
      abort ();
      break;
    }
  return 0;
}

static int
scm_var_handler (int mode, void * data, zzz_scm_t * value)
{
  switch (mode)
    {
    case MAGIC_MODE_SET:
      *((zzz_scm_t *) data) = *value;
      break;
    case MAGIC_MODE_GET:
      *value = *((zzz_scm_t *) data);
      break;
    case MAGIC_MODE_MARK:
      zzz_mark_cell (*((zzz_scm_t *) data));
      break;
    case MAGIC_MODE_FREE:
      break;
    default:
      abort ();
      break;
    }
  return 0;
}

/* Layout of procedure vector for closure variables.
   vec[0] = set-proc value
   vec[1] = get-proc
   vec[2] = finalize-proc
*/
static int
closure_var_handler (int mode, void * data, zzz_scm_t * value)
{
  zzz_scm_t vec = data;
  zzz_scm_t result;

  switch (mode)
    {
    case MAGIC_MODE_SET:
      if (zzz_apply (zzz_current_environment, zzz_vector_get (vec, 0),
		     zzz_cons (*value, NULL), &result) != RESULT_SUCCESS)
	{
	  *value = result;
	  return 1;
	}
      break;
    case MAGIC_MODE_GET:
      if (zzz_apply (zzz_current_environment, zzz_vector_get (vec, 1), NULL,
		     &result) != RESULT_SUCCESS)
	{
	  *value = result;
	  return 1;
	}
      *value = result;
      break;
    case MAGIC_MODE_MARK:
      zzz_mark_cell ((zzz_scm_t) data);
      break;
    case MAGIC_MODE_FREE:
      if (zzz_apply (zzz_current_environment,
		 zzz_vector_get (vec, 2), NULL,
		 &result) != RESULT_SUCCESS)
	{
	  *value = result;
	  return 1;
	}
      break;
    default:
      abort ();
      break;
    }
  return 0;
}

void
zzz_bind_int_variable (char * name, int * address, int read_only)
{
  zzz_scm_t sym, var;

  assert (name != NULL);
  assert (address != NULL);
  var = make_magic_var (address, int_var_handler, read_only);
  sym = zzz_make_symbol (name, -1);
  set_symbol_value (sym, var);
}

void
zzz_bind_bool_variable (char * name, int * address, int read_only)
{
  zzz_scm_t sym, var;

  assert (name != NULL);
  assert (address != NULL);
  var = make_magic_var (address, bool_var_handler, read_only);
  sym = zzz_make_symbol (name, -1);
  set_symbol_value (sym, var);
}

void
zzz_bind_string_variable (char * name, char * address, int size, int read_only)
{
  zzz_scm_t sym, var;
  str_var_desc_t desc;

  assert (name != NULL);
  assert (address != NULL);
  assert (size >= 0);
  desc = zzz_malloc (sizeof (struct str_var_descriptor));
  desc->val = address;
  desc->len = strlen (address);
  desc->size = size;
  var = make_magic_var (desc, str_var_handler, read_only);
  sym = zzz_make_symbol (name, -1);
  set_symbol_value (sym, var);
}

void
zzz_bind_scm_variable (char * name, zzz_scm_t * address, int read_only)
{
  zzz_scm_t sym, var;

  assert (name != NULL);
  assert (address != NULL);
  var = make_magic_var (address, scm_var_handler, read_only);
  sym = zzz_make_symbol (name, -1);
  set_symbol_value (sym, var);
}

void
zzz_bind_closure_variable (char * name, zzz_scm_t vec, int read_only)
{
  zzz_scm_t sym, var;

  assert (name != NULL);
  assert (vector_p (vec));
  var = make_magic_var (vec, closure_var_handler, read_only);
  sym = zzz_make_symbol (name, -1);
  set_symbol_value (sym, var);
}

void zzz_bind_magic_variable (char * name, void * data, 
			      int (* handler) (int mode, void *, zzz_scm_t *),
			      int read_only)
{
  zzz_scm_t sym, var;

  assert (name != NULL);
  assert (handler != NULL);
  var = make_magic_var (data, handler, read_only);
  sym = zzz_make_symbol (name, -1);
  set_symbol_value (sym, var);
}

result_t
zzz_init_cvar (void)
{
  zzz_tagged_type_magic_var = zzz_define_tagged_type (magic_var_constructor,
						      magic_var_print,
						      magic_var_free, 
						      magic_var_mark,
						      NULL);
  return RESULT_SUCCESS;
}
