/*
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/

 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.

 * The Original Code is Rhino code, released
 * May 6, 1999.

 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
 * Rights Reserved.

 * Contributor(s): 

 * Alternatively, the contents of this file may be used under the
 * terms of the GNU Public License (the "GPL"), in which case the
 * provisions of the GPL are applicable instead of those above.
 * If you wish to allow use of your version of this file only
 * under the terms of the GPL and not to allow others to use your
 * version of this file under the NPL, indicate your decision by
 * deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL.  If you do not delete
 * the provisions above, a recipient may use your version of this
 * file under either the NPL or the GPL.
 */
/* Header file */
#include <stdio.h>
#include <math.h>
#include <limits.h>
#if defined(VX) || defined(TARGET_OS_LINUX)
#include <float.h>
#endif
#include "js-objectadapter.h"
#include "js-scriptruntime.h"
#include "js-exceptionhandler.h"
#include "js-nativeglobal.h"
#include "js-functionobject.h"
#include "js-utils.h"
#include "js-dtoa.h"
#include "js-nativenumber.h"

/* Internal data */
const jsString_t JSNativeNumber::className = "Number";

/* Procedures */
void JSNativeNumber::finishInit(JSContext * cx, JSScriptable * scope, JSFunctionObject * ctor, JSScriptable * proto)
{
    class NumberAdapter:JSObjectAdapter {
      public:
#define	defineNumberFunction(name)					\
	    static jsVal_t jsFunction_##name(JSContext *cx,		\
				JSScriptable *thisObj, int numArgs,	\
				jsVal_t *args, JSFunction *funObj) {	\
	        JSScriptable *scriptable;				\
									\
                scriptable = getScriptable(cx, thisObj,			\
				JSNativeNumber::className, funObj);	\
                                                                        \
                JSNativeNumber *_thisObj				\
				= (JSNativeNumber *) scriptable;	\
									\
                return _thisObj->jsFunction_##name(cx);			\
	    }
#define	defineNumberFunction1(name)					\
	    static jsVal_t jsFunction_##name(JSContext *cx,		\
				JSScriptable *thisObj, int numArgs,	\
				jsVal_t *args, JSFunction *funObj) {	\
	        JSScriptable *scriptable;				\
									\
                scriptable = getScriptable(cx, thisObj,			\
				JSNativeNumber::className, funObj);	\
                                                                        \
                JSNativeNumber *_thisObj				\
				= (JSNativeNumber *) scriptable;	\
									\
                return _thisObj->jsFunction_##name(cx, args[0]);	\
	    }

        defineNumberFunction1(toString) defineNumberFunction(valueOf) defineNumberFunction1(toLocaleString) defineNumberFunction1(toFixed) defineNumberFunction1(toExponential) defineNumberFunction1(toPrecision)
#undef	defineNumberFunction1
#undef	defineNumberFunction
    };
    static jsFunction_t functions[] = {
#define	ItemAdapter(name, clazz, length)	\
				{ #name,  clazz::jsFunction_##name, length }
        ItemAdapter(toString, NumberAdapter, 1),
        ItemAdapter(valueOf, NumberAdapter, 0),
        ItemAdapter(toLocaleString, NumberAdapter, 1),
        ItemAdapter(toFixed, NumberAdapter, 1),
        ItemAdapter(toExponential, NumberAdapter, 1),
        ItemAdapter(toPrecision, NumberAdapter, 1),
#undef	ItemAdapter
    };

    ((JSScriptableObject *) proto)->defineFunctionProperties(cx, className, TableSize(functions), functions, JS_PA_DontEnum);

    const int attr = JS_PA_DontEnum | JS_PA_Permanent | JS_PA_ReadOnly;
    static struct {
        const jsString_t name;
        double value;
    } nameValues[] = {
        {
        "NaN", 0.0}, {
        "POSITIVE_INFINITY", POSITIVE_INFINITY}, {
        "NEGATIVE_INFINITY", NEGATIVE_INFINITY}, {
        "MAX_VALUE", DBL_MAX}, {
    "MIN_VALUE", DBL_MIN},};

    nameValues[0].value = JSScriptRuntime::NaN;
#ifndef VX
    *((uint64_t *) & nameValues[4].value) = 1;
#endif
    for (int i = 0; i < (int)TableSize(nameValues); i++) {
        ctor->defineProperty(cx, nameValues[i].name, JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, nameValues[i].value)), attr);
    }
}
jsVal_t JSNativeNumber::jsConstructor(JSContext * cx, int numArgs, jsVal_t * args, JSFunction * ctorObj, jsBool_t inNewExpr)
{
    double d = numArgs >= 1 ? JSScriptRuntime::toNumber(cx, args[0])
            : defaultValue;

    if (inNewExpr) {
        // new Number(val) creates a new Number object.
        return JS_OBJECT_TO_VAL(new(cx) JSNativeNumber(d));
    }
    // Number(val) converts val to a number value.
    return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, d));
}

