/*
 * 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.
 */

#ifndef NVM_STATE_H
#define NVM_STATE_H

#include "types.h"
#include "classes.h"
#include "sb_hmac_drbg.h"

#define MEM_ABSOLUTE 0
#define MEM_THREADS 1
#define MEM_HEAP 2
#define MEM_IMAGE 3
#define MEM_STATICS 4
#define MEM_MEM 5

#define MARK_QUEUE_SIZE 64

// Value used to mark end of the free list. Will always point beyond the
// end of the heap, when used as an offset.
#define FL_END 0xffff

#define GC_IDLE 0
#define GC_MARKROOTS 1
#define GC_MARK 2
#define GC_SWEEP 3
#define GC_COMPACT 4
#define GC_COMPLETE 5

// Heap object colors
#define GC_WHITE 0
#define GC_LIGHTGREY 1
#define GC_DARKGREY 2
#define GC_BLACK 3

#if FAST_DISPATCH
typedef const void* const DISPATCH_LABEL;
#endif

typedef struct S_ConstantRecord
{
    /**
     * Offset to bytes of constant.
     */
    nvm_binary_size_t offset;
    /**
     * Type of constant.
     */
    byte constantType;
    byte stringLength;
} ConstantRecord;

_Static_assert(sizeof(ConstantRecord) == 8, "sizeof(ConstantRecord) == 8");

typedef struct S_ClassRecord
{
    /**
     * Object header, to allow access as a Java object.
     */
    objFlags objectHdr;
    /**
     * Space occupied by instance in bytes.
     */
    uint16_t classSize;
    /**
     * The following 4 fields are used differently depending upon the type
     * of class. They could be defined here as a union, but this structure
     * is also mad available to Java and it is not possible to represent
     * unions easily in Java. For this reason we use simple field names in
     * both the C and Java code. The following table describes the field
     * usage.
     *
     * Normal Class
     * CIAData1: Method table offset from the start of the binary.
     * CIAData2: Field table offset from the start of the binary.
     * CIACnt1:  Number of method records
     * CIACnt2:  Number of field records
     *
     * Interface Class
     * CIAData1: Method table offset from the start of the binary. if it is zero
     *           there are no methods. Otherwise it points to a single entry for
     *           the static initializer.
     * CIAData2: Instance member map offset from the start of the binary.
     * CIACnt1:  Class base, the class number of the first bit in the table
     * CIACnt2:  Number of valid bits in the table.
     *
     * Array Class
     * CIAData1: The dimension of the array
     * CIAData2: The class of the array element.
     * CIACnt1:  Unused
     * CIACnt2:  Unused
     */
    nvm_class_index_t CIAData1;
    nvm_class_index_t CIAData2;
    nvm_class_index_t CIACnt1;
    nvm_class_index_t CIACnt2;
    nvm_class_index_t parentClass;
    byte cflags;
    byte unused;
} ClassRecord;

_Static_assert(sizeof(ClassRecord) == 16, "sizeof(ClassRecord) == 16");

// Header for GC free objects
typedef struct
{
    objFlags flags;
    uint16_t next;
} FreeObject;

// Amount of size to allocate for special bytecode within the state object
#define SPECIAL_BYTECODE_SIZE 28

// Platform state
typedef struct nvm_state_t
{
#if FAST_DISPATCH
    DISPATCH_LABEL * volatile dispatch_table;
    DISPATCH_LABEL * dispatch_check_event;
    DISPATCH_LABEL * dispatch_normal;
#endif
    unsigned int vm_options;

    TIMESTAMP* sys_time;
    TIMESTAMP wake_time;
    TIMESTAMP switch_time;
    TIMESTAMP gc_time;
    TIMESTAMP force_gc_time;

    Thread *current_thread;
    REFERENCE threads;
    REFERENCE *thread_queue;
    
    Object *events[MAX_EVENTS];
    boolean shutdown_started;
    
    const byte *program_counter;
    int program_offset;
    STACKWORD *program_stack_top;
    STACKWORD *program_locals_base;
    int thrown_exception;
    
    Throwable *out_of_memory_error;
    
    byte* constant_values_base;
    ConstantRecord* constant_table_base;
    ClassRecord* class_base;
    objSync *static_sync_base;
    byte* static_fields_base;
    byte *class_static_state_base;
    
    byte *class_status_base;
    
    byte *installed_binary;
    nvm_exit_trace_t trace;

#if !FAST_DISPATCH
    volatile boolean scheduler_make_request;
#endif
    byte    scheduler_request_code;
    
    uint16_t failed_alloc_size;
    int gc_phase;
    Object gc_lock;
    uint32_t *gc_heap_end;         /* pointer to end of heap */
    uint32_t *gc_heap_start;       /* pointer to start of heap */
    uint32_t gc_memory_size;       /* total number of words in heap */
    uint32_t gc_obj_heap_memory_free;  /* number of free words in heap */
    Object *gc_obj_heap_start;      /* pointer to start of object heap */
    Object *gc_obj_heap_end;        /* pointer to end of object heap */
    byte *gc_obj_reference_map;     /* pointer to map of allocated objects */
    uint16_t *gc_alloc_prev;        /* current position in the free list */
    Object *gc_sweep_base;          /* current position of sweeping */
    FreeObject *gc_sweep_free;      /* current position of sweeping free ptr */
    Object *gc_overflow_scan;       /* current position of overflow scan */
    uint16_t gc_mark_queue[MARK_QUEUE_SIZE];
    int gc_mark_queue_hd;
    int gc_mark_queue_tl;
    
    unsigned int gc_obj_heap_memory_required;        /* Target amount of memory needed */
    unsigned int gc_obj_heap_largest_free;
    
    // Array heap data
    uint32_t *gc_array_heap_start;        /* pointer to start of array heap */
    uint32_t *gc_array_heap_end;          /* pointer to end of object heap */
    uint32_t *gc_array_heap_alloc;        /* array heap free block */
    uint32_t gc_array_heap_memory_free;   /* number of free words in array heap */
    uint32_t gc_array_heap_memory_required;
    _Bool gc_array_heap_freed; /* has any memory in the array heap been freed since the last sweep? */
    
    int gc_retry_state;
    Thread *gc_thread;
    
    uint16_t gc_free_list;

#if RELATIVE_REFERENCES
    byte specialBytecode[SPECIAL_BYTECODE_SIZE];
#endif
    
    byte reserved1;
    byte reserved2;

} nvm_state_t;

void init_state(nvm_state_t* pState);


#endif
