/*
 * The builtin Number object.
 * Copyright (c) 1998 New Generation Software (NGS) Oy
 *
 * Author: Markku Rossi <mtr@ngs.fi>
 */

/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library 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
 */

/*
 * $Source: /tmp/cvsroot-15-2-2007/clamav-devel/libclamav/js/b_number.c,v $
 * $Id: b_number.c,v 1.2 2006/10/28 11:27:44 njh Exp $
 */

/*
 * Standard: ECMAScript-2.0.draft-22-Apr-98
 */
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif

#ifdef	CL_EXPERIMENTAL

#include "jsint.h"

/*
 * Types and definitions.
 */

/* Class context. */
struct number_ctx_st
{
  JSSymbol s_MAX_VALUE;
  JSSymbol s_MIN_VALUE;
  JSSymbol s_NaN;
  JSSymbol s_NEGATIVE_INFINITY;
  JSSymbol s_POSITIVE_INFINITY;
};

typedef struct number_ctx_st NumberCtx;


/*
 * Static functions.
 */

/* Global method proc. */
static void
global_method (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
	       void *instance_context, JSNode *result_return,
	       JSNode *args)
{
  if (args->u.vinteger == 0)
    {
      result_return->type = JS_INTEGER;
      result_return->u.vinteger = 0;
    }
  else if (args->u.vinteger == 1)
    {
      js_vm_to_number (vm, &args[1], result_return);
    }
  else
    {
      sprintf (vm->error, "Number(): illegal amount of arguments");
      js_vm_error (vm);
    }
}

/* Method proc. */
static int
method (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
	void *instance_context, JSSymbol method, JSNode *result_return,
	JSNode *args)
{
  JSNode *n = instance_context;
  char buf[256];

  if (method == vm->syms.s_toString)
    {
      if (n)
	{
	  int radix = 10;

	  if (args->u.vinteger == 0)
	    ;
	  else if (args->u.vinteger == 1)
	    {
	      if (args[1].type != JS_INTEGER)
		goto argument_type_error;

	      radix = args[1].u.vinteger;
	    }
	  else
	    goto argument_error;

	  if (n->type == JS_INTEGER)
	    {
	      switch (radix)
		{
		case 2:
		  {
		    char buf2[256];
		    int i;
		    unsigned int bit = 1;
		    unsigned long ul = (unsigned long) n->u.vinteger;

		    for (i = 0; bit > 0; bit <<= 1, i++)
		      buf2[i] = (ul & bit) ? '1' : '0';

		    for (i--; i > 0 && buf2[i] == '0'; i--)
		      ;

		    bit = i;
		    for (; i >= 0; i--)
		      buf[bit - i] = buf2[i];
		    buf[bit + 1] = '\0';
		  }
		  break;

		case 8:
		  sprintf (buf, "%lo", (unsigned long) n->u.vinteger);
		  break;

		case 10:
		  sprintf (buf, "%ld", n->u.vinteger);
		  break;

		case 16:
		  sprintf (buf, "%lx", (unsigned long) n->u.vinteger);
		  break;

		default:
		  sprintf (vm->error, "Number.%s(): illegal radix %d",
			   js_vm_symname (vm, method), radix);
		  js_vm_error (vm);
		  break;
		}
	    }
	  else if (n->type == JS_FLOAT)
	    sprintf (buf, "%g", n->u.vfloat);
	  else
	    sprintf (buf, "NaN");

	  js_vm_make_string (vm, result_return, buf, strlen (buf));
	}
      else
	{
	  if (args->u.vinteger != 0)
	    goto argument_error;
	  js_vm_make_static_string (vm, result_return, "Number", 6);
	}
    }
  /* ********************************************************************** */
  else if (method == vm->syms.s_valueOf)
    {
      if (n == NULL)
	n = &vm->globals[js_vm_intern (vm, "Number")];

      JS_COPY (result_return, n);
    }
  /* ********************************************************************** */
  else
    return JS_PROPERTY_UNKNOWN;

  return JS_PROPERTY_FOUND;


  /*
   * Error handling.
   */

 argument_error:
  sprintf (vm->error, "Number.%s(): illegal amount of arguments",
	   js_vm_symname (vm, method));
  js_vm_error (vm);

 argument_type_error:
  sprintf (vm->error, "Number.%s(): illegal argument",
	   js_vm_symname (vm, method));
  js_vm_error (vm);

  /* NOTREACHED */
  return 0;
}

