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

/* Function Prototypes */
JSBool js_engine_execute_file(JSContext *ctx, const char *file);
static void js_error_handler(JSContext *ctx, const char *msg, JSErrorReport *er);

JSClass js_global_object_class = {
  "System",
  0,
  JS_PropertyStub,
  JS_PropertyStub,
  JS_PropertyStub,
  JS_PropertyStub,
  JS_EnumerateStub,
  JS_ResolveStub,
  JS_ConvertStub,
  JS_FinalizeStub,
  JSCLASS_NO_OPTIONAL_MEMBERS
};

int main(int argc, char **argv){
    char *fileToRun=NULL;
    JSRuntime *rt=NULL;
    JSContext *cx=NULL;
    JSObject *obj=NULL;

    if (argc > 1){
      fileToRun = argv[1];
    }
    
    rt = JS_NewRuntime(8L*1024L);
    if (!rt){
      printf("Failed to initialize JS Runtime.\n");
      return 1;
    }

    cx = JS_NewContext(rt, 8L*1024L*1024L);
    if (!cx){
      printf("Failed to initialize JS Context.\n");
      JS_DestroyRuntime(rt);
      return 1;
    }
    
    JS_SetErrorReporter(cx, js_error_handler);
    
    obj = JS_NewObject(cx, &js_global_object_class, NULL, NULL);
    if (!obj){
      printf("Failed to create global object.\n");
      JS_DestroyContext(cx);
      JS_DestroyRuntime(rt);
      return 1;
    }

    JS_InitStandardClasses(cx, obj);
    
    if (fileToRun){
      if (js_engine_execute_file(cx, fileToRun)){
        printf("File %s has been successfully executed.\n", fileToRun);
      }
      else {
        printf("Failed to executed %s.\n", fileToRun);
      }
    }
    else {
      printf("Usage: %s file\n", argv[0]);
    }


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


JSBool js_engine_execute_file(JSContext *ctx, const char *file){
    JSScript *script;
    jsval returnValue;
    JSBool returnVal;
    JSObject *global = JS_GetGlobalObject(ctx);
    struct stat statinfo;

    if (file == NULL){
        return JS_FALSE;
    }

    if (stat(file, &statinfo) == -1){
        return JS_FALSE;
    }

    if (!S_ISREG(statinfo.st_mode)){
        return JS_FALSE;
    }

    script = JS_CompileFile(ctx, global, file);

    if (script == NULL){
        return JS_FALSE;
    }
    
    returnVal = JS_ExecuteScript(ctx, global, script, &returnValue);
    JS_DestroyScript(ctx, script);
    return returnVal;
}



static void js_error_handler(JSContext *ctx, const char *msg, JSErrorReport *er){
    char *pointer=NULL;
    char *line=NULL;
    int len;

    if (er->linebuf != NULL){
        len = er->tokenptr - er->linebuf + 1;
        pointer = malloc(len);
        memset(pointer, '-', len);
        pointer[len-1]='\0';
        pointer[len-2]='^';

        len = strlen(er->linebuf)+1;
        line = malloc(len);
        strncpy(line, er->linebuf, len);
        line[len-1] = '\0';
    }
    else {
        len=0;
        pointer = malloc(1);
        line = malloc(1);
        pointer[0]='\0';
        line[0] = '\0';
    }

    while (len > 0 && (line[len-1] == '\r' || line[len-1] == '\n')){ line[len-1]='\0'; len--; }

    printf("JS Error: %s\nFile: %s:%u\n", msg, er->filename, er->lineno);
    if (line[0]){
      printf("%s\n%s\n", line, pointer);
    }

    free(pointer);
    free(line);
}

