/*
 * Copyright (C) 2018-2020 Western Digital Corporation or its affiliates
 * Copyright (C) 2017-2018 Wearable, Inc.
 * Copyright (C) 2000-2012 leJOS Contributors
 * Copyright (C) 2000 Jose H. Solorzano and TinyVM Contributors
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 * 
 * This Source Code Form is “Incompatible With Secondary Licenses”,
 * as defined by the Mozilla Public License, v. 2.0.
 */

#include "run.h"
#include "platform_hooks.h"
#include "language.h"
#include "threads.h"
#include "exceptions.h"
#include "memory.h"
#include "stack.h"

static void init_argv(nvm_state_t *pState, int argc, char *argv[])
{
    REFERENCE argRef = ptr2ref(new_single_array(pState, ALJAVA_LANG_STRING, argc, false));
    REFERENCE *pStart = ref_array(ref2obj(argRef));
    for (int i = 0; i<argc; i++) {
        pStart[i] = ptr2ref(NULL);
    }
    set_top_ref_cur(pState, argRef);
    for (int i = 0; i<argc; i++) {
        pStart[i] = obj2ref(new_string_from_cstr(pState, argv[i]));
    }
}

boolean nvm_run(nvm_state_t *pState, byte *heap, size_t heap_size, size_t array_heap_size,
                const byte *binary, int argc, char *argv[])
{
    byte *ram_end = heap + (heap_size - 2);
    byte *ram_start = heap;
    
    init_state(pState);
    init_time(pState);  // Sets up the mechanism for doing sys_calls.
    init_events(pState);

    install_binary(pState, binary);
    
    if (get_master_record(pState)->magicNumber != MAGIC
        || get_master_record(pState)->magicNumberAgain != MAGIC) {
        return false;
    }

    // Init the static storage and class sync objects in ram
    {
        MasterRecord *mrec = get_master_record(pState);
        int staticSize = mrec->staticStateLength;
        int syncSize = (mrec->lastClass + 1) * sizeof(objSync);
        
        staticSize = (staticSize + 3) & ~(3);
        syncSize = (syncSize + 3) & ~(3);
        
        ram_end -= staticSize;
        pState->class_static_state_base = ram_end;
        memset( (byte *)pState->class_static_state_base, 0, staticSize);
        ram_end -= syncSize;
        pState->static_sync_base = (objSync *)ram_end;
        memset( (byte *)pState->static_sync_base, 0, syncSize);
        
        // We need extra storage for the class state.
        int statusSize = (mrec->lastClass + 1) * sizeof( pState->class_status_base[0]);
        statusSize = (statusSize + 3) & ~(3);
        ram_end -= statusSize;
        pState->class_status_base = ram_end;
        memset( (byte *)pState->class_status_base, 0, statusSize);
    }
    
    // Initialize memory
    memory_init(pState);
    memory_add_region(pState, ram_start, ram_end, array_heap_size);
    
    init_threads(pState);
    nvm_init_exceptions(pState);
    if (argc > 0 && argv != NULL) {
        init_argv(pState, argc, argv);
    }
    engine(pState);

    return true;
}