#ifndef DTOA
void JSNativeNumber::normalize(jsString_t buf)
{
#if defined(SUNOS5)
    static jsChar_t Infinity[] = "Infinity";
    static jsChar_t Inf[] = "Inf";
    jsString_t s;

    s = strstr(buf, Inf);
    if (s != NULL) {
        memmove(s + TableSize(Infinity) - 1, s + TableSize(Inf) - 1, (strlen(s + TableSize(Inf) - 1) + 1) * sizeof(*s));
        memcpy(s, Infinity, (TableSize(Infinity) - 1) * sizeof(*s));
    }
#endif
}
int JSNativeNumber::getPrecision(JSContext * cx, jsVal_t arg, int precisionMin, int precisionMax)
{
    int precision;

    if (arg == JS_VAL_VOID) {
        precision = 0;
    }
    else {
        precision = JSScriptRuntime::toInt32(cx, arg);
        if (precision < precisionMin || precision > precisionMax) {
            cx->exceptionHandler->throwEx(JSNativeGlobal::constructError(cx, "RangeError", JSScriptRuntime::getMessage(cx, "msg.bad.precision"), this));
        }
    }
    return precision;
}
#else /* !DTOA */
jsVal_t JSNativeNumber::num_to(JSContext * cx, jsVal_t arg, int zeroArgMode, int oneArgMode, int precisionMin, int precisionMax, int precisionOffset)
{
    int precision;

    if (arg == JS_VAL_VOID) {
        precision = 0;
        oneArgMode = zeroArgMode;
    }
    else {
        precision = JSScriptRuntime::toInt32(cx, arg);
        if (precision < precisionMin || precision > precisionMax) {
            cx->exceptionHandler->throwEx(JSNativeGlobal::constructError(cx, "RangeError", JSScriptRuntime::getMessage(cx, "msg.bad.precision %d", precision), this));
        }
    }
    JSStringBuffer result(cx);

    JSDToA::JS_dtostr(cx, &result, oneArgMode, precision + precisionOffset, doubleValue);
    return JS_STRING_TO_VAL(JS_NewGCString(cx, result.toString()));
}
#endif /* DTOA */
jsString_t JSNativeNumber::toString(JSContext * cx, jsVal_t base)
{
    int i = base == JS_VAL_VOID ? 10 : JSScriptRuntime::toInt32(cx, base);
    return JSScriptRuntime::numberToString(cx, doubleValue, i);
}

void JSNativeNumber::defineClass(JSContext * cx, JSScriptable * scope)
{
    JSScriptable *proto = new(cx) JSNativeNumber();

    JSScriptableObject::defineClass(cx, scope, proto, jsConstructor, finishInit);
}

jsVal_t JSNativeNumber::jsFunction_toString(JSContext * cx, jsVal_t base)
{
    jsString_t s = toString(cx, base);

    if (s == NULL) {
        s = "";
    }

    jsVal_t result = JS_STRING_TO_VAL(JS_NewGCString(cx, s));

    JS_DeleteZoneObj(cx, s);
    return result;
}

jsVal_t JSNativeNumber::jsFunction_valueOf(JSContext * cx)
{
    return JS_DOUBLE_TO_VAL(JS_NewGCDouble(cx, doubleValue));
}

jsVal_t JSNativeNumber::jsFunction_toLocaleString(JSContext * cx, jsVal_t arg)
{
    return jsFunction_toString(cx, JS_VAL_VOID);
}

jsVal_t JSNativeNumber::jsFunction_toFixed(JSContext * cx, jsVal_t arg)
{
#ifndef DTOA
    int precision = getPrecision(cx, arg, -20, MAX_PRECISION);

    if (doubleValue >= 1.0e20) {
        return jsFunction_toString(cx, JS_VAL_VOID);
    }
    jsChar_t buf[128], format[16];

    sprintf(format, "%%.%df", precision);
    sprintf(buf, format, doubleValue);
    normalize(buf);

    return JS_STRING_TO_VAL(JS_NewGCString(cx, buf));
#else /* !DTOA */
    return num_to(cx, arg, JSDToA::DTOSTR_FIXED, JSDToA::DTOSTR_FIXED, -20, MAX_PRECISION, 0);
#endif /* DTOA */
}

jsVal_t JSNativeNumber::jsFunction_toExponential(JSContext * cx, jsVal_t arg)
{
#ifndef DTOA
    int precision = getPrecision(cx, arg, 0, MAX_PRECISION);

    jsChar_t buf[128], format[16];

    sprintf(format, "%%.%de", precision);
    sprintf(buf, format, doubleValue);
    normalize(buf);

    return JS_STRING_TO_VAL(JS_NewGCString(cx, buf));
#else /* !DTOA */
    return num_to(cx, arg, JSDToA::DTOSTR_STANDARD_EXPONENTIAL, JSDToA::DTOSTR_EXPONENTIAL, 0, MAX_PRECISION, 1);
#endif /* DTOA */
}

jsVal_t JSNativeNumber::jsFunction_toPrecision(JSContext * cx, jsVal_t arg)
{
#ifndef DTOA
    int precision = getPrecision(cx, arg, 1, MAX_PRECISION);

    jsChar_t buf[128], format[16];

    sprintf(format, "%%.%dg", precision);
    sprintf(buf, format, doubleValue);
    normalize(buf);

    return JS_STRING_TO_VAL(JS_NewGCString(cx, buf));
#else /* !DTOA */
    return num_to(cx, arg, JSDToA::DTOSTR_STANDARD, JSDToA::DTOSTR_PRECISION, 1, MAX_PRECISION, 0);
#endif /* DTOA */
}

jsString_t JSNativeNumber::toString(JSContext * cx)
{
    return toString(cx, JS_VAL_VOID);
}

jsBool_t JSNativeNumber::isInstanceOf(const jsString_t className)
{
    if (className == JSNativeNumber::className)
        return TRUE;
    return JSScriptableObject::isInstanceOf(className);
}

jsString_t JSNativeNumber::getClassName(JSContext * cx)
{
    return JSNativeNumber::className;
}

JSNativeNumber::JSNativeNumber()
{
    doubleValue = defaultValue;
}

JSNativeNumber::JSNativeNumber(double number)
{
    doubleValue = number;
}
