/*
 * Copyright 2006 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */


#include <jsapi.h>

#include "SkJS.h"
#include "SkString.h"

#ifdef _WIN32_WCE
extern "C" {
    void abort() {
        SkASSERT(0);
    }

    unsigned int _control87(unsigned int _new, unsigned int mask ) {
        SkASSERT(0);
        return 0;
    }

    time_t mktime(struct tm *timeptr ) {
        SkASSERT(0);
        return 0;
    }

//  int errno;

    char *strdup(const char *) {
        SkASSERT(0);
        return 0;
    }

    char *strerror(int errnum) {
        SkASSERT(0);
        return 0;
    }

    int isatty(void* fd) {
        SkASSERT(0);
        return 0;
    }

    int putenv(const char *envstring) {
        SkASSERT(0);
        return 0;
    }

    char *getenv(const char *varname) {
        SkASSERT(0);
        return 0;
    }

    void GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime) {
        SkASSERT(0);
    }

    struct tm * localtime(const time_t *timer) {
        SkASSERT(0);
        return 0;
    }

    size_t strftime(char *strDest, size_t maxsize, const char *format,
        const struct tm *timeptr ) {
        SkASSERT(0);
        return 0;
    }

}
#endif

static JSBool
global_enumerate(JSContext *cx, JSObject *obj)
{
#ifdef LAZY_STANDARD_CLASSES
    return JS_EnumerateStandardClasses(cx, obj);
#else
    return JS_TRUE;
#endif
}

static JSBool
global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp)
{
#ifdef LAZY_STANDARD_CLASSES
    if ((flags & JSRESOLVE_ASSIGNING) == 0) {
        JSBool resolved;

        if (!JS_ResolveStandardClass(cx, obj, id, &resolved))
            return JS_FALSE;
        if (resolved) {
            *objp = obj;
            return JS_TRUE;
        }
    }
#endif

#if defined(SHELL_HACK) && defined(DEBUG) && defined(XP_UNIX)
    if ((flags & (JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING)) == 0) {
        /*
         * Do this expensive hack only for unoptimized Unix builds, which are
         * not used for benchmarking.
         */
        char *path, *comp, *full;
        const char *name;
        JSBool ok, found;
        JSFunction *fun;

        if (!JSVAL_IS_STRING(id))
            return JS_TRUE;
        path = getenv("PATH");
        if (!path)
            return JS_TRUE;
        path = JS_strdup(cx, path);
        if (!path)
            return JS_FALSE;
        name = JS_GetStringBytes(JSVAL_TO_STRING(id));
        ok = JS_TRUE;
        for (comp = strtok(path, ":"); comp; comp = strtok(NULL, ":")) {
            if (*comp != '\0') {
                full = JS_smprintf("%s/%s", comp, name);
                if (!full) {
                    JS_ReportOutOfMemory(cx);
                    ok = JS_FALSE;
                    break;
                }
            } else {
                full = (char *)name;
            }
            found = (access(full, X_OK) == 0);
            if (*comp != '\0')
                free(full);
            if (found) {
                fun = JS_DefineFunction(cx, obj, name, Exec, 0, JSPROP_ENUMERATE);
                ok = (fun != NULL);
                if (ok)
                    *objp = obj;
                break;
            }
        }
        JS_free(cx, path);
        return ok;
    }
#else
    return JS_TRUE;
#endif
}

JSClass global_class = {
    "global", JSCLASS_NEW_RESOLVE,
    JS_PropertyStub,  JS_PropertyStub,
    JS_PropertyStub,  JS_PropertyStub,
    global_enumerate, (JSResolveOp) global_resolve,
    JS_ConvertStub,   JS_FinalizeStub
};

SkJS::SkJS(void* hwnd) : SkOSWindow(hwnd) {
    if ((fRuntime = JS_NewRuntime(0x100000)) == NULL) {
        SkASSERT(0);
        return;
    }
    if ((fContext = JS_NewContext(fRuntime, 0x1000)) == NULL) {
        SkASSERT(0);
        return;
    }
    ;
    if ((fGlobal = JS_NewObject(fContext, &global_class, NULL, NULL)) == NULL) {
        SkASSERT(0);
        return;
    }
    if (JS_InitStandardClasses(fContext, fGlobal) == NULL) {
        SkASSERT(0);
        return;
    }
    setConfig(SkBitmap::kARGB32_Config);
    updateSize();
    setVisibleP(true);
    InitializeDisplayables(getBitmap(), fContext, fGlobal, NULL);
}

SkJS::~SkJS() {
    DisposeDisplayables();
    JS_DestroyContext(fContext);
    JS_DestroyRuntime(fRuntime);
    JS_ShutDown();
}

SkBool SkJS::EvaluateScript(const char* script, jsval* rVal) {
    return JS_EvaluateScript(fContext, fGlobal, script, strlen(script),
        "memory" /* no file name */, 0 /* no line number */, rVal);
}

SkBool SkJS::ValueToString(jsval value, SkString* string) {
     JSString* str = JS_ValueToString(fContext, value);
     if (str == NULL)
         return false;
     string->set(JS_GetStringBytes(str));
     return true;
}

#ifdef SK_DEBUG
void SkJS::Test(void* hwnd) {
    SkJS js(hwnd);
    jsval val;
    SkBool success = js.EvaluateScript("22/7", &val);
    SkASSERT(success);
    SkString string;
    success = js.ValueToString(val, &string);
    SkASSERT(success);
    SkASSERT(strcmp(string.c_str(), "3.142857142857143") == 0);
    success = js.EvaluateScript(
        "var rect = new rectangle();"
        "rect.left = 4;"
        "rect.top = 10;"
        "rect.right = 20;"
        "rect.bottom = 30;"
        "rect.width = rect.height + 20;"
        "rect.draw();"
        , &val);
    SkASSERT(success);
    success = js.ValueToString(val, &string);
    SkASSERT(success);
}
#endifASSERT(success);