/* Property proc. */
static int
property (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info,
	  void *instance_context, JSSymbol property, int set, JSNode *node)
{
  NumberCtx *ctx = builtin_info->obj_context;

  /* The default result type. */
  node->type = JS_FLOAT;

  if (property == ctx->s_MAX_VALUE)
    {
      if (set)
	goto immutable;

      node->u.vfloat = DBL_MAX;
    }
  else if (property == ctx->s_MIN_VALUE)
    {
      if (set)
	goto immutable;

      node->u.vfloat = DBL_MIN;
    }
  else if (property == ctx->s_NaN)
    {
      if (set)
	goto immutable;

      node->type = JS_NAN;
    }
  else if (property == ctx->s_NEGATIVE_INFINITY)
    {
      if (set)
	goto immutable;

      JS_MAKE_NEGATIVE_INFINITY (node);
    }
  else if (property == ctx->s_POSITIVE_INFINITY)
    {
      if (set)
	goto immutable;

      JS_MAKE_POSITIVE_INFINITY (node);
    }
  else
    {
      if (!set)
	node->type = JS_UNDEFINED;

      return JS_PROPERTY_UNKNOWN;
    }

  return JS_PROPERTY_FOUND;


  /*
   * Error handling.
   */

 immutable:
  sprintf (vm->error, "Number.%s: immutable property",
	   js_vm_symname (vm, property));
  js_vm_error (vm);

  /* NOTREACHED */
  return 0;
}

/* New proc. */
static void
new_proc (JSVirtualMachine *vm, JSBuiltinInfo *builtin_info, JSNode *args,
	  JSNode *result_return)
{
  if (args->u.vinteger == 0)
    {
      result_return->type = JS_INTEGER;
      result_return->u.vinteger = 0;
    }
  else if (args->u.vinteger == 1)
    {
      js_vm_to_number (vm, &args[1], result_return);
    }
  else
    {
      sprintf (vm->error, "new Number(): illegal amount of arguments");
      js_vm_error (vm);
    }
}

/*
 * Global functions.
 */

void
js_builtin_Number (JSVirtualMachine *vm)
{
  NumberCtx *ctx;
  JSNode *n;
  JSBuiltinInfo *info;

  ctx = js_calloc (vm, 1, sizeof (*ctx));

  ctx->s_MAX_VALUE		= js_vm_intern (vm, "MAX_VALUE");
  ctx->s_MIN_VALUE		= js_vm_intern (vm, "MIN_VALUE");
  ctx->s_NaN			= js_vm_intern (vm, "NaN");
  ctx->s_NEGATIVE_INFINITY	= js_vm_intern (vm, "NEGATIVE_INFINITY");
  ctx->s_POSITIVE_INFINITY	= js_vm_intern (vm, "POSITIVE_INFINITY");

  info = js_vm_builtin_info_create (vm);
  vm->prim[JS_INTEGER]	= info;
  vm->prim[JS_FLOAT]	= info;
  vm->prim[JS_NAN]	= info;

  info->global_method_proc	= global_method;
  info->method_proc 		= method;
  info->property_proc		= property;
  info->new_proc		= new_proc;
  info->obj_context		= ctx;
  info->obj_context_delete	= js_free;

  /* Define it. */
  n = &vm->globals[js_vm_intern (vm, "Number")];
  js_vm_builtin_create (vm, n, info, NULL);
}
#endif	/*CL_EXPERIMENTAL*/