#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <jsapi.h>
#include "encrypt.h"


static JSBool executeJSFunction(JSContext *cx, const char *file, const char *function, uintN argc, jsval *argv, jsval *ret);
JSClass globalObjectClass = {
    "global",
    0,

    JS_PropertyStub,
    JS_PropertyStub,
    JS_PropertyStub,
    JS_PropertyStub,
    JS_EnumerateStub,
    JS_ResolveStub,
    JS_ConvertStub,
    JS_FinalizeStub,

    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
};


char *encryptData(EncryptionTypes *type, const char *data){
    JSRuntime *rt=NULL;
    JSContext *cx=NULL;
    jsval *parameters=NULL;
    jsval ret;
    JSString *stdinData=NULL;
    char *stdinDataBuffer;


    rt = JS_NewRuntime(1073741824);
    if (rt == NULL){
        printf("Failed to start Spidermonkey, cannot perform encryption.\n");
        return strdup(data);
    }

    cx = JS_NewContext(rt, 8192);
    if (cx == NULL){
        JS_DestroyRuntime(rt);
        printf("Failed to start Spidermonkey, cannot perform encryption.\n");
        return strdup(data);
    }


     /* Spidermonkey manages the memory space of it's strings, so we need to duplicate the passed string. 
      * We must allocate the memory using Spidermonkey's JS_malloc function though, so that it is safe for 
      * Spidermonkey to manage it.
      */
    stdinDataBuffer = JS_malloc(cx, strlen(data));
    memcpy(stdinDataBuffer, data, strlen(data));

    stdinData = JS_NewString(cx, stdinDataBuffer, strlen(stdinDataBuffer));
    
    parameters=JS_malloc(cx, sizeof(*parameters));
    parameters[0] = STRING_TO_JSVAL(stdinData);

    /* Execute the javascript file. */
    if (executeJSFunction(cx, type->filePath, type->functionName, 1, parameters, &ret) == JS_TRUE){
        JSString *retString = JS_ValueToString(cx, ret);
        char *encryptedString = JS_GetStringBytes(retString);
        char *returnedString;


        /* We duplicate the string returned by JS_GetStringBytes because when we clean up the 
         * Runtime and Context the string pointed to by encryptedString will be cleaned up and 
         * invalid.  Therefore we need our own copy if we are going to use it after the cleanup.
         */
        returnedString = strdup(encryptedString);

        JS_DestroyContext(cx);
        JS_DestroyRuntime(rt);
        return returnedString;
    }

    /* Cleanup Spidermonkey */
    JS_DestroyContext(cx);
    JS_DestroyRuntime(rt);
    return strdup(data);
}


static JSBool executeJSFunction(JSContext *cx, const char *file, const char *function, uintN argc, jsval *argv, jsval *ret){
    char *expandedFilename = expandTilda(file);
    struct stat fileDetails;
    JSObject *global=NULL;
    JSScript *script=NULL;


    if (stat(expandedFilename, &fileDetails) == -1){
        printf("Encryption code file '%s' does not exist.\n", expandedFilename);
        free(expandedFilename);
        return JS_FALSE;
    }


    /* Create a global object */
    global = JS_NewObject(cx, &globalObjectClass, NULL, NULL);
    if (global == NULL){
        printf("Execution of encryptor failed: Cannot create global object.\n");
        free(expandedFilename);
        return JS_FALSE;
    }

    JS_InitStandardClasses(cx, global);
    
    script = JS_CompileFile(cx, global, expandedFilename);
    if (script == NULL){
        printf("Execution of encryptor failed: Cannot compile file '%s'.\n", expandedFilename);
        free(expandedFilename);
        return JS_FALSE;
    }

    if (JS_CallFunctionName(cx, global, function, argc, argv, ret) == JS_FALSE){
        printf("Execution of encryptor failed: Cannot execute function '%s' with string parameter.\n", function);
        free(expandedFilename);
        return JS_FALSE;
    }
    
    free(expandedFilename);
    return JS_TRUE;
}
